【串口收发框架】【printf重定向】【圆周率计算】

📅 2026/8/10 22:15:02
【串口收发框架】【printf重定向】【圆周率计算】
本开源程序实现如题目所示的三个功能1、串口收发框架使用硬件UART中断方式实现串口收发中断方式相对查询方式和GPIO软件模拟的优点在此就不多赘述了使用定时器实现串口接收超时复位避免接收到错误数据或数据未发完就终止时错误的状态始终保留导致再发送正确的数据正确的数据又和之前错误的数据拼接成一个错误的数据收发缓存为全局变量使用指针在中断函数中实现自动处理不需要在主程序中轮询某些状态和标志中断程序简单高效全状态机控制不会耽误大量时间不使用软件延时。2、printf重定向使用标准库stdio.h、stdarg.h实现变量格式化printf重定向到UART输出然后就可以愉快的使用printf了缺点是和直接写数组相比会消耗更多code空间。3、圆周率计算使用莱布尼茨法计算圆周率用于printf输出变量由于莱布尼茨法是收敛的无穷级数且偶数项为正奇数项为负程序中保留上一次迭代结果并和本次迭代结果求平均值以加速收敛实测454次迭代结果为3.141592如不求相邻两次平均值则需上万次迭代且由于浮点数精度的限制几万次迭代后误差会越来越大。单片机STC8G1K08A-8PIN时钟频率11.0592MHzUART1 9600bps 8N1为便于观察主程序中延迟50ms串口默认为阻塞模式即上一个数据包未发完又发新的数据包时会等待串口状态复位即发送长度为0Uart_Send_Lenth0该变量为全局变量数据包发送完毕后在中断程序中清零表示数据包发送完毕该模式在软件发送频率大于硬件发送频率时软件发送频率会被拖慢但不会发送错误的数据注释掉while(Uart_Send_Lenth)即可改为覆盖模式该模式上一个数据包未发完又发新的数据包时新的数据包会覆盖掉原数据包且串口状态被重置发送的数据包会被打断但软件发送不会被拖慢如在阻塞模式下没有达到预期速度或在覆盖模式下发送数据错误请降低发送频率或提高波特率该串口收发框架100%。如不想使用printf函数直接写发送缓存以下为范例程序T_Buffer[0]T;T_Buffer[1];T_Buffer[2]Hex_to_Ascii[t%10000/1000];T_Buffer[3]Hex_to_Ascii[t%1000/100];T_Buffer[4].;T_Buffer[5]Hex_to_Ascii[t%100/10];T_Buffer[6]Hex_to_Ascii[t%10];T_Buffer[7]C;T_Buffer[8] ;T_Buffer[9]P;T_Buffer[10];T_Buffer[11]Hex_to_Ascii[p%1000000/100000];T_Buffer[12]Hex_to_Ascii[p%100000/10000];T_Buffer[13]Hex_to_Ascii[p%10000/1000];T_Buffer[14].;T_Buffer[15]Hex_to_Ascii[p%1000/100];T_Buffer[16]Hex_to_Ascii[p%100/10];T_Buffer[17]Hex_to_Ascii[p%10];T_Buffer[18]K;T_Buffer[19]P;T_Buffer[20]a;T_Buffer[21]0x0d;T_Buffer[22]0x0a;UART_Send(23);对发送缓存数组赋值后调用UART_Send(x)即可x为发送长度。因没有使用串口接收功能串口接收程序是个半成品需用户按照具体协议编写解析程序以下为范例程序void Uart_Isr(void) interrupt 4{static unsigned int tp;unsigned int temp,lenth,i;if(RI){RI0;Uart_Start();R_Buffer[RP]SBUF;if(RP1){temp(R_Buffer[0]8)R_Buffer[1];if(temp0x0200){R_Buffer[1]0x07;switch(R_Buffer[0]){case R:Uart_Send_Iap(R_Buffer[1]);Sector0xFF;break;case W:SectorR_Buffer[1];Iap_Erase_Sector(Sector*0x0200);Uart_Printf(扇区%bd已擦除\xFD请切换到HEX模式写入数\xFD据\r\n,Sector);break;}Uart_Stop();}else if(temp2temp512){lenthtemp;}}if(RPlenth-1lenth2Sector8){for(i0;ilenth;i){Iap_Program_Byte(Sector*0x0200i,R_Buffer);Uart_Start();}Uart_Stop();Uart_Printf(扇区%bd写入%d字节\r\n,Sector,lenth);Sector0xFF;}else if(RPR_Buffer_Len-1){Uart_Stop();}else if(TR0){RP;}}if(TI){TI0;if(Uart_Send_Lenth!0){SBUF(T_Buffer[TP]);TP;}if(TPUart_Send_Lenth){TP0;Uart_Send_Lenth0;}}}这是一个接收不固定长度数据包的程序前两个字节为长度最大长度512字节如您也需要接收这么长的数据包则需要修改接收缓存长度R_Buffer_Len大于数据包长度注意接收到除了包尾的每个字节后都要调用Uart_Start()函数以重置超时定时器在接收到期望的数据包正确数据包或错误数据包后调用Uart_Stop()函数立即重置状态即可如不立即重置状态在超时后也会自动重置状态但在此期间又收到数据会发生数据接收错误。该例程包含汉字0xFD问题的解决方法供参考。以下是完整程序/*----------------------------分割线----------------------------*/#include STC8G.H#include define.h#include intrins.h#include string.h#include stdio.h#include stdarg.h#define RXD P30#define TXD P31#define FOSC 11059200UL#define BAUD 9600UL#define BRT (0x10000-FOSC/BAUD/4)#define R_Buffer_Len 64 //Uart1接收缓存长度#define T_Buffer_Len 64 //Uart1发送缓存长度unsigned char RP; //Uart1接收指针unsigned char TP; //Uart1发送指针unsigned char Uart_Send_Lenth; //Uart1发送长度unsigned char xdata R_Buffer[R_Buffer_Len]; //Uart1接收缓存unsigned char xdata T_Buffer[T_Buffer_Len]; //Uart1发送缓存///*----------------------------延时10usSTC-Y611.0592MHz----------------------------*///void Delay_10us(void)//{// unsigned char i;// i35;// while(--i);//}///*----------------------------延时x10us----------------------------*///void Delay_x10us(unsigned char x)//{// while(x--)// Delay_10us();//}/*----------------------------延时10msSTC-Y611.0592MHz----------------------------*/void Delay_10ms(void){unsigned char i,j;_nop_();_nop_();i144;j157;do{while(--j);}while(--i);}/*----------------------------延时x10ms----------------------------*/void Delay_x10ms(unsigned char x){while(x--)Delay_10ms();}void UART_Send(unsigned int x){TP0;Uart_Send_Lenthx;TI1;}void Uart_Printf(unsigned char *v,...){va_list ap;va_start(ap,v);while(Uart_Send_Lenth);UART_Send(vsprintf(T_Buffer,v,ap));va_end(ap);}void Init(void){P_SW2|EAXFR;P3M00x00;P3M10x00;AUXR0x40; //设置定时器0时钟为12T模式设置定时器1为1T模式设置定时器1为波特率发生器TMOD0x01; //设置定时器0为16位不自动重装载模式设置定时器1为16位自动重装载模式TL00x00; //设置定时器0初始值5msTH00xEE; //设置定时器0初始值5msTF00; //清除TF0中断标志位ET01; //启用定时器0中断SCON0x50; //设置UART1模式为8位数据可变波特率TL1BRT; //设置UART1波特率TH1BRT8; //设置UART1波特率TR11; //打开定时器1ES1; //启用UART1中断EA1; //启用总中断}void main(void){unsigned int x;float pi,pi_last;Init();x0;pi0.0F;while(1){x%2?(pi-4.0F/((float)x*2.0F1.0F)):(pi4.0F/((float)x*2.0F1.0F));Uart_Printf(x%U pi%1.6F\r\n,x,(pipi_last)/2);pi_lastpi;if(x454)while(1);x;Delay_x10ms(5);}}void Uart_Start(void){TL00x00;TH00xEE;TR01;}void Uart_Stop(void){TR00;TL00x00;TH00xEE;RP0;memset(R_Buffer,0x00,sizeof R_Buffer);}void Timer0_Isr(void) interrupt 1{Uart_Stop();}void Uart_Isr(void) interrupt 4{if(RI){RI0;Uart_Start();R_Buffer[RP]SBUF;if(RPR_Buffer_Len-1){Uart_Stop();}else if(TR0){RP;}}if(TI){TI0;if(Uart_Send_Lenth!0){SBUF(T_Buffer[TP]);TP;}if(TPUart_Send_Lenth){TP0;Uart_Send_Lenth0;}}}硬件选项的时钟频率11.0592M注意下载端口一定要选CDC不要选HID否则不能正确发送下载命令用户命令按图设置功能描述打印到控制台串口输出参数说明*format格式控制。支持以下格式%ds16数据%uu16数据%lds32数据%luu32数据%s字符串%cASCII字符%Xu16数据的16进制字符串%lXu32数据的16进制字符串%[0][1-5]u以n的宽度显示u16数据不足时可前置添0超过时宽度限制无效%[0][1-10]lu以n的宽度显示u32数据不足时可前置添0超过时宽度限制无效%.[0-4]d对s16数据以小数形式呈现%.[0-9]ld:对s32数据以小数形式呈现%.[0-4]u对u16数据以小数形式呈现%.[0-9]lu对u32数据以小数形式呈现%.nd参数1为保留小数位数参数为2为s16数据%.nld参数1为保留小数位数参数为2为s32数据%.nu参数1为保留小数位数参数为2为u16数据%.nlu参数1为保留小数位数参数为2为u32数据%t参数1为字符串指针。参数2为限制长度可以是参数1的空间大小-1。将此字符之前的格式化的字符复制到第1个字符串指针位置%hs:参数1为u8指针。参数2为限制长度。将此数据转为十六进制字符串。%hn:参数1为u8指针。参数2为限制长度。直接拷贝源数据。