使用stm8s时本来期望能够产生1us精度的脉冲,但是调来调去也没能实现。目前的情况是使用内部16MHz的RC时钟源,然后给到timer4,再经 8分频产生一个25us的中断,我能搞出来的最小中断周期是15us左右,考虑到那不是一个很好的整数,主要是当要延时整数比如100,1000时没办法除尽,所以就只好取25us为一个最小单位了。如果有人能用定时器产生1us的脉冲请指点一下。多谢。
下面上代码吧:
基本的思路就是使用timer4每隔25us计数一次,然后延时函数对timer4的中断次数进行统计。完成指定的延时时间后退出while循环。
#include"stm8s.h"
uint8_t startflag = 0;
uint16_t timercnt = 0;
/*******************************************************************************
函数名;init_clk()
功能 :初始化系统时钟
输入 :无
输出 :无
返回值:无
备注 :无
*******************************************************************************/
void init_clk(void)
{
//初始化时钟
CLK_HSICmd(ENABLE);//开始内部高频RC
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);//不分频
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
}
/*******************************************************************************
函数名:init_timer4()
功能 :初始化timer4
输入 :无
输出 :无
返回值:无
备注 :无
*******************************************************************************/
void init_timer4(void)
{
TIM4_TimeBaseInit(TIM4_PRESCALER_8, 50); //每个计数周期为0.5us@8分频@16MHz
TIM4_ClearFlag(TIM4_FLAG_UPDATE);
TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
TIM4_Cmd(ENABLE);
}
/*******************************************************************************
函数名:init_gpio()
功能 :初始化GPIO
输入 :无
输出 :无
返回值:无
备注 :无
*******************************************************************************/
void init_gpio(void)
{
GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_LOW_FAST);
}
/*******************************************************************************
函数名:delayus()
功能 :延时函数
输入 :us 微秒数
输出 :无
返回值:无
备注 :目前支持25us的整数倍,最小为25us,不能再小了
*******************************************************************************/
void delayus(uint16_t us)
{
//Set the flag to make ISR start to count
startflag = 1;
us = (us <= 25?25:us);
us = us / 25 - 1;
// TIM4_Cmd(ENABLE);
//Wait until expire
while(timercnt <= us);
//TIM4_Cmd(DISABLE);
//Clear flag and timer cnt
startflag = 0;
timercnt = 0;
}
/*******************************************************************************
函数名:delayms()
功能 :延时函数
输入 :ms 毫秒数
输出 :无
返回值:无
备注 :最小为1ms
*******************************************************************************/
void delayms(uint16_t ms)
{
uint16_t i=0;
uint16_t j=0;
for(i=0;i<ms;i++)
{
delayus(1000);
}
}
/*******************************************************************************
函数名:main()
功能 :主函数
输入 :无
输出 :无
返回值:无
备注 : 无
*******************************************************************************/
int main(void)
{
init_gpio();
init_clk();
init_timer4();
enableInterrupts();
while(1)
{
GPIO_WriteReverse(GPIOB, GPIO_PIN_5);
delayus(50);
//delayms(750);
}
return 0;
}
void assert_failed(u8* file, u32 line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
中断函数的:
/**
* @brief Timer4 Update/Overflow Interrupt routine.
* @param None
* @retval None
*/
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
/* In order to detect unexpected events during development,
it is recommended to set a breakpoint on the following instruction.
*/
if(startflag == 1)
{
timercnt++;
}
TIM4_ClearITPendingBit(TIM4_IT_UPDATE);
}
在使用示波器测试的时候发现,delayus(1000);时测出来的延时为1014us,那多出来的14us不知道从哪里来的,且使用其他参数时也会有多出来的10+us,对这个多出来的值看着有点难受,感觉这误差有点大啊。然后看程序觉得有可能是在实现的机制上有问题,因为计数器一直在工作,只有检测到startflag==1时才开始对timercnt进行累加,这在一定程度上存在至少1个计数周期的延误,比如上次计数器刚刚完成中断,此时用户就设置了startflag=1,但是timer4要等到下次中断来的时候才能计数,所以至少有25us不能计数,不对啊,此时写日志时才意识到这个是导致它少计数的,而不是多计数的啊。当时发现这个问题的时候无以为这样会导致它会多计时了,所以就像不使用这个机制,修改一下。先让计数器暂停工作,然后用户调用delayus()时再启动计数,延时时间到了之后再停止timer4,也就是我在上面delayus()中注释掉的TIM4_Cmd()两个接口。因为这样改完后,使用示波器一次,发现误差更大了,还不如一开始使用的那个机制,我猜可能是在delayus()中多调用了2次 使能和禁能接口导致的。所以最后还是换回去开始的机制了,也就是上面的代码。
哦对了,再补充一下,我觉得使用纯软件延时太扯淡了。虽然单次直接使用
while(1)
{
GPIO_WriteReverse(GPIOB, GPIO_PIN_5);
}
这种方法就可以实现100ns左右的延时输出方波,但是如果想延时200ns,300ns,500ns,650ns这样的延时的话实在是很麻烦,因为它并不是像看上去的那样只要简单的成倍数的修改参数就行了,完全没有规律,反正我是找不着,而且找这个规律实在是太烦了,这样里面牵扯一个函数调用时间的消耗,就是即使调用一个空函数也会消耗时间,以及每次调用的误差在多次调用的累加的问题,比如调用1次asm(“nop”),和2次,3次,4次,直到多次的耗时竟然不是一个规矩的线性的递增,反正我是根据它们的差计数得出的延时与实际用示波器测试出来的结果有不小的误差,先不搞了,也许以后有时间再来搞吧。