仿ALIENTEK STM32开发板,写的STM8S的延时函数(基于IAR),汇编精确延时.奉献给大家了.

2019-07-19 20:06发布

已经写完了STM8S的系统文件(同stm32一样,包括sys.c,delay.c和uart.c三个文件)
这里先分享delay.c和delay.h.
本来想仿效STM32用一个定时器来做,无奈STM8S提供的是一个8位定时器,真是食之无味了...
算来算去,用这个8位的定时器横竖都不好做延时,用16位的定时器嘛又怕浪费.真不知道st的人怎么想的.
这个8位的基本定时器,连ucosii的滴答时钟都不好提供...(24Mhz下,最大只能1.3ms左右中断一次,16Mhz则是2ms左右)
难不成多次中断,触发一次任务切换?
所以,表示蛋疼的很,无奈只能想用汇编的方式来实现精确延时了.
研究了几天,终于小有成就.汇编写了一个us级延时函数,还算比较准.
24M和8M时,差别有点多,24M时STM8S要插入一个flash等待周期,这样st说是可以提供20MIPS的性能,但是我迷糊了...到底此时的指令周期如何算?st没有提到.
最后测试发现,大概是19Mhz的指令运行速度,也就是单周期指令,执行时间在24M时钟频率下约为1/19Mhz 秒.单根据我的计算公式,得不到19这个值,于是只能取近似了,按理说20比较准,不过实测16会准确一些.故采用了16,然后导致的结果就是延时变快了.
比如延时100ms,实际上可能是92ms左右.

而8Mhz时,差别有点多,表示不太理解,因为根据我的设计,刚好是8个周期的,应该是延时准确才对,但事实并非如此.
下面是各时钟频率下的延时准确性(测试时使用的延时至少>10us):
 //92%  @24Mhz
//98%   @16Mhz
//98%   @12Mhz
//86%   @8Mhz
可以看到,12M和16M条件下有比较高的准确度,24M稍微差点(主要是偏快).
一般应用,这样的精度,应该都可以满足使用了,源码下面附上,如有改进意见,欢迎提出,开源才能集思广益,而后才能精益求精.


delay.h代码如下:

#ifndef  __DELAY_H
#define  __DELAY_H
#include "sys.h"
//////////////////////////////////////////////////////////////////////////////// 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK
//使用汇编代码进行精确延时处理
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2013/6/25
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//******************************************************************************
//V1.0 20130625
//支持不同时钟频率
//提供delay_us,delay_ms两个延时函数.
//////////////////////////////////////////////////////////////////////////////// void delay_init(u8 clk); //延时函数初始化
void delay_us(u16 nus);  //us级延时函数,最大65536us.
void delay_ms(u32 nms);  //ms级延时函数
#endif

delay.c代码如下:

#include "delay.h"
//////////////////////////////////////////////////////////////////////////////// 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK
//使用汇编代码进行精确延时处理
//包括delay_us,delay_ms
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2013/6/25
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//******************************************************************************
//V1.0 20130625
//支持不同时钟频率
//提供delay_us,delay_ms两个延时函数.
////////////////////////////////////////////////////////////////////////////////
volatile u8 fac_us=0; //us延时倍乘数   //延时函数初始化
//为确保准确度,请保证时钟频率最好为4的倍数,最低8Mhz
//clk:时钟频率(24/16/12/8等)
void delay_init(u8 clk)
{
 if(clk>16)fac_us=(16-4)/4;//24Mhz时,stm8大概19个周期为1us
 else if(clk>4)fac_us=(clk-4)/4;
 else fac_us=1;
}
//延时nus
//延时时间=(fac_us*4+4)*nus*(T)
//其中,T为CPU运行频率(Mhz)的倒数,单位为us.
//准确度:
//92%  @24Mhz
//98%  @16Mhz
//98%  @12Mhz
//86%  @8Mhz
void delay_us(u16 nus)

__asm(
"PUSH A          "  //1T,压栈
"DELAY_XUS:      "  
"LD A,fac_us     "   //1T,fac_us加载到累加器A
"DELAY_US_1:     "  
"NOP             "  //1T,nop延时
"DEC A           "  //1T,A--
"JRNE DELAY_US_1 "   //不等于0,则跳转(2T)到DELAY_US_1继续执行,若等于0,则不跳转(1T).
"NOP             "  //1T,nop延时
"DECW X          "  //1T,x--
"JRNE DELAY_XUS  "    //不等于0,则跳转(2T)到DELAY_XUS继续执行,若等于0,则不跳转(1T).
"POP A           "  //1T,出栈
);
}
//延时nms 
//为保证准确度,nms不要大于16640.
void delay_ms(u32 nms)
{
 u8 t;
 if(nms>65)
 {
  t=nms/65;
  while(t--)delay_us(65000);
  nms=nms%65;
 }
 delay_us(nms*1000);
}                        
 
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
49条回答
NE558
1楼-- · 2019-07-23 13:19
回复【8楼】正点原子:
---------------------------------
原子哥 想问下 如果延时时音没到  会执行其它的吗? 嘿嘿看不懂汇编。。
NE558
2楼-- · 2019-07-23 17:47
 精彩回答 2  元偷偷看……
网络孤客
3楼-- · 2019-07-23 22:36
回复【37楼】正点原子:
---------------------------------
新手想请教:
1、不同型号STM8S延时误差是否一样?
2、你是怎样测试延时是否准确?
FrankDavids
4楼-- · 2019-07-24 02:45
 精彩回答 2  元偷偷看……
fanghuiopenedv
5楼-- · 2019-07-24 06:24
 精彩回答 2  元偷偷看……
Joen0_0
6楼-- · 2019-07-24 07:19
本帖最后由 Joen0_0 于 2016-5-25 20:23 编辑

#include "delay.h"

volatile u8 fac_us=0; //us延时倍乘数

void CLK_init(u8 clk)
{
  //CLK_SWR=0xB4; 外部时钟HSE
  CLK_SWR=0xe1;   //内部高速HSI
  CLK_CKDIVR=0x00;  //不分频

if(clk>16)fac_us=(16-4)/4;//24Mhz时,stm8大概19个周期为1us
else if(clk>4)fac_us=(clk-4)/4;
else fac_us=1;
}

void delayus(u16 us)
{
  __asm(
  "PUSH A          "  //1T,压栈
  "DELAY_XUS:       "   
  "LD A,fac_us      "   //1T,fac_us加载到累加器A
  "DELAY_US_1:      "  
  "NOP             "  //1T,nop延时
  "DEC A            "  //1T,A--
  "JRNE DELAY_US_1 "   //不等于0,则跳转(2T)到DELAY_US_1继续执行,若等于0,则不跳转(1T).
  "NOP             "  //1T,nop延时
  "DECW X          "  //1T,x--
  "JRNE DELAY_XUS   "    //不等于0,则跳转(2T)到DELAY_XUS继续执行,若等于0,则不跳转(1T).
  "POP A            "  //1T,出栈
  );
}

//为保证准确度,nms不要大于16640
void delayms(u16 ms)
{
  u8 t;
if(ms>65)
{
  t=ms/65;
  while(t--)delayus(65000);
  ms=ms%65;
}
delayus(ms*1000);
}

主函数
#include "include.h"

void LED_init(void)
{
  //PC_DDR|=0x08;设置PC3为输出模式
  //PC_CR1|=0x08;设置PC3为推挽输出
  //PC_CR2|=0x00;设置PC3为10MHz快速输出
  
  PB_DDR|=0x20;
  PB_CR1|=0x20;
  PB_CR2|=0x00;
}


void main( void )
{
  CLK_init(8);
  LED_init();
  
  while (1)
        {
          PB_ODR^=0x00;
           delayms(1000);
          PB_ODR^=0xf0;
           delayms(1000);
        }
}

以肉眼看秒表和灯,亮一秒,灭一秒,判定,16M的配置(HSI不分频),给8M的值才正常工作。
原子哥,是我哪写错了?

一周热门 更多>