[STM8] 解决了IAR STM8 TIM4首次定时不准确的问题,又产生了其它问题

[复制链接]
6817|18
 楼主| panxiaoyi 发表于 2021-8-31 09:45 | 显示全部楼层 |阅读模式

  1. //STM8S105_UART2_TIM4 ,IAR V3.11 。每个新工程必须设置:芯片型号 、输出hex 、ST_Link

  2. //STM8的TIM4/6是最简单的8位定时计数器,基本上只具备自动重载和计数溢出中断功能。

  3. //设置自动重装载寄存器时,新写入的值可以立刻生效,也可以设置为有新的事件产生时才能生效。
  4. //设置预分频寄存器时,新写入的值不会立刻生效,必须要等到有新的事件产生时才能生效。
  5. //什么是事件产生呢?就是计数溢出,或者写1到事件产生寄存器的UG位。
  6. //其实预分频寄存器和自动重装载寄存器都属于缓冲器,真正起作用的是它们背后的影子寄存器。
  7. //事件产生时,这两个缓冲寄存器的值都会立刻更新到影子寄存器。

  8. #include "stdio.h"
  9. #include "iostm8s105k4.h"
  10. #include "UART.h"

  11. #define TIM4_IF_Clear() TIM4_SR=0x00                                            //清除中断标记

  12. unsigned short i, isr[17];
  13. unsigned char  n=0, n2=0;

  14. //****************************************************************************

  15. void SYSTEM_Init(void)
  16. {
  17.   CLK_ICKR=0x01;                                                                //HSI_EN,高速内部RC振荡器使能,16MHz
  18.   CLK_CKDIVR=((0<<3)|0);                                                        //HSI分频取值0-3,CPU分频取值0-7 ,它们都代表1/2/4/8/...分频
  19.   CLK_PCKENR1=0xFF;                                                             //全部外设时钟使能,可关闭不用的外设时钟以实现节能
  20.   CLK_PCKENR2=0xFF;
  21. }

  22. //****************************************************************************

  23. void TIM4_Init(void)
  24. {
  25.   TIM4_CR1_ARPE=1;                                                              //使能自动重装载寄存器缓冲器,该语句可以取消
  26.   TIM4_CR1_URS=1;                                                               //计数溢出时才产生事件,自动重装载影子寄存器更新时不产生事件,重要语句!
  27.   TIM4_PSCR=7;                                                                  //128分频,最大128分频
  28.   TIM4_ARR=249;                                                                 //中断频率 = 时钟 /(n分频 * (TIM4_ARR+1)),如果时钟是16MHz,则中断频率=500Hz
  29.   TIM4_EGR_UG=1;                                                                //人为产生更新事件,使能预分频值(也包括自动重装载值)更新到影子寄存器,并且计数重新开始
  30.   TIM4_IER_UIE=1;                                                               //计数溢出中断使能
  31.   TIM4_CR1_CEN=1;                                                               //TIM使能
  32. }

  33. //****************************************************************************

  34. #pragma vector=23+2                                                             //中断向量,由于中断向量号前面有两个空缺,因此要偏移

  35. __interrupt void TIM4_Isr(void)                                                 //TIM4中断函数,函数名可以自定义
  36. {
  37.   if(n<17)                                                                      //每次中断都记录主循环i的值
  38.   {
  39.     isr[n]=i;                          
  40.     n++;
  41.   }
  42.   TIM4_SR=0x00;                                                                 //清零中断标记
  43.   i=0;
  44. }

  45. //****************************************************************************

  46. int main( void )
  47. {
  48.   SYSTEM_Init();                                                                //系统初始化
  49.   UART2_Init();                                                                 //串口2初始化
  50.   
  51.   printf("QQ68354248\n");                                                       //使用该语句前必须包含stdio.h文件并重定义putchar函数

  52.   TIM4_Init();                                                                  //TIM4初始化
  53.   TIM4_IF_Clear();
  54.   
  55.   asm("RIM");                                                                   //插入汇编,使能全局中断
  56.   
  57.   while(1)
  58.   {   
  59.     i++;
  60.     if(n==17)
  61.     {
  62.       for(n2=0; n2<17; n2++)
  63.       {
  64.         printf("前10次中断后 i = %u\n",isr[n2]);                                //用系统的函数显示                              
  65.         //UART2_ShowU16(isr[n2]);                                               //或者用自己写的函数显示
  66.         //UART2_SendEnter();
  67.       }
  68.       while(1);                                                                 //进入死循环
  69.     }
  70.   }
  71. }
解决了IAR STM8 TIM4首次定时不准确的问题,又产生了其它问题
01.png
02.png

STM8S105.rar

481.49 KB, 下载次数: 10

 楼主| panxiaoyi 发表于 2021-8-31 09:48 | 显示全部楼层
问题就出现在这个语句上

TIM4_ARR=249

如果等于250,定时器中断时间正常,如果等于249,定时器中断时间不正常,请看图片
 楼主| panxiaoyi 发表于 2021-8-31 09:50 | 显示全部楼层
不正常的时候,每中断三次,就有一次的结果 = 上两次结果相加
 楼主| panxiaoyi 发表于 2021-8-31 09:52 | 显示全部楼层
请教大家帮我分析一下,是什么原因?

TIM4_ARR=249,取其它数字如 210 ,211 都正常,212 不正常
香水城 发表于 2021-8-31 11:33 | 显示全部楼层
你是从STM32转过来的吧,STM8的定时器是16位没错,赋值要分高低来操作。

TIM1->ARRH  = TIM1_ARRH_VALUE;
  TIM1->ARRL  = TIM1_ARRL_VALUE;
 楼主| panxiaoyi 发表于 2021-8-31 12:26 来自手机 | 显示全部楼层
你好,这个代码是我自己写出来,TIM4是8位的,没有涉及到高低8位的操作,你提到的是TIM1
香水城 发表于 2021-8-31 14:01 | 显示全部楼层
嗯 TIM4是8位的,那么这个赋值不会有问题,问题可能跟你这个输出有关。

你把分频系数再加大点验证下。
 楼主| panxiaoyi 发表于 2021-9-1 01:20 | 显示全部楼层

找到问题的根源了,上面的主循环可以等效于

while(1)
{
  i++;            // i 是16位变量
}

由于 i 是16位变量,且变化速度非常的快,而且很多时候,CPU都是在将新的结果赋值给 i

CPU在赋值给 i 时,应该是分成 2 条指令完成,即,先赋值给高8位,再赋值给低8位(我不懂汇编哦,这个是瞎猜的)

由于中断频率非常高,这样,在主程序中,CPU就有可能只赋值了 i 的高8位,低8位还没有来得及赋值就被中断了

而在中断语句运行时,我这时候去读取 i 的值就肯定出错。

我刚才把 i 修改成8位的 i 和 i2,运行结果正常

明天总结后再传代码上来
 楼主| panxiaoyi 发表于 2021-9-1 08:53 | 显示全部楼层
在主程序中,在 i++ 的前面加入中断关闭,在 i++ 的后面加入中断开启。这样就可以避免出错了,代码如下

  1.     TIM4_IER_UIE=0;    //计数溢出中断关闭
  2.     i++;               //16位变量
  3.     TIM4_IER_UIE=1;    //计数溢出中断使能


总结:在8位机中,在中断程序里面读取主程序的大于8位的变量时,必须要考虑读取的结果有可能出错,因为中断有可能打乱了该变量的多字节的赋值时序
 楼主| panxiaoyi 发表于 2021-9-1 09:07 | 显示全部楼层
如果是16位机或者32位机,这个错误就不会发生,所以,@香水城,你的指点是很有参考价值的,多谢
kiwis66 发表于 2021-9-1 09:20 | 显示全部楼层
感觉8的应用,比32复杂呢~
单片小菜 发表于 2021-9-1 11:11 | 显示全部楼层
现在使用的KEIL比较多吧,IAR越来越少了。
littlelida 发表于 2021-9-1 14:56 | 显示全部楼层
没用过8,看着代码,还真不适应~~
纯学习了
heimaojingzhang 发表于 2021-9-10 12:11 | 显示全部楼层
首次不准的原因是什么呀
keaibukelian 发表于 2021-9-10 12:13 | 显示全部楼层
本人还没有用过这个系列的芯片
labasi 发表于 2021-9-10 12:14 | 显示全部楼层
这个不是双核的吧
paotangsan 发表于 2021-9-10 12:16 | 显示全部楼层
其实应用起来不那么复杂
renzheshengui 发表于 2021-9-10 13:39 | 显示全部楼层
为什么改成249就好了呢
 楼主| panxiaoyi 发表于 2021-9-11 10:23 | 显示全部楼层
renzheshengui 发表于 2021-9-10 13:39
为什么改成249就好了呢

这个是概率问题,如果中断发生时,主程序也刚刚好在对 i 赋值,且赋值到一半(赋值了高八位,低八位还没有赋值),这时,中断程序又读取 i ,则读取的数据就是错误的数据,如果中断时没有遇上 i 的赋值,则程序正常
您需要登录后才可以回帖 登录 | 注册

本版积分规则

53

主题

417

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部