STM32智能雨刷器

简介: 基于STM32智能雨刷器 功能演示

基于STM32智能雨刷器

功能演示

微信图片_20230221192146.png

https://player.youku.com/embed/XNTg0OTQ1NzE4OA==

基于STM32的智能雨刷器


所用物料

  • STM32
  • 雨滴传感器
  • SG90舵机

所用知识

  • 中段
  • PWM
  • ADC

基本步骤

雨滴传感器使用ADC传输给stm32,stm32实时检测雨滴信号,然后根据判断控制PWM来实现舵机的不同动作,按键触发中断控制PWM

代码

主函数

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "exti.h"
#include "beep.h"
#include "timer.h"
#include "adc.h"
int main(void)
{   
    u16 adcx;
  int i = 0;//用于舵机转动的for循环
  int frequency = 175;//舵机转动的初始角度 175-195
  float temp;     //通过ADC转换获取雨滴传感器的值
  int  integer;   
  delay_init();       //延时函数初始化   
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  uart_init(115200);      //串口初始化为115200
  LED_Init();         //初始化与LED连接的硬件接口 
  BEEP_Init();      //初始化蜂鸣器IO
  EXTIX_Init();           //初始化外部中断输入 
  LED0=0;         //先点亮红灯
  TIM3_PWM_Init(199,7199);   //不分频。PWM频率=72000000/900=80Khz
    Adc_Init();         //ADC初始化
  while(1)
  {
      adcx=Get_Adc_Average(ADC_Channel_1,2);
    temp=(float)adcx*(3.3/4096);
    temp*=100;
    integer = temp;
        if(integer > NUMERICAL_THREEHUNDRED && integer < NUMERICAL_FOURHUNDRED)//不下雨
    { 
      delay_ms(1);
    } 
    if(integer > NUMERICAL_TWOHUNDREDANDTWENT && integer <= NUMERICAL_THREEHUNDRED)//小雨
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMIN);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
        delay_ms(DELAYMIN);
      }
    }
    if(integer > NUMERICAL_ONEHUNDREDANDFORTY && integer <= NUMERICAL_TWOHUNDREDANDTWENT)//中雨
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMID);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
          delay_ms(DELAYMID);
      }
    }
    if(integer > NUMERICAL_TEN && integer <= NUMERICAL_ONEHUNDREDANDFORTY)//大雨
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMAX);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
        delay_ms(DELAYMAX);
      }
    }
        //printf("%d\r\n",integer);//串口发送LOG值
      //delay_ms(500);    
  } 
}

雨滴传感器数据传输

ADC数模转换,将模拟信号转换成数字信号,以下是配置代码

代码下载链接(开源)https://download.csdn.net/download/m0_48216397/84200504

下面是各个代码简述

adc.h

#ifndef __ADC_H
#define __ADC_H 
#include "sys.h"
void Adc_Init(void);
u16  Get_Adc(u8 ch); 
u16 Get_Adc_Average(u8 ch,u8 times); 
#endif 

adc.c

 #include "adc.h"
 #include "delay.h"
//初始化ADC
//这里仅以规则通道为例
//我们默认将开启通道0~3                                     
void  Adc_Init(void)
{   
  ADC_InitTypeDef ADC_InitStructure; 
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1  , ENABLE );   //使能ADC1通道时钟
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
  //PA1 作为模拟通道输入引脚                         
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;   //模拟输入引脚
  GPIO_Init(GPIOA, &GPIO_InitStructure);  
  ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  //ADC工作模式:ADC1和ADC2工作在独立模式
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //ADC数据右对齐
  ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
  ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   
  ADC_Cmd(ADC1, ENABLE);  //使能指定的ADC1
  ADC_ResetCalibration(ADC1); //使能复位校准  
  while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
  ADC_StartCalibration(ADC1);  //开启AD校准
  while(ADC_GetCalibrationStatus(ADC1));   //等待校准结束
//  ADC_SoftwareStartConvCmd(ADC1, ENABLE);   //使能指定的ADC1的软件转换启动功能
}         
//获得ADC值
//ch:通道值 0~3
u16 Get_Adc(u8 ch)   
{
    //设置指定ADC的规则组通道,一个序列,采样时间
  ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );  //ADC1,ADC通道,采样时间为239.5周期             
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);   //使能指定的ADC1的软件转换启动功能  
  while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
  return ADC_GetConversionValue(ADC1);  //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
  u32 temp_val=0;
  u8 t;
  for(t=0;t<times;t++)
  {
    temp_val+=Get_Adc(ch);
    delay_ms(5);
  }
  return temp_val/times;
}    

PWM时钟配置

time.h

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
#endif

time.c

#include "timer.h"
#include "led.h"
#include "usart.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
  TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值  计数到5000为500ms
  TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
  TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
  TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
  if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
    {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
        LED1=!LED1;
    }
}
//TIM3 PWM部分初始化 
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u16 arr,u16 psc)
{  
  GPIO_InitTypeDef GPIO_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);  //使能定时器3时钟
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
  GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    
   //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形  GPIOB.5
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIO
   //初始化TIM3
  TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 
  TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  //初始化TIM3 Channel2 PWM模式   
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
  TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
  TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}

中断配置

exti.h

#ifndef __EXTI_H
#define __EXIT_H   
#include "sys.h"
void EXTIX_Init(void);//外部中断初始化    
#endif

exti.c

#include "exti.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "beep.h"
void EXTIX_Init(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
    KEY_Init();  // 按键端口初始化
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
   //GPIOE.3    中断线以及中断初始化配置 下降沿触发 //KEY1
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
    EXTI_InitStructure.EXTI_Line=EXTI_Line3;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_Init(&EXTI_InitStructure);     //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
   //GPIOE.4    中断线以及中断初始化配置  下降沿触发 //KEY0
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
    EXTI_InitStructure.EXTI_Line=EXTI_Line4;
    EXTI_Init(&EXTI_InitStructure);     //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
   //GPIOA.0    中断线以及中断初始化配置 上升沿触发 PA0  WK_UP
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0); 
    EXTI_InitStructure.EXTI_Line=EXTI_Line0;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
    EXTI_Init(&EXTI_InitStructure);   //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;      //使能按键WK_UP所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;  //抢占优先级2, 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;         //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure); 
    NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;      //使能按键KEY1所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;  //抢占优先级2 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;         //子优先级1 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;      //使能按键KEY0所在的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;  //抢占优先级2 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;         //子优先级0 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;               //使能外部中断通道
    NVIC_Init(&NVIC_InitStructure);     //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
//外部中断0服务程序 
void EXTI0_IRQHandler(void)//亮度最亮
{
  delay_ms(10);//消抖
  if(WK_UP==1)     //WK_UP按键
  {        
        for(i = 0;i<10;i++)
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMIN);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
        delay_ms(DELAYMIN);
      }
    }  
    delay_ms(1000); 
  }
  EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)//亮度
{
  delay_ms(10);//消抖
  if(KEY1==0)  //按键KEY1
  {        
    for(i = 0;i<10;i++)
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMIN);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
        delay_ms(DELAYMIN);
      }
    }  
        delay_ms(1000);       
  }    
  EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
}
void EXTI4_IRQHandler(void)//亮度在中间
{
  delay_ms(10);//消抖
  if(KEY0==0)  //按键KEY0
  {
      for(i = 0;i<10;i++)
    {
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency++;
        delay_ms(DELAYMIN);
      }
      for(i = 0;i<20;i++)
      {
          TIM_SetCompare2(TIM3,frequency);
        frequency--;
        delay_ms(DELAYMIN);
      }
    }  
    delay_ms(1000); 
  }    
  EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
}

按键中断配置

key.h

#ifndef __KEY_H
#define __KEY_H  
#include "sys.h"   
#define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
#define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
#define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP) 
#define KEY0_PRES   1 //KEY0按下
#define KEY1_PRES   2 //KEY1按下
#define WKUP_PRES   3 //KEY_UP按下(即WK_UP/KEY_UP)
void KEY_Init(void);//IO初始化
u8 KEY_Scan(u8);    //按键扫描函数              
#endif

key.c

#include "stm32f10x.h"
#include "key.h"
#include "sys.h" 
#include "delay.h"
//按键初始化函数
void KEY_Init(void) //IO初始化
{ 
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3
  //初始化 WK_UP-->GPIOA.0   下拉输入
  GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉   
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
}

时钟配置

sys.h

#ifndef __SYS_H
#define __SYS_H 
#include "stm32f10x.h"
//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS   0   //定义系统文件夹是否支持UCOS
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    
#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 
#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 
#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 
#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 
#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入
#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入
#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入
//以下为汇编函数
void WFI_SET(void);   //执行WFI指令
void INTX_DISABLE(void);//关闭所有中断
void INTX_ENABLE(void); //开启所有中断
void MSR_MSP(u32 addr); //设置堆栈地址
#endif

sys.c

#include "sys.h"
//THUMB指令不支持汇编内联
//采用如下方法实现执行汇编指令WFI  
void WFI_SET(void)
{
  __ASM volatile("wfi");      
}
//关闭所有中断
void INTX_DISABLE(void)
{     
  __ASM volatile("cpsid i");
}
//开启所有中断
void INTX_ENABLE(void)
{
  __ASM volatile("cpsie i");      
}
//设置栈顶地址
//addr:栈顶地址
__asm void MSR_MSP(u32 addr) 
{
    MSR MSP, r0       //set Main Stack value
    BX r14
}

延时函数配置

delay.h

#ifndef __DELAY_H
#define __DELAY_H          
#include "sys.h"  
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif

delay.c

#include "delay.h"
//   
//如果需要使用OS,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"         //ucos 使用   
#endif 
static u8  fac_us=0;              //us延时倍乘数        
static u16 fac_ms=0;              //ms延时倍乘数,在ucos下,代表每个节拍的ms数
#if SYSTEM_SUPPORT_OS             //如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
//当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
//首先是3个宏定义:
//    delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
//delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
// delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
//然后是3个函数:
//  delay_osschedlock:用于锁定OS任务调度,禁止调度
//delay_osschedunlock:用于解锁OS任务调度,重新开启调度
//    delay_ostimedly:用于OS延时,可以引起任务调度.
//本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
//支持UCOSII
#ifdef  OS_CRITICAL_METHOD            //OS_CRITICAL_METHOD定义了,说明要支持UCOSII       
#define delay_osrunning   OSRunning     //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OS_TICKS_PER_SEC  //OS时钟节拍,即每秒调度次数
#define delay_osintnesting  OSIntNesting    //中断嵌套级别,即中断嵌套次数
#endif
//支持UCOSIII
#ifdef  CPU_CFG_CRITICAL_METHOD         //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII 
#define delay_osrunning   OSRunning     //OS是否运行标记,0,不运行;1,在运行
#define delay_ostickspersec OSCfg_TickRate_Hz //OS时钟节拍,即每秒调度次数
#define delay_osintnesting  OSIntNestingCtr   //中断嵌套级别,即中断嵌套次数
#endif
//us级延时时,关闭任务调度(防止打断us级延迟)
void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD          //使用UCOSIII
  OS_ERR err; 
  OSSchedLock(&err);              //UCOSIII的方式,禁止调度,防止打断us延时
#else                     //否则UCOSII
  OSSchedLock();                //UCOSII的方式,禁止调度,防止打断us延时
#endif
}
//us级延时时,恢复任务调度
void delay_osschedunlock(void)
{ 
#ifdef CPU_CFG_CRITICAL_METHOD          //使用UCOSIII
  OS_ERR err; 
  OSSchedUnlock(&err);            //UCOSIII的方式,恢复调度
#else                     //否则UCOSII
  OSSchedUnlock();              //UCOSII的方式,恢复调度
#endif
}
//调用OS自带的延时函数延时
//ticks:延时的节拍数
void delay_ostimedly(u32 ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
  OS_ERR err; 
  OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err); //UCOSIII延时采用周期模式
#else
  OSTimeDly(ticks);             //UCOSII延时
#endif 
}
//systick中断服务函数,使用ucos时用到
void SysTick_Handler(void)
{ 
  if(delay_osrunning==1)            //OS开始跑了,才执行正常的调度处理
  {
    OSIntEnter();             //进入中断
    OSTimeTick();                 //调用ucos的时钟服务程序               
    OSIntExit();                  //触发任务切换软中断
  }
}
#endif
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
#if SYSTEM_SUPPORT_OS               //如果需要支持OS.
  u32 reload;
#endif
  SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟  HCLK/8
  fac_us=SystemCoreClock/8000000;       //为系统时钟的1/8  
#if SYSTEM_SUPPORT_OS               //如果需要支持OS.
  reload=SystemCoreClock/8000000;       //每秒钟的计数次数 单位为K    
  reload*=1000000/delay_ostickspersec;    //根据delay_ostickspersec设定溢出时间
                        //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右  
  fac_ms=1000/delay_ostickspersec;      //代表OS可以延时的最少单位    
  SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;    //开启SYSTICK中断
  SysTick->LOAD=reload;             //每1/delay_ostickspersec秒中断一次 
  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;     //开启SYSTICK    
#else
  fac_ms=(u16)fac_us*1000;          //非OS下,代表每个ms需要的systick时钟数   
#endif
}                   
#if SYSTEM_SUPPORT_OS               //如果需要支持OS.
//延时nus
//nus为要延时的us数.                           
void delay_us(u32 nus)
{   
  u32 ticks;
  u32 told,tnow,tcnt=0;
  u32 reload=SysTick->LOAD;         //LOAD的值         
  ticks=nus*fac_us;               //需要的节拍数         
  tcnt=0;
  delay_osschedlock();            //阻止OS调度,防止打断us延时
  told=SysTick->VAL;                  //刚进入时的计数器值
  while(1)
  {
    tnow=SysTick->VAL;  
    if(tnow!=told)
    {     
      if(tnow<told)tcnt+=told-tnow;   //这里注意一下SYSTICK是一个递减的计数器就可以了.
      else tcnt+=reload-tnow+told;      
      told=tnow;
      if(tcnt>=ticks)break;       //时间超过/等于要延迟的时间,则退出.
    }  
  };
  delay_osschedunlock();            //恢复OS调度                      
}
//延时nms
//nms:要延时的ms数
void delay_ms(u16 nms)
{ 
  if(delay_osrunning&&delay_osintnesting==0)  //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)     
  {    
    if(nms>=fac_ms)             //延时的时间大于OS的最少时间周期 
    { 
        delay_ostimedly(nms/fac_ms);    //OS延时
    }
    nms%=fac_ms;              //OS已经无法提供这么小的延时了,采用普通方式延时    
  }
  delay_us((u32)(nms*1000));          //普通方式延时  
}
#else //不用OS时
//延时nus
//nus为要延时的us数.                           
void delay_us(u32 nus)
{   
  u32 temp;        
  SysTick->LOAD=nus*fac_us;           //时间加载         
  SysTick->VAL=0x00;                  //清空计数器
  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  //开始倒数    
  do
  {
    temp=SysTick->CTRL;
  }while((temp&0x01)&&!(temp&(1<<16)));   //等待时间到达   
  SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  //关闭计数器
  SysTick->VAL =0X00;                //清空计数器   
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{           
  u32 temp;      
  SysTick->LOAD=(u32)nms*fac_ms;        //时间加载(SysTick->LOAD为24bit)
  SysTick->VAL =0x00;             //清空计数器
  SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;  //开始倒数  
  do
  {
    temp=SysTick->CTRL;
  }while((temp&0x01)&&!(temp&(1<<16)));   //等待时间到达   
  SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;  //关闭计数器
  SysTick->VAL =0X00;                 //清空计数器         
} 
#endif 

串口打log配置,可用可不用

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"  
#include "sys.h" 
#define USART_REC_LEN       200   //定义最大接收字节数 200
#define EN_USART1_RX      1   //使能(1)/禁止(0)串口1接收
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;            //接收状态标记  
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif

usart.c

#include "sys.h"
#include "usart.h"    
//   
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h"         //ucos 使用   
#endif
//加入以下代码,支持printf函数,而不需要选择use MicroLIB    
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
  int handle; 
}; 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
  x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
  while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
  return ch;
}
#endif 
/*使用microLib的方法*/
 /* 
int fputc(int ch, FILE *f)
{
  USART_SendData(USART1, (uint8_t) ch);
  while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}  
    return ch;
}
int GetKey (void)  { 
    while (!(USART1->SR & USART_FLAG_RXNE));
    return ((int)(USART1->DR & 0x1FF));
}
*/
#if EN_USART1_RX   //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误     
u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15,  接收完成标志
//bit14,  接收到0x0d
//bit13~0,  接收到的有效字节数目
u16 USART_RX_STA=0;       //接收状态标记    
void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
  //USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
  //USART1_RX   GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  
  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;    //子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     //IRQ通道使能
  NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
   //USART 初始化设置
  USART_InitStructure.USART_BaudRate = bound;//串口波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  USART_Init(USART1, &USART_InitStructure); //初始化串口1
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 
}
void USART1_IRQHandler(void)                  //串口1中断服务程序
  {
  u8 Res;
#if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  OSIntEnter();    
#endif
  if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    {
    Res =USART_ReceiveData(USART1); //读取接收到的数据
    if((USART_RX_STA&0x8000)==0)//接收未完成
      {
      if(USART_RX_STA&0x4000)//接收到了0x0d
        {
        if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
        else USART_RX_STA|=0x8000;  //接收完成了 
        }
      else //还没收到0X0D
        { 
        if(Res==0x0d)USART_RX_STA|=0x4000;
        else
          {
          USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
          USART_RX_STA++;
          if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收    
          }    
        }
      }        
     } 
#if SYSTEM_SUPPORT_OS   //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  OSIntExit();                         
#endif
} 
#endif


相关文章
|
6月前
|
传感器 监控 BI
基于STM32的智能垃圾分类系统设计与实现
基于STM32的智能垃圾分类系统设计与实现
482 0
|
6月前
|
传感器 监控
基于STM32的智能公交站牌系统设计与实现
基于STM32的智能公交站牌系统设计与实现
121 0
|
6月前
|
传感器 监控
基于STM32的智能农业环境监测系统设计与实现
基于STM32的智能农业环境监测系统设计与实现
452 0
|
6月前
|
传感器 监控 数据挖掘
基于STM32的智能停车场导航系统设计与实现
基于STM32的智能停车场导航系统设计与实现
115 0
|
6月前
|
传感器 物联网 芯片
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
124 0
|
6月前
|
传感器
基于stm32的智能婴儿床(毕业设计)
基于stm32的智能婴儿床(毕业设计)
|
6月前
|
传感器 监控 API
基于STM32的智能灌溉系统设计与实现
基于STM32的智能灌溉系统设计与实现
486 1
|
5月前
|
传感器 数据采集 监控
基于阿里云MQTT服务,设计一个STM32的智能光伏控制系统
这篇文章详细介绍了利用STM32F103C8T6单片机实现光伏发电系统的关键技术。全文分为四章:第一章阐述了光伏发电的背景、意义及应用场景,强调其在绿色能源领域的重要性。第二章介绍了如何通过STM32F103C8T6及光敏电阻和伺服电机实现光线追踪系统,详细描述了硬件选择、连接及使用HAL库编写的单片机程序。第三章讲解了最大功率点追踪(MPPT)的原理,并展示了如何利用STM32F103C8T6和相关传感器、DC-DC转换器实现MPPT功能。第四章描述了如何通过STM32F103C8T6与SIM7600CE 4G模块连接到阿里云MQTT服务,实现设备状态数据的远程传输和控制。本文提供了全面的硬
17698 5
|
6月前
|
传感器 监控 安全
基于STM32的智能公交车辆管理系统设计与实现
基于STM32的智能公交车辆管理系统设计与实现
86 1
|
6月前
|
物联网 程序员 语音技术
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结
STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结-3