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


相关文章
|
7月前
|
传感器 监控 BI
基于STM32的智能垃圾分类系统设计与实现
基于STM32的智能垃圾分类系统设计与实现
555 0
|
7月前
|
传感器 监控
基于STM32的智能公交站牌系统设计与实现
基于STM32的智能公交站牌系统设计与实现
142 0
|
7月前
|
传感器 监控
基于STM32的智能农业环境监测系统设计与实现
基于STM32的智能农业环境监测系统设计与实现
520 0
|
7月前
|
传感器 监控 数据挖掘
基于STM32的智能停车场导航系统设计与实现
基于STM32的智能停车场导航系统设计与实现
130 0
|
7月前
|
传感器 物联网 芯片
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
毕业设计 基于STM32单片机无线ZIGBEE智能大棚土壤湿度光照检测
142 0
|
7月前
|
传感器
基于stm32的智能婴儿床(毕业设计)
基于stm32的智能婴儿床(毕业设计)
|
7月前
|
传感器 监控 API
基于STM32的智能灌溉系统设计与实现
基于STM32的智能灌溉系统设计与实现
609 1
|
6月前
|
传感器 数据采集 监控
基于阿里云MQTT服务,设计一个STM32的智能光伏控制系统
这篇文章详细介绍了利用STM32F103C8T6单片机实现光伏发电系统的关键技术。全文分为四章:第一章阐述了光伏发电的背景、意义及应用场景,强调其在绿色能源领域的重要性。第二章介绍了如何通过STM32F103C8T6及光敏电阻和伺服电机实现光线追踪系统,详细描述了硬件选择、连接及使用HAL库编写的单片机程序。第三章讲解了最大功率点追踪(MPPT)的原理,并展示了如何利用STM32F103C8T6和相关传感器、DC-DC转换器实现MPPT功能。第四章描述了如何通过STM32F103C8T6与SIM7600CE 4G模块连接到阿里云MQTT服务,实现设备状态数据的远程传输和控制。本文提供了全面的硬
17740 5
|
7月前
|
传感器 监控 安全
基于STM32的智能公交车辆管理系统设计与实现
基于STM32的智能公交车辆管理系统设计与实现
104 1
|
7月前
|
人工智能 安全 搜索推荐
单片机毕业设计|基于stm32智能快递箱设计
随在当今的社会,网上购物以及线下获取快递己经成为日常生活中很重要的一个组成部分,电子商务的发展也带来了快递业的繁荣。这对快递业而言,是一个巨大的发展机遇,同时也是一个不可忽视的挑战。当前,快件运输的安全性越来越受到大家的重视,对快件的服务要求也越来越高。但就目前的快递行业来说,也面临着这样那样的问题,比较经常遇到送快递的到了,业务不在家,取快递时间对不上等。在此基础上,提出了一种以STM32为核心的智能化快递柜。本快递柜的主要功能有,指纹解锁功能,按键功能,警报功能,继电器柜门开锁功能,验证码功能,主要设计加入了指纹解锁功能。本系统以STM32F103为主控芯片,配置了指纹传感、4*4矩阵键盘
246 0