你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32智能雨刷器

[复制链接]
STMCU小助手 发布时间:2022-5-25 19:00
基于STM32智能雨刷器

所用物料
STM32
雨滴传感器
SG90舵机
所用知识

中段
PWM
ADC

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

代码
主函数
main.c

  1. #include "led.h"
  2. #include "delay.h"
  3. #include "key.h"
  4. #include "sys.h"
  5. #include "usart.h"
  6. #include "exti.h"
  7. #include "beep.h"
  8. #include "timer.h"
  9. #include "adc.h"

  10. int main(void)
  11. {               
  12.     u16 adcx;
  13.         int i = 0;//用于舵机转动的for循环
  14.         int frequency = 175;//舵机转动的初始角度 175-195
  15.         float temp;     //通过ADC转换获取雨滴传感器的值
  16.         int  integer;   
  17.         delay_init();                    //延时函数初始化         
  18.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  19.         uart_init(115200);            //串口初始化为115200
  20.         LED_Init();                                  //初始化与LED连接的硬件接口
  21.         BEEP_Init();                         //初始化蜂鸣器IO
  22.         EXTIX_Init();                 //初始化外部中断输入
  23.         LED0=0;                                        //先点亮红灯
  24.         TIM3_PWM_Init(199,7199);         //不分频。PWM频率=72000000/900=80Khz
  25.     Adc_Init();                                  //ADC初始化
  26.         while(1)
  27.         {
  28.             adcx=Get_Adc_Average(ADC_Channel_1,2);
  29.                 temp=(float)adcx*(3.3/4096);
  30.                 temp*=100;
  31.                 integer = temp;
  32.         if(integer > NUMERICAL_THREEHUNDRED && integer < NUMERICAL_FOURHUNDRED)//不下雨
  33.                 {        
  34.                         delay_ms(1);
  35.                 }        
  36.                 if(integer > NUMERICAL_TWOHUNDREDANDTWENT && integer <= NUMERICAL_THREEHUNDRED)//小雨
  37.                 {
  38.                         for(i = 0;i<20;i++)
  39.                         {
  40.                             TIM_SetCompare2(TIM3,frequency);
  41.                                 frequency++;
  42.                                 delay_ms(DELAYMIN);
  43.                         }
  44.                         for(i = 0;i<20;i++)
  45.                         {
  46.                             TIM_SetCompare2(TIM3,frequency);
  47.                                 frequency--;
  48.                                 delay_ms(DELAYMIN);
  49.                         }
  50.                 }
  51.                 if(integer > NUMERICAL_ONEHUNDREDANDFORTY && integer <= NUMERICAL_TWOHUNDREDANDTWENT)//中雨
  52.                 {
  53.                         for(i = 0;i<20;i++)
  54.                         {
  55.                             TIM_SetCompare2(TIM3,frequency);
  56.                                 frequency++;
  57.                                 delay_ms(DELAYMID);
  58.                         }
  59.                         for(i = 0;i<20;i++)
  60.                         {
  61.                             TIM_SetCompare2(TIM3,frequency);
  62.                                 frequency--;
  63.                             delay_ms(DELAYMID);
  64.                         }
  65.                 }
  66.                 if(integer > NUMERICAL_TEN && integer <= NUMERICAL_ONEHUNDREDANDFORTY)//大雨
  67.                 {
  68.                         for(i = 0;i<20;i++)
  69.                         {
  70.                             TIM_SetCompare2(TIM3,frequency);
  71.                                 frequency++;
  72.                                 delay_ms(DELAYMAX);
  73.                         }
  74.                         for(i = 0;i<20;i++)
  75.                         {
  76.                             TIM_SetCompare2(TIM3,frequency);
  77.                                 frequency--;
  78.                                 delay_ms(DELAYMAX);
  79.                         }
  80.                 }
  81.         //printf("%d\r\n",integer);//串口发送LOG值
  82.             //delay_ms(500);         
  83.         }        
  84. }
复制代码

雨滴传感器数据传输
ADC数模转换,将模拟信号转换成数字信号,以下是配置代码

下面是各个代码简述
adc.h
  1. #ifndef __ADC_H
  2. #define __ADC_H        
  3. #include "sys.h"

  4. void Adc_Init(void);
  5. u16  Get_Adc(u8 ch);
  6. u16 Get_Adc_Average(u8 ch,u8 times);

  7. #endif
复制代码

adc.c

  1. #include "adc.h"
  2. #include "delay.h"
  3.                   
  4. //初始化ADC
  5. //这里仅以规则通道为例
  6. //我们默认将开启通道0~3                                                                                                                                          
  7. void  Adc_Init(void)
  8. {         
  9.         ADC_InitTypeDef ADC_InitStructure;
  10.         GPIO_InitTypeDef GPIO_InitStructure;

  11.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1        , ENABLE );          //使能ADC1通道时钟


  12.         RCC_ADCCLKConfig(RCC_PCLK2_Div6);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M

  13.         //PA1 作为模拟通道输入引脚                        
  14.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  15.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;                //模拟输入引脚
  16.         GPIO_Init(GPIOA, &GPIO_InitStructure);        

  17.         ADC_DeInit(ADC1);  //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值

  18.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //ADC工作模式:ADC1和ADC2工作在独立模式
  19.         ADC_InitStructure.ADC_ScanConvMode = DISABLE;        //模数转换工作在单通道模式
  20.         ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;        //模数转换工作在单次转换模式
  21.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;        //转换由软件而不是外部触发启动
  22.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;        //ADC数据右对齐
  23.         ADC_InitStructure.ADC_NbrOfChannel = 1;        //顺序进行规则转换的ADC通道的数目
  24.         ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器   


  25.         ADC_Cmd(ADC1, ENABLE);        //使能指定的ADC1
  26.         
  27.         ADC_ResetCalibration(ADC1);        //使能复位校准  
  28.          
  29.         while(ADC_GetResetCalibrationStatus(ADC1));        //等待复位校准结束
  30.         
  31.         ADC_StartCalibration(ADC1);         //开启AD校准

  32.         while(ADC_GetCalibrationStatus(ADC1));         //等待校准结束

  33. //        ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能

  34. }                                 
  35. //获得ADC值
  36. //ch:通道值 0~3
  37. u16 Get_Adc(u8 ch)   
  38. {
  39.           //设置指定ADC的规则组通道,一个序列,采样时间
  40.         ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );        //ADC1,ADC通道,采样时间为239.5周期                                      

  41.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //使能指定的ADC1的软件转换启动功能        
  42.          
  43.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

  44.         return ADC_GetConversionValue(ADC1);        //返回最近一次ADC1规则组的转换结果
  45. }

  46. u16 Get_Adc_Average(u8 ch,u8 times)
  47. {
  48.         u32 temp_val=0;
  49.         u8 t;
  50.         for(t=0;t<times;t++)
  51.         {
  52.                 temp_val+=Get_Adc(ch);
  53.                 delay_ms(5);
  54.         }
  55.         return temp_val/times;
  56. }         
复制代码

PWM时钟配置
time.h

  1. #ifndef __TIMER_H
  2. #define __TIMER_H
  3. #include "sys.h"

  4. void TIM3_Int_Init(u16 arr,u16 psc);
  5. void TIM3_PWM_Init(u16 arr,u16 psc);
  6. #endif
复制代码

time.c

  1. #include "timer.h"
  2. #include "led.h"
  3. #include "usart.h"
  4.             
  5. //通用定时器3中断初始化
  6. //这里时钟选择为APB1的2倍,而APB1为36M
  7. //arr:自动重装值。
  8. //psc:时钟预分频数
  9. //这里使用的是定时器3!
  10. void TIM3_Int_Init(u16 arr,u16 psc)
  11. {
  12.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  13.         NVIC_InitTypeDef NVIC_InitStructure;

  14.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

  15.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到5000为500ms
  16.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
  17.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  18.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  19.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

  20.         TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

  21.         NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
  22.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
  23.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
  24.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
  25.         NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

  26.         TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设
  27.                                                          
  28. }
  29. //定时器3中断服务程序
  30. void TIM3_IRQHandler(void)   //TIM3中断
  31. {
  32.         if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
  33.                 {
  34.                     TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源
  35.                     LED1=!LED1;
  36.                 }
  37. }
  38. //TIM3 PWM部分初始化
  39. //PWM输出初始化
  40. //arr:自动重装值
  41. //psc:时钟预分频数
  42. void TIM3_PWM_Init(u16 arr,u16 psc)
  43. {  
  44.         GPIO_InitTypeDef GPIO_InitStructure;
  45.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  46.         TIM_OCInitTypeDef  TIM_OCInitStructure;
  47.         

  48.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);        //使能定时器3时钟
  49.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟
  50.         
  51.         GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5   

  52.    //设置该引脚为复用输出功能,输出TIM3 CH2的PWM脉冲波形        GPIOB.5
  53.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
  54.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  55.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  56.         GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO

  57.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
  58.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
  59.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  60.         GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIO
  61.         
  62.    //初始化TIM3
  63.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
  64.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
  65.         TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
  66.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
  67.         TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
  68.         
  69.         //初始化TIM3 Channel2 PWM模式         
  70.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
  71.          TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
  72.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
  73.         TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2

  74.         TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器

  75.         TIM_Cmd(TIM3, ENABLE);  //使能TIM3
  76. }
复制代码

中断配置
exti.h

  1. #ifndef __EXTI_H
  2. #define __EXIT_H         
  3. #include "sys.h"
  4.          
  5. void EXTIX_Init(void);//外部中断初始化                 
  6. #endif
复制代码

exti.c

  1. #include "exti.h"
  2. #include "led.h"
  3. #include "key.h"
  4. #include "delay.h"
  5. #include "usart.h"
  6. #include "beep.h"

  7. void EXTIX_Init(void)
  8. {

  9.            EXTI_InitTypeDef EXTI_InitStructure;
  10.          NVIC_InitTypeDef NVIC_InitStructure;

  11.     KEY_Init();         //        按键端口初始化
  12.           RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);        //使能复用功能时钟

  13.    //GPIOE.3          中断线以及中断初始化配置 下降沿触发 //KEY1
  14.           GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
  15.           EXTI_InitStructure.EXTI_Line=EXTI_Line3;
  16.           EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;        
  17.           EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  18.           EXTI_Init(&EXTI_InitStructure);                  //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器

  19.    //GPIOE.4          中断线以及中断初始化配置  下降沿触发        //KEY0
  20.           GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);
  21.           EXTI_InitStructure.EXTI_Line=EXTI_Line4;
  22.           EXTI_Init(&EXTI_InitStructure);                  //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器


  23.    //GPIOA.0          中断线以及中断初始化配置 上升沿触发 PA0  WK_UP
  24.          GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

  25.           EXTI_InitStructure.EXTI_Line=EXTI_Line0;
  26.           EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
  27.           EXTI_Init(&EXTI_InitStructure);                //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器


  28.           NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;                        //使能按键WK_UP所在的外部中断通道
  29.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;        //抢占优先级2,
  30.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;                                        //子优先级3
  31.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                //使能外部中断通道
  32.           NVIC_Init(&NVIC_InitStructure);

  33.           NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;                        //使能按键KEY1所在的外部中断通道
  34.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;        //抢占优先级2
  35.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;                                        //子优先级1
  36.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                //使能外部中断通道
  37.           NVIC_Init(&NVIC_InitStructure);            //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

  38.           NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;                        //使能按键KEY0所在的外部中断通道
  39.           NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;        //抢占优先级2
  40.           NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;                                        //子优先级0
  41.           NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                                                //使能外部中断通道
  42.           NVIC_Init(&NVIC_InitStructure);            //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

  43. }
  44. //外部中断0服务程序
  45. void EXTI0_IRQHandler(void)//亮度最亮
  46. {
  47.         delay_ms(10);//消抖
  48.         if(WK_UP==1)                  //WK_UP按键
  49.         {                                 
  50.         for(i = 0;i<10;i++)
  51.                 {
  52.                         for(i = 0;i<20;i++)
  53.                         {
  54.                             TIM_SetCompare2(TIM3,frequency);
  55.                                 frequency++;
  56.                                 delay_ms(DELAYMIN);
  57.                         }
  58.                         for(i = 0;i<20;i++)
  59.                         {
  60.                             TIM_SetCompare2(TIM3,frequency);
  61.                                 frequency--;
  62.                                 delay_ms(DELAYMIN);
  63.                         }
  64.                 }  
  65.                 delay_ms(1000);        
  66.         }
  67.         EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位  
  68. }
  69. //外部中断3服务程序
  70. void EXTI3_IRQHandler(void)//亮度
  71. {
  72.         delay_ms(10);//消抖
  73.         if(KEY1==0)         //按键KEY1
  74.         {                                 
  75.                 for(i = 0;i<10;i++)
  76.                 {
  77.                         for(i = 0;i<20;i++)
  78.                         {
  79.                             TIM_SetCompare2(TIM3,frequency);
  80.                                 frequency++;
  81.                                 delay_ms(DELAYMIN);
  82.                         }
  83.                         for(i = 0;i<20;i++)
  84.                         {
  85.                             TIM_SetCompare2(TIM3,frequency);
  86.                                 frequency--;
  87.                                 delay_ms(DELAYMIN);
  88.                         }
  89.                 }  
  90.         delay_ms(1000);                          
  91.         }                 
  92.         EXTI_ClearITPendingBit(EXTI_Line3);  //清除LINE3上的中断标志位  
  93. }
  94. void EXTI4_IRQHandler(void)//亮度在中间
  95. {
  96.         delay_ms(10);//消抖
  97.         if(KEY0==0)         //按键KEY0
  98.         {
  99.             for(i = 0;i<10;i++)
  100.                 {
  101.                         for(i = 0;i<20;i++)
  102.                         {
  103.                             TIM_SetCompare2(TIM3,frequency);
  104.                                 frequency++;
  105.                                 delay_ms(DELAYMIN);
  106.                         }
  107.                         for(i = 0;i<20;i++)
  108.                         {
  109.                             TIM_SetCompare2(TIM3,frequency);
  110.                                 frequency--;
  111.                                 delay_ms(DELAYMIN);
  112.                         }
  113.                 }  
  114.                 delay_ms(1000);        
  115.         }                 
  116.         EXTI_ClearITPendingBit(EXTI_Line4);  //清除LINE4上的中断标志位  
  117. }
复制代码

按键中断配置
key.h

  1. #ifndef __KEY_H
  2. #define __KEY_H         
  3. #include "sys.h"         

  4. #define KEY0  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)//读取按键0
  5. #define KEY1  GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)//读取按键1
  6. #define WK_UP   GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)//读取按键3(WK_UP)

  7. #define KEY0_PRES         1        //KEY0按下
  8. #define KEY1_PRES          2        //KEY1按下
  9. #define WKUP_PRES   3        //KEY_UP按下(即WK_UP/KEY_UP)

  10. void KEY_Init(void);//IO初始化
  11. u8 KEY_Scan(u8);          //按键扫描函数                                            
  12. #endif
复制代码

key.c

  1. #include "stm32f10x.h"
  2. #include "key.h"
  3. #include "sys.h"
  4. #include "delay.h"
  5.                                                                     
  6. //按键初始化函数
  7. void KEY_Init(void) //IO初始化
  8. {
  9.          GPIO_InitTypeDef GPIO_InitStructure;

  10.          RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟

  11.         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1
  12.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
  13.          GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3

  14.         //初始化 WK_UP-->GPIOA.0          下拉输入
  15.         GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;
  16.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉         
  17.         GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0
  18. }
复制代码

时钟配置
sys.h

  1. #ifndef __SYS_H
  2. #define __SYS_H        
  3. #include "stm32f10x.h"

  4. //0,不支持ucos
  5. //1,支持ucos
  6. #define SYSTEM_SUPPORT_OS                0                //定义系统文件夹是否支持UCOS
  7.                                                                                                                                             
  8.          
  9. //位带操作,实现51类似的GPIO控制功能
  10. //具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
  11. //IO口操作宏定义
  12. #define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
  13. #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr))
  14. #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))
  15. //IO口地址映射
  16. #define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C
  17. #define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C
  18. #define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C
  19. #define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C
  20. #define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C
  21. #define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C   
  22. #define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C   

  23. #define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808
  24. #define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08
  25. #define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008
  26. #define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408
  27. #define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808
  28. #define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08
  29. #define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08

  30. //IO口操作,只对单一的IO口!
  31. //确保n的值小于16!
  32. #define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出
  33. #define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入

  34. #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出
  35. #define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入

  36. #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出
  37. #define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入

  38. #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出
  39. #define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入

  40. #define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出
  41. #define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

  42. #define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出
  43. #define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

  44. #define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出
  45. #define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

  46. //以下为汇编函数
  47. void WFI_SET(void);                //执行WFI指令
  48. void INTX_DISABLE(void);//关闭所有中断
  49. void INTX_ENABLE(void);        //开启所有中断
  50. void MSR_MSP(u32 addr);        //设置堆栈地址

  51. #endif
复制代码

sys.c

  1. #include "sys.h"

  2. //THUMB指令不支持汇编内联
  3. //采用如下方法实现执行汇编指令WFI  
  4. void WFI_SET(void)
  5. {
  6.         __ASM volatile("wfi");                  
  7. }
  8. //关闭所有中断
  9. void INTX_DISABLE(void)
  10. {                  
  11.         __ASM volatile("cpsid i");
  12. }
  13. //开启所有中断
  14. void INTX_ENABLE(void)
  15. {
  16.         __ASM volatile("cpsie i");                  
  17. }
  18. //设置栈顶地址
  19. //addr:栈顶地址
  20. __asm void MSR_MSP(u32 addr)
  21. {
  22.     MSR MSP, r0                         //set Main Stack value
  23.     BX r14
  24. }
复制代码

延时函数配置
delay.h

  1. #ifndef __DELAY_H
  2. #define __DELAY_H                           
  3. #include "sys.h"  
  4.          
  5. void delay_init(void);
  6. void delay_ms(u16 nms);
  7. void delay_us(u32 nus);

  8. #endif
复制代码

delay.c

  1. #include "delay.h"
  2. //         
  3. //如果需要使用OS,则包括下面的头文件即可.
  4. #if SYSTEM_SUPPORT_OS
  5. #include "includes.h"                                        //ucos 使用         
  6. #endif

  7. static u8  fac_us=0;                                                        //us延时倍乘数                           
  8. static u16 fac_ms=0;                                                        //ms延时倍乘数,在ucos下,代表每个节拍的ms数
  9.         
  10.         
  11. #if SYSTEM_SUPPORT_OS                                                        //如果SYSTEM_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS).
  12. //当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
  13. //首先是3个宏定义:
  14. //    delay_osrunning:用于表示OS当前是否正在运行,以决定是否可以使用相关函数
  15. //delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始哈systick
  16. // delay_osintnesting:用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
  17. //然后是3个函数:
  18. //  delay_osschedlock:用于锁定OS任务调度,禁止调度
  19. //delay_osschedunlock:用于解锁OS任务调度,重新开启调度
  20. //    delay_ostimedly:用于OS延时,可以引起任务调度.

  21. //本例程仅作UCOSII和UCOSIII的支持,其他OS,请自行参考着移植
  22. //支持UCOSII
  23. #ifdef         OS_CRITICAL_METHOD                                                //OS_CRITICAL_METHOD定义了,说明要支持UCOSII                                
  24. #define delay_osrunning                OSRunning                        //OS是否运行标记,0,不运行;1,在运行
  25. #define delay_ostickspersec        OS_TICKS_PER_SEC        //OS时钟节拍,即每秒调度次数
  26. #define delay_osintnesting         OSIntNesting                //中断嵌套级别,即中断嵌套次数
  27. #endif

  28. //支持UCOSIII
  29. #ifdef         CPU_CFG_CRITICAL_METHOD                                        //CPU_CFG_CRITICAL_METHOD定义了,说明要支持UCOSIII        
  30. #define delay_osrunning                OSRunning                        //OS是否运行标记,0,不运行;1,在运行
  31. #define delay_ostickspersec        OSCfg_TickRate_Hz        //OS时钟节拍,即每秒调度次数
  32. #define delay_osintnesting         OSIntNestingCtr                //中断嵌套级别,即中断嵌套次数
  33. #endif


  34. //us级延时时,关闭任务调度(防止打断us级延迟)
  35. void delay_osschedlock(void)
  36. {
  37. #ifdef CPU_CFG_CRITICAL_METHOD                                   //使用UCOSIII
  38.         OS_ERR err;
  39.         OSSchedLock(&err);                                                        //UCOSIII的方式,禁止调度,防止打断us延时
  40. #else                                                                                        //否则UCOSII
  41.         OSSchedLock();                                                                //UCOSII的方式,禁止调度,防止打断us延时
  42. #endif
  43. }

  44. //us级延时时,恢复任务调度
  45. void delay_osschedunlock(void)
  46. {        
  47. #ifdef CPU_CFG_CRITICAL_METHOD                                   //使用UCOSIII
  48.         OS_ERR err;
  49.         OSSchedUnlock(&err);                                                //UCOSIII的方式,恢复调度
  50. #else                                                                                        //否则UCOSII
  51.         OSSchedUnlock();                                                        //UCOSII的方式,恢复调度
  52. #endif
  53. }

  54. //调用OS自带的延时函数延时
  55. //ticks:延时的节拍数
  56. void delay_ostimedly(u32 ticks)
  57. {
  58. #ifdef CPU_CFG_CRITICAL_METHOD
  59.         OS_ERR err;
  60.         OSTimeDly(ticks,OS_OPT_TIME_PERIODIC,&err);        //UCOSIII延时采用周期模式
  61. #else
  62.         OSTimeDly(ticks);                                                        //UCOSII延时
  63. #endif
  64. }

  65. //systick中断服务函数,使用ucos时用到
  66. void SysTick_Handler(void)
  67. {        
  68.         if(delay_osrunning==1)                                                //OS开始跑了,才执行正常的调度处理
  69.         {
  70.                 OSIntEnter();                                                        //进入中断
  71.                 OSTimeTick();                                               //调用ucos的时钟服务程序               
  72.                 OSIntExit();                                                        //触发任务切换软中断
  73.         }
  74. }
  75. #endif

  76. //初始化延迟函数
  77. //当使用OS的时候,此函数会初始化OS的时钟节拍
  78. //SYSTICK的时钟固定为HCLK时钟的1/8
  79. //SYSCLK:系统时钟
  80. void delay_init()
  81. {
  82. #if SYSTEM_SUPPORT_OS                                                          //如果需要支持OS.
  83.         u32 reload;
  84. #endif
  85.         SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);        //选择外部时钟  HCLK/8
  86.         fac_us=SystemCoreClock/8000000;                                //为系统时钟的1/8  
  87. #if SYSTEM_SUPPORT_OS                                                          //如果需要支持OS.
  88.         reload=SystemCoreClock/8000000;                                //每秒钟的计数次数 单位为K           
  89.         reload*=1000000/delay_ostickspersec;                //根据delay_ostickspersec设定溢出时间
  90.                                                                                                 //reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右        
  91.         fac_ms=1000/delay_ostickspersec;                        //代表OS可以延时的最少单位           

  92.         SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk;           //开启SYSTICK中断
  93.         SysTick->LOAD=reload;                                                 //每1/delay_ostickspersec秒中断一次        
  94.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk;           //开启SYSTICK   

  95. #else
  96.         fac_ms=(u16)fac_us*1000;                                        //非OS下,代表每个ms需要的systick时钟数   
  97. #endif
  98. }                                                                    

  99. #if SYSTEM_SUPPORT_OS                                                          //如果需要支持OS.
  100. //延时nus
  101. //nus为要延时的us数.                                                                                       
  102. void delay_us(u32 nus)
  103. {               
  104.         u32 ticks;
  105.         u32 told,tnow,tcnt=0;
  106.         u32 reload=SysTick->LOAD;                                        //LOAD的值                     
  107.         ticks=nus*fac_us;                                                         //需要的节拍数                           
  108.         tcnt=0;
  109.         delay_osschedlock();                                                //阻止OS调度,防止打断us延时
  110.         told=SysTick->VAL;                                                //刚进入时的计数器值
  111.         while(1)
  112.         {
  113.                 tnow=SysTick->VAL;        
  114.                 if(tnow!=told)
  115.                 {            
  116.                         if(tnow<told)tcnt+=told-tnow;                //这里注意一下SYSTICK是一个递减的计数器就可以了.
  117.                         else tcnt+=reload-tnow+told;            
  118.                         told=tnow;
  119.                         if(tcnt>=ticks)break;                                //时间超过/等于要延迟的时间,则退出.
  120.                 }  
  121.         };
  122.         delay_osschedunlock();                                                //恢复OS调度                                                                           
  123. }
  124. //延时nms
  125. //nms:要延时的ms数
  126. void delay_ms(u16 nms)
  127. {        
  128.         if(delay_osrunning&&delay_osintnesting==0)        //如果OS已经在跑了,并且不是在中断里面(中断里面不能任务调度)            
  129.         {                 
  130.                 if(nms>=fac_ms)                                                        //延时的时间大于OS的最少时间周期
  131.                 {
  132.                            delay_ostimedly(nms/fac_ms);                //OS延时
  133.                 }
  134.                 nms%=fac_ms;                                                        //OS已经无法提供这么小的延时了,采用普通方式延时   
  135.         }
  136.         delay_us((u32)(nms*1000));                                        //普通方式延时  
  137. }
  138. #else //不用OS时
  139. //延时nus
  140. //nus为要延时的us数.                                                                                       
  141. void delay_us(u32 nus)
  142. {               
  143.         u32 temp;                     
  144.         SysTick->LOAD=nus*fac_us;                                         //时间加载                           
  145.         SysTick->VAL=0x00;                                                //清空计数器
  146.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数         
  147.         do
  148.         {
  149.                 temp=SysTick->CTRL;
  150.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  151.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  152.         SysTick->VAL =0X00;                                               //清空计数器         
  153. }
  154. //延时nms
  155. //注意nms的范围
  156. //SysTick->LOAD为24位寄存器,所以,最大延时为:
  157. //nms<=0xffffff*8*1000/SYSCLK
  158. //SYSCLK单位为Hz,nms单位为ms
  159. //对72M条件下,nms<=1864
  160. void delay_ms(u16 nms)
  161. {                                    
  162.         u32 temp;                  
  163.         SysTick->LOAD=(u32)nms*fac_ms;                                //时间加载(SysTick->LOAD为24bit)
  164.         SysTick->VAL =0x00;                                                        //清空计数器
  165.         SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;        //开始倒数  
  166.         do
  167.         {
  168.                 temp=SysTick->CTRL;
  169.         }while((temp&0x01)&&!(temp&(1<<16)));                //等待时间到达   
  170.         SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;        //关闭计数器
  171.         SysTick->VAL =0X00;                                               //清空计数器                     
  172. }
  173. #endif
复制代码

串口打log配置,可用可不用
usart.h

  1. #ifndef __USART_H
  2. #define __USART_H
  3. #include "stdio.h"        
  4. #include "sys.h"

  5. #define USART_REC_LEN                          200          //定义最大接收字节数 200
  6. #define EN_USART1_RX                         1                //使能(1)/禁止(0)串口1接收
  7.                   
  8. extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
  9. extern u16 USART_RX_STA;                         //接收状态标记        
  10. //如果想串口中断接收,请不要注释以下宏定义
  11. void uart_init(u32 bound);
  12. #endif
复制代码

usart.c

  1. #include "sys.h"
  2. #include "usart.h"         
  3. //         
  4. //如果使用ucos,则包括下面的头文件即可.
  5. #if SYSTEM_SUPPORT_OS
  6. #include "includes.h"                                        //ucos 使用         
  7. #endif

  8. //加入以下代码,支持printf函数,而不需要选择use MicroLIB         
  9. #if 1
  10. #pragma import(__use_no_semihosting)            
  11. //标准库需要的支持函数                 
  12. struct __FILE
  13. {
  14.         int handle;

  15. };

  16. FILE __stdout;      
  17. //定义_sys_exit()以避免使用半主机模式   
  18. void _sys_exit(int x)
  19. {
  20.         x = x;
  21. }
  22. //重定义fputc函数
  23. int fputc(int ch, FILE *f)
  24. {      
  25.         while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
  26.     USART1->DR = (u8) ch;      
  27.         return ch;
  28. }
  29. #endif

  30. /*使用microLib的方法*/
  31. /*
  32. int fputc(int ch, FILE *f)
  33. {
  34.         USART_SendData(USART1, (uint8_t) ch);

  35.         while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}        

  36.     return ch;
  37. }
  38. int GetKey (void)  {

  39.     while (!(USART1->SR & USART_FLAG_RXNE));

  40.     return ((int)(USART1->DR & 0x1FF));
  41. }
  42. */

  43. #if EN_USART1_RX   //如果使能了接收
  44. //串口1中断服务程序
  45. //注意,读取USARTx->SR能避免莫名其妙的错误           
  46. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
  47. //接收状态
  48. //bit15,        接收完成标志
  49. //bit14,        接收到0x0d
  50. //bit13~0,        接收到的有效字节数目
  51. u16 USART_RX_STA=0;       //接收状态标记         

  52. void uart_init(u32 bound){
  53.   //GPIO端口设置
  54.   GPIO_InitTypeDef GPIO_InitStructure;
  55.         USART_InitTypeDef USART_InitStructure;
  56.         NVIC_InitTypeDef NVIC_InitStructure;
  57.          
  58.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟

  59.         //USART1_TX   GPIOA.9
  60.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  61.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  62.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
  63.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9

  64.   //USART1_RX          GPIOA.10初始化
  65.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  66.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  67.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  68.   //Usart1 NVIC 配置
  69.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  70.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  71.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
  72.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
  73.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器

  74.    //USART 初始化设置

  75.         USART_InitStructure.USART_BaudRate = bound;//串口波特率
  76.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  77.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
  78.         USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
  79.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
  80.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式

  81.   USART_Init(USART1, &USART_InitStructure); //初始化串口1
  82.   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  83.   USART_Cmd(USART1, ENABLE);                    //使能串口1
  84. }

  85. void USART1_IRQHandler(void)                        //串口1中断服务程序
  86.         {
  87.         u8 Res;
  88. #if SYSTEM_SUPPORT_OS                 //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  89.         OSIntEnter();   
  90. #endif
  91.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
  92.                 {
  93.                 Res =USART_ReceiveData(USART1);        //读取接收到的数据
  94.                
  95.                 if((USART_RX_STA&0x8000)==0)//接收未完成
  96.                         {
  97.                         if(USART_RX_STA&0x4000)//接收到了0x0d
  98.                                 {
  99.                                 if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
  100.                                 else USART_RX_STA|=0x8000;        //接收完成了
  101.                                 }
  102.                         else //还没收到0X0D
  103.                                 {        
  104.                                 if(Res==0x0d)USART_RX_STA|=0x4000;
  105.                                 else
  106.                                         {
  107.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
  108.                                         USART_RX_STA++;
  109.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收         
  110.                                         }                 
  111.                                 }
  112.                         }                    
  113.      }
  114. #if SYSTEM_SUPPORT_OS         //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
  115.         OSIntExit();                                                                                          
  116. #endif
  117. }
  118. #endif
复制代码



收藏 评论0 发布时间:2022-5-25 19:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版