打印
[STM8]

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

[复制链接]
6177|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

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

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

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

#include "stdio.h"
#include "iostm8s105k4.h"
#include "UART.h"

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

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

//****************************************************************************

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

//****************************************************************************

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

//****************************************************************************

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

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

//****************************************************************************

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

  TIM4_Init();                                                                  //TIM4初始化
  TIM4_IF_Clear();
  
  asm("RIM");                                                                   //插入汇编,使能全局中断
  
  while(1)
  {   
    i++;
    if(n==17)
    {
      for(n2=0; n2<17; n2++)
      {
        printf("前10次中断后 i = %u\n",isr[n2]);                                //用系统的函数显示                              
        //UART2_ShowU16(isr[n2]);                                               //或者用自己写的函数显示
        //UART2_SendEnter();
      }
      while(1);                                                                 //进入死循环
    }
  }
}
解决了IAR STM8 TIM4首次定时不准确的问题,又产生了其它问题

02.png (249.37 KB )

02.png

01.png (279.15 KB )

01.png

STM8S105.rar

481.49 KB

使用特权

评论回复
沙发
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 不正常

使用特权

评论回复
5
香水城| | 2021-8-31 11:33 | 只看该作者
你是从STM32转过来的吧,STM8的定时器是16位没错,赋值要分高低来操作。

TIM1->ARRH  = TIM1_ARRH_VALUE;
  TIM1->ARRL  = TIM1_ARRL_VALUE;

使用特权

评论回复
6
panxiaoyi|  楼主 | 2021-8-31 12:26 | 只看该作者
你好,这个代码是我自己写出来,TIM4是8位的,没有涉及到高低8位的操作,你提到的是TIM1

使用特权

评论回复
7
香水城| | 2021-8-31 14:01 | 只看该作者
嗯 TIM4是8位的,那么这个赋值不会有问题,问题可能跟你这个输出有关。

你把分频系数再加大点验证下。

使用特权

评论回复
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,运行结果正常

明天总结后再传代码上来

使用特权

评论回复
9
panxiaoyi|  楼主 | 2021-9-1 08:53 | 只看该作者
在主程序中,在 i++ 的前面加入中断关闭,在 i++ 的后面加入中断开启。这样就可以避免出错了,代码如下

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


总结:在8位机中,在中断程序里面读取主程序的大于8位的变量时,必须要考虑读取的结果有可能出错,因为中断有可能打乱了该变量的多字节的赋值时序

使用特权

评论回复
10
panxiaoyi|  楼主 | 2021-9-1 09:07 | 只看该作者
如果是16位机或者32位机,这个错误就不会发生,所以,@香水城,你的指点是很有参考价值的,多谢

使用特权

评论回复
11
kiwis66| | 2021-9-1 09:20 | 只看该作者
感觉8的应用,比32复杂呢~

使用特权

评论回复
12
单片小菜| | 2021-9-1 11:11 | 只看该作者
现在使用的KEIL比较多吧,IAR越来越少了。

使用特权

评论回复
13
littlelida| | 2021-9-1 14:56 | 只看该作者
没用过8,看着代码,还真不适应~~
纯学习了

使用特权

评论回复
14
heimaojingzhang| | 2021-9-10 12:11 | 只看该作者
首次不准的原因是什么呀

使用特权

评论回复
15
keaibukelian| | 2021-9-10 12:13 | 只看该作者
本人还没有用过这个系列的芯片

使用特权

评论回复
16
labasi| | 2021-9-10 12:14 | 只看该作者
这个不是双核的吧

使用特权

评论回复
17
paotangsan| | 2021-9-10 12:16 | 只看该作者
其实应用起来不那么复杂

使用特权

评论回复
18
renzheshengui| | 2021-9-10 13:39 | 只看该作者
为什么改成249就好了呢

使用特权

评论回复
19
panxiaoyi|  楼主 | 2021-9-11 10:23 | 只看该作者
renzheshengui 发表于 2021-9-10 13:39
为什么改成249就好了呢

这个是概率问题,如果中断发生时,主程序也刚刚好在对 i 赋值,且赋值到一半(赋值了高八位,低八位还没有赋值),这时,中断程序又读取 i ,则读取的数据就是错误的数据,如果中断时没有遇上 i 的赋值,则程序正常

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

47

主题

384

帖子

2

粉丝