串口中断:可以用标志位解决(首先需要保证串口可以发送,程序里面有打印的语句,可以在串口调试助手界面显示收到的语句),中断函数只需要写个标志位就行(flage)。其他程序就可以在主函数操作了,有的程序需要进行清零操作。非常好用。
变量的类型用到什么可以定义什么,uint8_t,uint16_t,uint32_t,float,double。(看是否定义的类型太小或者表示的范围所导致的问题)
定时器:周期和频率。0.5s采集一次,就是500ms采集一次,周期设为10ms,计数器值加一,加到50,就为500ms,触发中断,采集一次。
uint16_t g_timer0Count = 0; ///<timer0 Count 每10ms计数加1
//定时器初始化
void Timer_Init(void)
{
TIMER_ConfigType timerConfig0;
memset(&timerConfig0, 0, sizeof(timerConfig0));
/* configure TIMER channel 0, only enable timer */
timerConfig0.periodValue = Delay10ms; ///10ms timeout 10s中断
timerConfig0.interruptEn = ENABLE; //中断使能
timerConfig0.linkModeEn = DISABLE; //禁能链接
timerConfig0.timerEn = ENABLE; //使能TIMER0
timerConfig0.callBack = TIM0_Task; //中断回调函数
TIMER_Init(TIMER_CHANNEL0, &timerConfig0); //TIMER0配置初始化
}
//TIMER0中断回调函数
void TIM0_Task(void *device, uint32_t wpara, uint32_t lpara)
{
g_timer0Count++; //每10ms计数加一
}
//TIMER的计数配置及链接
void TIMER_Clock(void)
{
Timer_Init();
}
//Timer计时
int main(void)
{
InitDelay();
TIMER_Clock();
while (1)
{
}
}
定时器就好像是CPU的一个秘书一样,这个秘书专门管帮CPU来计时,并到时间后提醒CPU要做某件事情。所以CPU有了定时器之后,只需要预先把自己XX时间之后必须要做的事情绑定到定时器中断ISR即可,到了时间之后定时器就会以中断的方式提醒CPU来处理这个事情。
3. 定时器的原理
(1)定时器计时其实是通过计数来实现的。定时器内部有一个计数器,这个计数器根据一个时钟(这个时钟来自于ARM的APB总线,然后经过时钟模块内部的分频器来分频得到)来工作。每隔一个时钟周期,计数器就就计数一次,定时器的时间就是计数器计数值x时钟周期。
(2)定时器内部有1个寄存器TCNT,计时开始时我们会把一个总的计数值(譬如说300)放入TCNT寄存器中,然后每隔一个时钟周期(假设为1ms)TCNT中的值会自动减1(硬件自动完成,不需要CPU软件去干预),知道TCNT中减为0的时候,TCNT就会触发定时器中断。最后的计时时间就是300ms。
TIM3_Init_init(5000-1,7200-1);//定时器频率2Hz 72M/7200=10Khz计数频率 就是1s计10K个数 1/2*10K=5000(arr)
TIM4_PWM_Init(1000-1,72-1); //PWM频率1KHz 72M/72=1M计数频率 1s计1M个数 装载值1000 1/1K*1M=1000(arr)
频率公式 f = 1 / T,12 = 1 / T,例如:50=1 / T,算出来T=0.02秒,也就是50hz。的频率下振动一次需要0.02秒,以HZ为单位,T以秒(s)为单位。
分频→它会把12T的这个模式进行分频输出1MHZ。
拓展秒换算如下↓
1MHZ=1000 000HZ
1KHZ=1000HZ
1KHz = 103Hz 1MHz = 103KHz 1GHz = 103MHz 1THz = 103GHz
1秒(s) =1000毫秒(ms)
1毫秒(ms)=1000微秒 (us)
1微秒(us)=1000纳秒 (ns)
1纳秒(ns)=1000皮秒 (ps)
1千赫 (kHz 103 Hz) = 1 000 Hz;
1兆赫 (MHz 106 Hz) = 1 000 000 Hz;
1吉赫 (GHz 109 Hz) = 1 000 000 000 Hz;
1太赫 (THz 1012 Hz) = 1 000 000 000 000 Hz;
PWM(脉宽调制),PWM定时器(本质上还是定时器)
脉宽调制(脉宽调制)占空比计算公式可以通过以下方式获得)
占空比(占空比)是指PWM信号中高电平持续时间与一个周期时间之比。通常以百分比表示
占空比=(高电平持续时间/一个周期时间)x 100%
如果一个脉宽调制信号的周期时间为10 ms,其中高电平持续时间为2ms,则计算其占空比:
占空比=(2ms/10 ms)x 100%=20%。
PWM(脉冲宽度调制)是一种对模拟信号电平进行数字编码的方法,单片机只能输出0或5V的数字信号而不能输出模拟信号,那么如果想使用单片机来获得一个模拟电压信号,则需通过使用单片机配合程序改变IO口输出的方波的占空比从而获得使用数字信号来模拟的模拟电压信号。这里涉及到了PWM脉宽调制的重要指标:占空比。
stm32正常内部时钟频率为72M,若要产生一个频率为1khz,占空比为50%,分辨率为1%的PWM波形,对应的公式就为
频率1000=CK_PSC/(psc+1)/(ARR+1)
占空比50%=CCR/(ARR+1)
分辨率1%=1/(ARR+1)
因stm32内部时钟频率CK_PSC为72M
即ARR=99 CCR=50 PSC=720
PWM(TIM3_CH2,定时器3通道2)。(引脚重映射功能)(引脚复用功能)
重映射时钟(AFIO时钟)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
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
//初始化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
}
1.定时器与延时的区别
大家可能会觉得我们用延时函数照样可以实现上一讲代码的实验现象,但是定时器与延时的概念不同,延时函数需要占用CPU的使用权,正在延时的时候其他任务没有CPU的使用权就会拖慢执行效率。
而定时器是不需要占用CPU的使用权的,它是独立自己运行的,就像我们在第一讲的时候提到调好5分钟的闹钟,在这5分钟里我们可以随意执行任务,也可以什么事都不做,但是5分钟过后闹钟响了就要执行相关的任务了。
所以上一讲的代码的实现原理就是每隔51微秒,有个变量会自加1,过了1000个51微秒的时候LED的状态才会改变,可以说CPU在51ms的时间里基本没什么事做,只是在51微秒到了的时候做了“cnt++;”这样简单的任务,然后又空闲地等下一个51微秒的到来再执行“cnt++;”。