基于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数模转换,将模拟信号转换成数字信号,以下是配置代码
下面是各个代码简述
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
复制代码
|