赞
踩
本实验通过STM32F103C8T6(下文统称为32)的两个内部定时器中断TIM2和TIM3同时产生周期为2秒和5秒的计数,来控制LED以2秒为周期闪烁,USART串口每5秒发送数据给电脑。
STM32F103C8T6共有4个内部定时器,分别是TIM1、TIM2、TIM3和TIM4。其中,TIM1为高级定时器,具备捕获/比较通道和互补输出功能;而TIM2、TIM3和TIM4为通用定时器,具有捕获/比较通道,但没有互补输出功能。本实验我们采用TIM2和TIM3进行简单的定时功能,定时器的实质就是计数器,定时器的实质就是计数器,定时器的实质就是计数器,(主要的事情说三遍!!!),下面解析一下代码部分。
TIM2和TIM3定时器中断初始化配置:
第一步:开启外设时钟,APB1为TIM2和TIM3的总线,APB2为GPIO口的总线(不知道外设在哪条总线上可查询32手册AHB/APB桥(APB)小节)。直接调用库函数RCC_APB1PeriphClockCmd()和RCC_APB2PeriphClockCmd()选择对应外设设置成ENABLE,GPIO口选择GPIOA,这样对应的外设时钟就开启了。
第二步:GPIO口初始化配置,调用GPIO_Init()结构体函数进行配置,查手册可知GPIO口模式为普通推挽输出模式,GPIOA口选择Pin_0口,引脚传输频率通常选择50MHz。
第三步:内部时钟开启、定时器配置、更新标志位,调用TIM_InternalClockConfig()开启定时器内部时钟;调用TIM_TimeBaseInit()结构体配置定时器,结构体成员TIM_ClockDivision配置选择1分频,模式选择向上计数即:脉冲每过来一个上升沿计数器加一。自动重装器和预分频器的值是根据你所需的时间来计算配置的,以2秒为例:预分频器给7200得到的频率为72MHz/7200=10000Hz,当自动重装给20000时10000Hz/20000Hz=0.5Hz,根据公式T=1/f得T=1/0.5=2秒,即0.5Hz=2秒,在算自己想要的定时时间时,可保持预分频器值不变,改变自动重装器的值,来得到自己想要的时间。更新标志位是手动产生更新事件,如果不更新开启定时器中断后会立刻进入一次中断。
第四步:定时器中断配置,调用TIM_ITConfig()开启定时器中断源。调用NVIC_PriorityGroupConfig()选择中断源分组,这里选择2分组即:两个抢占优先级和两个响应优先级,如果对中断优先级没有太大要求,通常选择2分组,抢占和响应各两个。调用NVIC_Init()结构体进行中断初始化,TIM2抢占给2,响应给1,TIM3抢占给2,响应给1;STM32的中断优先级处理首先看抢占优先级,只有当两个中断的抢占优先级不同时,抢占优先级高的中断才会打断抢占优先级低的中断。在这里,TIM3的抢占优先级为1,而TIM2的抢占优先级为2。因此,TIM3的抢占优先级高于TIM2。当两个中断同时请求服务时,TIM3的中断会先被响应,因为它具有更高的抢占优先级。响应优先级在这种情况下不起作用,因为它们的抢占优先级已经不同,响应优先级只在多个中断具有相同抢占优先级时才有意义(值越小级别越高)。
第五步:开启定时器使能,调用TIM_Cmd()开启
注意:当配置多个定时器初始化时,只需重新配置结构体成员即可。
- void Time_Init()
- {
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
-
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
- GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;
- GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
-
- TIM_InternalClockConfig(TIM2);//开启内部时钟TIM2
- TIM_InternalClockConfig(TIM3);//开启内部时钟TIM2
-
-
- TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
- TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;//定时器时钟预分频
- TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;//向上计数
- TIM_TimeBaseInitStructure.TIM_Period= 20000-1;//自动重装器:20000次,每2秒中断一次
- TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1;//预分频器:10000Hz
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;//用于配置高级定时器的重复计数功能
- //(这个项目不需要用,直接给0)
- TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
-
- TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;//定时器时钟预分频
- TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;//向上计数
- TIM_TimeBaseInitStructure.TIM_Period= 50000-1;//自动重装器:50000次,每5秒中断一次
- TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1;//预分频器:10000Hz
- TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;//用于配置高级定时器的重复计数功能
- //(这个项目不需要用直接给0)
- TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
-
-
- TIM_ClearFlag(TIM2,TIM_FLAG_Update);//更新标志位
- TIM_ClearFlag(TIM3,TIM_FLAG_Update);//更新标志位
-
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启中断源更新
- TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//开启中断源更新
-
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
-
- NVIC_InitTypeDef NVIC_InitStructure;
- NVIC_InitStructure.NVIC_IRQChannel= TIM2_IRQn;//选择中断通道
- NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;//开启中断通道
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2;//抢占优先级(值越小级别越高)
- NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;//响应优先级(即子优先级,值越小级别越高)
- NVIC_Init(&NVIC_InitStructure);
-
- NVIC_InitStructure.NVIC_IRQChannel= TIM3_IRQn;//选择中断通道
- NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1;//抢占优先级(值越小级别越高)
- NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;//响应优先级(即子优先级,值越小级别越高)
- NVIC_Init(&NVIC_InitStructure);
-
- TIM_Cmd(TIM2,ENABLE);//开启TIM2定时器
- TIM_Cmd(TIM3,ENABLE);//开启TIM3定时器
- }
USART初始化配置:
第一步:开启外设时钟、GPIO初始化,方法跟TIM中配置的一样,选择USART1,GPIO_Pin_9,
模式为复用推挽输出模式(即片上外设输出模式)。
第二步:配置USART初始化,调用USART_Init()结构体进行配置,结构体成员USART_BaudRate波特率需要选择跟串口助手一样的波特率才能成功传输;模式选择为发送TX模式。
第三步:开启USART使能,调用USART_Cmd();
- void Usart_Init()
- {
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
-
- GPIO_InitTypeDef GPIO_InitStructure;
- GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
- GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
- GPIO_Init(GPIOA,&GPIO_InitStructure);
-
-
- USART_InitTypeDef USART_InitStructure;
- USART_InitStructure.USART_BaudRate= 9600;//波特率
- USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//硬件流控模式
- USART_InitStructure.USART_Mode= USART_Mode_Tx;//模式选择
- USART_InitStructure.USART_Parity= USART_Parity_No;//奇偶校验
- USART_InitStructure.USART_StopBits= USART_StopBits_1;//数据帧结尾处的停止位的数量
- USART_InitStructure.USART_WordLength= USART_WordLength_8b;//传输数据长度(如需要校验选择9位)
- USART_Init(USART1,&USART_InitStructure);
-
- USART_Cmd(USART1,ENABLE);//USART使能
- }
USART发送一个字节代码:自定义一个无返回值函数用于发送一个字节,调用USART_SendData();进行字节发送,调用USART_GetFlagStatus();检测TXE的标志位,加上while循环用来判断传输是否完成,如果TXE=0(RESET)即还没传输完毕,继续在while中循环传输,检测到1时,传输完成跳出while循环。
- //发送一个字节
- void Usart_SendByte(uint8_t Byte)
- {
- USART_SendData(USART1,Byte);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待TXE标志位置1(即传输完毕)
- }
USART发送一个字符串:定义一个函数参数为指针,指针用来指向要发送字符串的首地址,用for循环加上面的发送一个字节函数,把字符串一个一个遍历发送出去。
- //发送字符串
- void Usart_SendString(char*String)//char*指针指向要发送String的字符串的起始位置
- {
- uint16_t i;
- for(i=0;String[i]!=0;i++)
- {
- Usart_SendByte(String[i]);
- }
- }
TIM2、TIM3中断函数:两个定时器中断函数有自己专门的函数名字,可以在标准库文件Start中md.s中找到(如下图),在中断中调用TIM_GetITStatus()用if判断是否为对应定时器中断,如果是TIM_GetITStatus()==SET(即等于1)则进入中断执行程序,最后需要清理中断调用TIM_ClearITPendingBit()为下一次中断做准备;TIM2为LED中断程序,先给PA0口高电平点亮LED,延时2秒,再判断PA0口电平进行反转电平(即使LED熄灭后再次开始定时);TIM3为USART串口通信,可直接调用自定义函数Usart_SendString();发送字符串,这里以"Hello World"为例。
- void TIM2_IRQHandler(void)
- {
- if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
- {
- GPIO_SetBits(GPIOA, GPIO_Pin_0);//将PA0引脚设置为高电平
- Delay_ms(2000);//延迟2ms
- if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_0)==SET)
- {
- GPIO_ResetBits(GPIOA,GPIO_Pin_0);//将PA0引脚设置为低电平
- }
- TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
- }
- }
-
-
- void TIM3_IRQHandler(void)
- {
- if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
- {
- Usart_SendString("Hello,World\r\n");
- TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
- }
- }
主函数:对各个自定义函数在主函数中初始化
- #include "stm32f10x.h" // Device header
-
-
- void Delay_ms(uint32_t xms)//延迟函数
- {
- while(xms--)
- {
- Delay_us(1000);
- }
- }
-
-
- int main(void)
- {
- OLED_Init();
- Time_Init();
- Usart_Init();
- while(1)
- {
-
- }
- }
接线图:图中OLED部分可不接,本实验没用到OLED,另外在32的PA0口连接一个LED,正极连接PA0,负极连接面包板负极。
实验现象:
WeChat_20240310163007
以上内容都是自己自学和理解,如有理解不正确的地方,可在评论区指出或者私信我进行更改,大家共同进步,希望这篇文章对大家有帮助。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。