stm32f103c8与stc8a8k64d4比拟紧张资源比拟
项
STM32F103V8

STC8A8K64D4
GPIO
37pin
43pin
主频
72M
45M
flash
64K
64K
RAM
20K
8K
UART
3
4
ADC
10 ch,10位
15ch, 12位
定时器
1tick + 4个32位
4个
乘除法指令
单周期硬件乘除法
无
价格
64元
4.5元
最主要的是: STC有现货
针对stc8a8k64d4的硬件资源,由于其没有硬件乘除法,导致其在须要进行大量数据打算的运用处景上效率低下,对付一样平常的运用处景,stc在硬件资源上完备够用,在现有的供应链条件下,STC是一个非常不错的选择。
俗话说有利就有弊,STC带来了不少的硬件本钱上风,但是其在编程上却带来了一些不适应,我们在代码编写时,须要特殊把稳。
由于stc系列单片机是基于51架构,虽然它在内核上进行了加强,但是由于时期的局限性,51架构和当代的MCU架构比较,存在不少的差距。由于当代编译器不断的演进,我们大部人都是利用C措辞进行编程,这在一定程度上,帮助我们屏蔽了架构层面的巨大差异。而且由于架构的掉队,再加宏晶公司与ST公司本身在IP上面的技能差距,这导致我们在利用STC51的外设时,也有很大的差异。下图是51的架构和stm32的架构,可以看出明显的差距。
编程模型的差异 -- 函数重入
对付C措辞的编程模型(请参看我前面的文章《C措辞代码组成 - BSS、Data、Stack、Heap、Code、Const》),51和ARM在处理stack上,有巨大的差异。51加架构中,sp指针指向的区域是pdata区域,这个区域最大情形只有256个字节,作为栈来说,是不可能用来分配局部变量的。因此keil编译器对付临时变量,采取了一种叫作变量覆盖的技能,即多个函数利用同一区域作作为临时变量区,下一个函数调用运行时,会将上一个函数运行的临时变量覆盖掉。这样就办理了栈空间不敷的问题。而且由于没有进出栈的操作,也提高了代码效率。但这带来的最大的问题便是函数重入问题,即51代码全部是不可重入的,如果一个函数在中断中被调用,那就意味着这个函数必须是可重入的,才能正常运行,而且这个中断函数所调用的下级函数也必须是可重入的。如何办理重入问题:
keil通过仿照堆栈技能,可以实现函数的重入,通过增加关键字 reentrant 将一个函数定义为可重入函数;将须要重入的函数定义为原子操作,即进入该函数时,禁止全局中断,实行完成后,开启全局中断;中断函数中不调用其它函数,只是置标志,进行变量操作,然后再在main的主循环中查询标志,再实行相应的操作;以上三种方法,各有缺点,方法一大略粗爆,须要花费更多的资源;方法二会导致中断相应不及时;方法三导致中断相应不及时。以是用户须要根据项目特点,利用得当的策略;
函数传参
C51通过寄存器通报参数,最多只能通报三个参数
对付三个或者三个以内的参数,利用寄存器传参;对付多于三个的参数,可以定义为xdata变量进行传参;将函数定义为可重入函数利用栈进行传参数;乘除法
由于51没有硬件乘除法,而且是8位机,对付除法指令,十分耗时,这会导致程序相应慢。对付除法指令,只管即便利用移位操作来代替。
调试问题
STC51是没有像arm那样的jtag调试接口,下载程序和调试是通过串口进行的,但是在利用串口进行单步调试时,非常不稳定,常常通讯不上,导致调试失落败。在实际项目中,我是利用uart打印log来进行调试。由于打印的字符串是定义在常量区,过多的打印会导致代码量增大,以是要合理的利用打印。
串口驱动优化
由于STC51的串口只有发送完成中断和接管完成中断,而没有接管空闲中断,这就须要我们利用分外的处理办法,详细方法可以拜会我前面的文章《串口吸收中的帧同步》。代码如下
//收发数据的数据构造typedef struct{ u8 id; //串口号 u8 TX_read; //发送读指针 u8 TX_write; //发送写指针 u8 B_TX_busy; //忙标志 u8 RX_Cnt; //吸收字节计数 u8 RX_TimeOut; //吸收超时 u8 B_RX_OK; //吸收块完成} COMx_Define; u8 xdata TX1_Buffer[COM_TX1_Lenth]; //发送缓冲u8 xdata RX1_Buffer[COM_RX1_Lenth]; //吸收缓冲//定时器中断//用来判断帧超时, 在1ms中断中调用void Serial_HookMs(void){ if(COM1.RX_TimeOut > 0){ COM1.RX_TimeOut --; } else { //if(COM1.RX_Cnt > 0){ //COM1.B_RX_OK = 1; //} }}//中断函数,发送中断和接管中断void UART1_ISR_Handler (void) interrupt UART1_VECTOR{ if(RI){ RI = 0; if(COM1.RX_Cnt >= COM_RX1_Lenth) COM1.RX_Cnt = 0; RX1_Buffer[COM1.RX_Cnt++] = SBUF; COM1.RX_TimeOut = TimeOutSet1; } if(TI){ TI = 0; if(COM1.TX_read != COM1.TX_write){ SBUF = TX1_Buffer[COM1.TX_read]; if(++COM1.TX_read >= COM_TX1_Lenth) COM1.TX_read = 0; } else COM1.B_TX_busy = 0; }}int Serial_Read(UART_Port_t port, uint8_t pmsg, int msg_size){ COMx_Define com = NULL; int len = 0; uint8_t rbuff = NULL; switch(port){ case UART1: com = &COM1; rbuff = RX1_Buffer; break; default: return -1; } if((com->RX_TimeOut == 0) && (com->RX_Cnt > 0)){ if(msg_size < com->RX_Cnt){ return -2; } len = com->RX_Cnt; memcpy(pmsg, rbuff, len); com->RX_Cnt = 0; return len; } return -3;}//采取中断发送int Serial_Write(UART_Port_t port, uint8_t pmsg, int len){ COMx_Define com = NULL; uint8_t sbuff = NULL; int i; int s_buff_max = 0; void ( uart_send)(u8 dat); switch(port){ case UART1: com = &COM1; sbuff = TX1_Buffer; uart_send = TX1_write2buff; s_buff_max = COM_TX1_Lenth; break; default: return -1; } if(com->B_TX_busy != 0){ //发送中 return -2; } //剩余空间不足 if((s_buff_max - (com->TX_write + s_buff_max - com->TX_read) % s_buff_max) < len){ return -3; } //将数据考贝到缓冲区中; for(i = 1; i < len; i ++){ sbuff[com->TX_write] = pmsg[i]; com->TX_write ++; com->TX_write %= s_buff_max; } uart_send(pmsg[0]); return len;}
利用串口的收发中断以避免壅塞式的收发,用定时器实现帧间隔判断来判断是否是完全一帧,这样极大的增加了程序的效率。
IIC驱动库的坑
在项目中,利用STC8A的IIC操作外设时,新焊接的板子,会涌现不断复位重启的征象,通过加打印定位程序是卡去世在iic操作中,通过剖析程序,在iic的驱动中存在等应答的操作,这是一个去世循环,如下:
void Wait(void){ while (!(I2CMSST & 0x40)); I2CMSST &= ~0x40;}
由于新焊接的板子,存在虚焊的问题,导到IIC应答失落败,这就使IIC永久 收不到应答,从而去世在wait中,看门狗无法喂狗,导致复位。将驱动改为如下:
void Wait(void){ u32 i = 0; while (!(I2CMSST & 0x40)){ i ++; if(i > 250){ break; } } I2CMSST &= ~0x40;}
通过增加超机遇制,来避免永久无法应答的问题。
ADC的坑
在项目中,利用ADC采集变送器的电压旗子暗记,当ADC采集到的值大于某个值时,输出开关旗子暗记,实际中,ADC采集是在主循环中做的,主循环约为1ms,当溘然接入电压旗子暗记时,差不多要3~4ms,才会输出开关旗子暗记,存在很大的时延。按理来说,时延该当在一个循环周期以内,即1ms以内才是正常的。而通过通讯口读取出来的稳定值是精确的。末了通过打印ADC转换值,才创造ADC的值是有一个较缓的上升过程,这是由于ADC的输入电阻和采样电容较大导致的,详细的请参看前面的文章《AD转换中的采样电容和输入电阻》。纵然通过调度参数,增加采样韶光,依然无法办理此问题,末了办理的办法是连续多次转换(多次测试后为4次),取末了一次的结果作为效值,从而办理此问题。
数据类型
由于C51是8位单片机,其数据类型长度与ARM比较还是很大差异的,比如int在keil c51中,默认是 16位的。如果在调用外部第三方库时,稍不把稳,很随意马虎涌现数据溢出。详细的可以参看stc8a库中的type_def.h文件。
由于STC51的外设功能相对大略,而且STC的示例完成度很高,利用起来还是很方便,从32位机转到51,在前期会有一个磨合的过程,由于编译器的缺点提示功能的强大,再加上网上资源的丰富,一样平常的问题,基本都能很快办理。总的来说,对付一些适宜的运用处景,STC的51单片机是个不错的选择。