当前位置:   article > 正文

STM32F103C8T6内部定时器中断控制LED和USART串口通信代码_c8t6的定时器1中断

c8t6的定时器1中断

         本实验通过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()开启

注意:当配置多个定时器初始化时,只需重新配置结构体成员即可。

  1. void Time_Init()
  2. {
  3. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
  4. RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
  5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  6. GPIO_InitTypeDef GPIO_InitStructure;
  7. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP;
  8. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_0;
  9. GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
  10. GPIO_Init(GPIOA,&GPIO_InitStructure);
  11. TIM_InternalClockConfig(TIM2);//开启内部时钟TIM2
  12. TIM_InternalClockConfig(TIM3);//开启内部时钟TIM2
  13. TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
  14. TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;//定时器时钟预分频
  15. TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;//向上计数
  16. TIM_TimeBaseInitStructure.TIM_Period= 20000-1;//自动重装器:20000次,每2秒中断一次
  17. TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1;//预分频器:10000Hz
  18. TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;//用于配置高级定时器的重复计数功能
  19. //(这个项目不需要用,直接给0)
  20. TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
  21. TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV1;//定时器时钟预分频
  22. TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;//向上计数
  23. TIM_TimeBaseInitStructure.TIM_Period= 50000-1;//自动重装器:50000次,每5秒中断一次
  24. TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1;//预分频器:10000Hz
  25. TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;//用于配置高级定时器的重复计数功能
  26. //(这个项目不需要用直接给0)
  27. TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
  28. TIM_ClearFlag(TIM2,TIM_FLAG_Update);//更新标志位
  29. TIM_ClearFlag(TIM3,TIM_FLAG_Update);//更新标志位
  30. TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启中断源更新
  31. TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);//开启中断源更新
  32. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组
  33. NVIC_InitTypeDef NVIC_InitStructure;
  34. NVIC_InitStructure.NVIC_IRQChannel= TIM2_IRQn;//选择中断通道
  35. NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;//开启中断通道
  36. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 2;//抢占优先级(值越小级别越高)
  37. NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;//响应优先级(即子优先级,值越小级别越高)
  38. NVIC_Init(&NVIC_InitStructure);
  39. NVIC_InitStructure.NVIC_IRQChannel= TIM3_IRQn;//选择中断通道
  40. NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;
  41. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1;//抢占优先级(值越小级别越高)
  42. NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;//响应优先级(即子优先级,值越小级别越高)
  43. NVIC_Init(&NVIC_InitStructure);
  44. TIM_Cmd(TIM2,ENABLE);//开启TIM2定时器
  45. TIM_Cmd(TIM3,ENABLE);//开启TIM3定时器
  46. }

USART初始化配置:

第一步:开启外设时钟、GPIO初始化,方法跟TIM中配置的一样,选择USART1,GPIO_Pin_9,

模式为复用推挽输出模式(即片上外设输出模式)。

第二步:配置USART初始化,调用USART_Init()结构体进行配置,结构体成员USART_BaudRate波特率需要选择跟串口助手一样的波特率才能成功传输;模式选择为发送TX模式。

第三步:开启USART使能,调用USART_Cmd();

  1. void Usart_Init()
  2. {
  3. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
  4. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
  5. GPIO_InitTypeDef GPIO_InitStructure;
  6. GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;
  7. GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;
  8. GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
  9. GPIO_Init(GPIOA,&GPIO_InitStructure);
  10. USART_InitTypeDef USART_InitStructure;
  11. USART_InitStructure.USART_BaudRate= 9600;//波特率
  12. USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//硬件流控模式
  13. USART_InitStructure.USART_Mode= USART_Mode_Tx;//模式选择
  14. USART_InitStructure.USART_Parity= USART_Parity_No;//奇偶校验
  15. USART_InitStructure.USART_StopBits= USART_StopBits_1;//数据帧结尾处的停止位的数量
  16. USART_InitStructure.USART_WordLength= USART_WordLength_8b;//传输数据长度(如需要校验选择9位)
  17. USART_Init(USART1,&USART_InitStructure);
  18. USART_Cmd(USART1,ENABLE);//USART使能
  19. }

       USART发送一个字节代码:自定义一个无返回值函数用于发送一个字节,调用USART_SendData();进行字节发送,调用USART_GetFlagStatus();检测TXE的标志位,加上while循环用来判断传输是否完成,如果TXE=0(RESET)即还没传输完毕,继续在while中循环传输,检测到1时,传输完成跳出while循环。

  1. //发送一个字节
  2. void Usart_SendByte(uint8_t Byte)
  3. {
  4. USART_SendData(USART1,Byte);
  5. while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待TXE标志位置1(即传输完毕)
  6. }

        USART发送一个字符串:定义一个函数参数为指针,指针用来指向要发送字符串的首地址,用for循环加上面的发送一个字节函数,把字符串一个一个遍历发送出去。

  1. //发送字符串
  2. void Usart_SendString(char*String)//char*指针指向要发送String的字符串的起始位置
  3. {
  4. uint16_t i;
  5. for(i=0;String[i]!=0;i++)
  6. {
  7. Usart_SendByte(String[i]);
  8. }
  9. }

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"为例。

  1. void TIM2_IRQHandler(void)
  2. {
  3. if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
  4. {
  5. GPIO_SetBits(GPIOA, GPIO_Pin_0);//将PA0引脚设置为高电平
  6. Delay_ms(2000);//延迟2ms
  7. if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_0)==SET)
  8. {
  9. GPIO_ResetBits(GPIOA,GPIO_Pin_0);//将PA0引脚设置为低电平
  10. }
  11. TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
  12. }
  13. }
  14. void TIM3_IRQHandler(void)
  15. {
  16. if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET)
  17. {
  18. Usart_SendString("Hello,World\r\n");
  19. TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
  20. }
  21. }

主函数:对各个自定义函数在主函数中初始化

  1. #include "stm32f10x.h" // Device header
  2. void Delay_ms(uint32_t xms)//延迟函数
  3. {
  4. while(xms--)
  5. {
  6. Delay_us(1000);
  7. }
  8. }
  9. int main(void)
  10. {
  11. OLED_Init();
  12. Time_Init();
  13. Usart_Init();
  14. while(1)
  15. {
  16. }
  17. }

接线图:图中OLED部分可不接,本实验没用到OLED,另外在32的PA0口连接一个LED,正极连接PA0,负极连接面包板负极。

实验现象:

WeChat_20240310163007

以上内容都是自己自学和理解,如有理解不正确的地方,可在评论区指出或者私信我进行更改,大家共同进步,希望这篇文章对大家有帮助。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/433271
推荐阅读
相关标签
  

闽ICP备14008679号