第一个STM8项目的记录

说明

我的第一个项目是stm8。
显示部分是点阵加数码管,通过串口控制下位机变频器的运行。虽然看起来很简单,但是项目复杂度超过我以前开发板上的所有项目,因此碰到了很多前所未有的问题。在此记录一笔。
注,这是本人第一个项目的记录,不保证科学与正确


1,misplaced local declaration错误

变量必须在函数的最开始处一口气全部声明或者定义掉
这其实是说明cosmic不支持c99的原因。


2,stm8中,各数据类型长度

类型 长度
char 1字节8bit
int 2字节16bit
short int 2字节16bit
float 4字节32bit
double 4字节32bit
long int 4字节32bit
long long int 4字节32bit

为了以后移植方便,我决定采用C99的扩展类型uint8_t uint16_t这种。


3,STVD的中断中不能使用long类型

小标题只是该bug的表现之一。确切的bug描述是
在stm8_interrupt_vector.c使用超过2byte的类型。就会产生类似段重合之类的错误。
这里写图片描述
下面是bug的详细描述。

3.1。

在stm8_interrupt_vector.c中,函数定义里的变量不能超过2个byte。这个函数不仅仅是中断处理函数,还包括放在这个文件中的普通函数,这个变量限制对全局变量局部变量同样有效。

3.2。

同样,在这个文件中的变量默认是signed类型,就算在使用前声明为extern unsiged也不行,但是在使用中进行强制类型转换(unsigned int )或者数字后面加u是可行的。
这句话的意思是比如声明变量的时候为unsiged int,那么理论上的变量范围是0~65535。而stm8的int类型是2byte,理论上是不会报错的。
但是实际上,如果变量使用的时候超过了32768。那么就会报错。即变量在使用过程中如果超过了有符号类型的最大值,那么编译器就会进行扩展成4byte。
这种bug,即使在使用文件前extern一下也是不行的。
但是,如果在使用中,再进行一次强制类型转换(unsigned int)或者数字后面加个u,却是的可行。

3.3。

不可以用long类型。即不可以让变量超过2个byte。

3.4。

但是,如果函数定义在其他地方,而该文件只是声明引用的话,没有问题。因此很多人没发现问题是,他们把中断函数定义放在了其他地方,而stm8_interrupt_vector.c只是声明。
此外我查看lxf文件,发现stm8_interrupt_vector.cs是链接在const区的,也许问题出在这里。但是我水平不够,暂时无法发现真正的原因。


4,IAR的一些初始化操作

iar for stm烧录程序,开始新建工程时候选择芯片是无效的,一定要在打开工程后右击工程-option-general option-选择芯片。不然能编译,但是下载无效。
此外,若是使用标准库,还需要在C/C++Compiler中的preprcessor选择工程头文件路径和预定义符号
当前工程文件目录代码是PROJDIR
预定义符号选择芯片就可以了。
需要在工程里添加标准库的C源码文件,h文件不用添加,上面包含了。
对不需要的C文件,要么不添加进去,要么右击option-exclude from build排除编译之外。


5,stm8的一些问题

stm不能同时初始化多个同功能的定时器,我初始化了2个时间不同的溢出定时器结果出现问题。且这个不是时间重合带来的问题,因为我只要一开第二个定时器的功能就会出问题。至今未能找到原因。因为我后来只用了一个定时器。
stm的定时中断处理程序的时间是微秒级。
曾经,我定时处理程序是10us一次,然后处理了很多东西,结果发现定时不准时。最后发现,是10us处理一次太快了。导致处理中多次被中断,进而影响时间。
后来,我用了个定时器1ms的中断,但是实际运行中发现,40行的代码,全部只进行取余判断从来递增递减取反置一相关全局变量。抓波形测出一次中断运行时间居然要170us以上。换句话说,1ms中断时候,在中断中运行的时间就接近五分之一。
后来我用了10ms的中断,结果同样的代码,运行时间缩短到了50us以下。看来中断的进入转出需要比较长的时间。
用stm的蜂鸣器必须配置选项字节中的AFR7为BEEP。
LSI的时钟校准是通过AWU测定的。如果把这个外设时钟关了,会进入忙循环导致没有任何反应。
不能关,蜂鸣器外设是通过这个使能的,关了就开不了了


6,串口发送速度

在用定时器控制串口发送频率时,有点特殊。
串口本身的发送时间是20~30ms,按照下位机的要求,我的定时器规定100ms中断一次,然后发送数据,那么出现的情况是如下这样。
0ms定时器中断写1—-30ms串口发送数据完毕,对定时器flag写0—-100ms定时器中断写1—-30ms后串口发送数据完毕。
在这里上一串串口数据发送结束到下一串数据发送开始,中间间隔是70ms。而我想要的是上一串串口数据结束到下一串数据开始,中间间隔100ms。
解决方法,有两种。
1是将定时器中断弄长一点,比如130ms,那么就能使100ms间隔了,不过这个方法的问题时,串口本身发送数据的时间是不定的。也即是串口数据之间的间隔是不定的。
2是定时器中断时间短一点,比如1ms一次,那么当数据大于100ms时发送数据,发送完写0,等下一次大于100ms再次发送数据。这种方法间隔比较准确。
3最后敲定最终方案,10ms一次加1,15次后开始发送。


7,一些C错误

Warning[Pa082]: undefined behavior: the order of volatile accesses is undefined in this statement
运算符两边都是volatile变量的警告。一般来说volatile不应该参与计算。
神奇的缺少)错误,是因为#define的时候多加了个分号。
神奇的缺少;错误,是在预处理文件中发生的,原因是前一个预处理文件的最后一个语句忘了加分号。
在使用标准库可变参数时候。va相关函数,总是发现数据不对。
最后发现是va_arg在计算步长是用的是int(2个字节),而我是却将其设为uint8_t一个字节。将u8改成int就对了。
在测试串口的时候,发现串口怎么也不发送数据。最后检测发现,没有清除发送完成中断的flag。同样的问题,我在定时器处理的时候也发生过。
所以下次注意,不用忘记在中断处理程序中清除中断相关flag,当然如果不用(我用不到发送完成中断),最好不要随便开中断。尤其是那种不是自动处理的中断。
同样的,我还不小心开了校验中断,因此会莫名其妙的不发送数据。
这里总结下,stm的中断flag必须手动清除!!如果找不到,那么能不用就不用。
外部中断这个我没找到如何手动清除,如果出问题要注意。查资料得出,8s没有,8l有。
expected an identifier,使用enum类型报错。是因为重复定义,前面#define了。
要小心执行一次和执行多次的区别,有些函数我们只需要在一定时间内执行一次就好了。
在stm的程序容量超过32k(地址超过64k),会出现奇怪的定义错误。
在iar设置里面降代码改成large就可以了。这个其实就是stvd长指针的意思。


8,其他

原本我是TIM4 8位中断,后来10ms不够了,所以用了TIM3,但是发现不能中断。反复检查初始化没有问题。
后来发现,利用库函数校准LSI的时候,需要用到TIM3的捕获功能,这个函数一定要放在主函数时钟初始化之后,定时溢出初始化之前。
我在进行LED半秒闪烁的时候,先开始是等于一个时间值(比如50)然后开,等于另一个时间值(比如100)关,但是发现总是闪烁几下,卡1-2秒,再正常。
后来我改成了范围判断,正常了。
原因是程序速度跟不上,正好跳过了这个定时值,时间值是根据定时器定期变化的,变化的频率是10ms的时间。但是程序运行中这个函数的运行频率不一定正好10ms内一定能运行一次。实际情况可能是,函数正好在49的时候运行了下,51的时候又运行了,恰恰是跳过了50。
所有的取余运算都会有这个,很可能会跳过需要的数值。个人认为改成范围判断会好很多。
在进行同一个点阵,但是同时显示两个不相关内容时,发现会有闪屏现象
因为其中一个内容需要定时闪烁一个点,我为了使两种显示不冲突,用的是或运算填充数据,即读出数据,或运算,输入数据。因此,闪烁功能的实现必须要清空屏幕才能搞定。
后来我改了下,在跑圈内部等到闪烁的时候请空屏幕。然后把中间的数字显示放到了跑圈下面。改了下两个函数的调用次数,总算搞定。
后来采用了更好的显存显示方案。再也没有这种问题。因为对显存的各种操作,只要最后没有显示出来,那是可以任意更改不用考虑次序的。
因此这里总结显示的输出模式是
1,填充显存数据
2,根据不同的情况略微修改显存数据
3,输出。然后123循环。
这样的好处是,修改的时候不涉及输出,因为1,和2的数据可以不同也不会闪屏。


9,优化

容量居然不够用。
标准库的初始化函数占据空间比寄存器大,是大很多很多,我测试了两个函数的寄存器实现和库实现。编译出来的容量库比寄存器大了一个8倍,一个20倍
查看发现,所有的只读变量,包含只读常量数据,代码全部在flash里面。
所有的可写全局变量,静态全局变量还有堆栈全部在RAM中。

查看iar汇编代码。
在定时中有两种判断定时方法,首先定义一个全局的节拍,每次定时器中断加1,记录开机到现在的所有节拍数。因此,一种是取余置相关全局flag为0。外部函数判断这个flag是否为0
另一种是设置外部flag不断递减,只到0。外部判断是否为0.
两种方法比较后发现,取余的方法一般是30个字节,而递减到0的方法有15个字节。
其中,第一种方法,取余有22+字节,剩下8字节赋值
第二种,自增自减是4字节,剩下是判断是否为0

你可能感兴趣的:(stm8)