GPIO模拟UART串口时序
FPGA发送、接收使用的是独立的时序,并行处理易于实现。而单片机只能使用定时器来模拟时序,并通过外部下降沿中断触发启动,实时性受到限制;对于实时性要求较高的应用,需要同时处理发送和接收时(全双工)1路UART需要使用2个定时器;而半双工应用可以只使用一个定时器即可实现。基于50MHZ的M0一般9600是可以实现的,在向上估计会不稳定。主要是应用于对于特定设计临时增加低速串口通信,降低成本。
1、myiouart.h+ myiouart.c
2、资源占用:1个输入中断+2个定时器(针对实时要求高的场合)
myiouart.h: //<<< Use Configuration Wizard in Context Menu >>> #ifndef _MYIOUART_H_ #define _MYIOUART_H_ #include "LPC11xx.h" //<h> MyIouartConfig [2017.2.20] //<i> 注:双定时器模拟串口!CT32B0+CT32B1 //<o>ECHO <0=> No <1=> Yes //<i>回显测试功能: //<i>打开回显功能后,模拟串口将自动返回自己所接收到的数据,仅作测试串口功能使用! #define ENABLE_ECHO 0 //<o> COM_BAUDRATE <1200=> 1200 <9600=> 9600 <57600=> 57600 <115200=> 115200 //<i> 串口通讯波特率配置 //<i> 默认为9600! #define IOUART_BAUDRATE 9600 //</h> //-------------------------------------------------------------- #define IOUART_RECV_NULL 0 //没有接收数据 #define IOUART_RECV_OK 1 //接收数据成功返回值 #define IOUART_RECV_WRONG_ST 11 //接收数据起始位出错返回值 #define IOUART_RECV_WRONG_EN 12 //接收数据结束位出错返回值 // #define SET_UART_BAUD 48000000UL/(IOUART_BAUDRATE) //波特率设置 #define MyUart_STOPBITS 1 // //config iouart3 #define MyIOUART_TX_PORT PIO2_8 #define MyIOUART_RX_PORT PIO2_7 #define MyIOUART_TX_P LPC_GPIO2 #define MyIOUART_RX_P LPC_GPIO2 #define MyIOUART_TX_P_INDEX 8 #define MyIOUART_RX_P_INDEX 7 // #define MyIOUART_RX_IRQn EINT2_IRQn //------------------------------------------ extern char g_strtemp[32]; extern unsigned long g_rx_cnt;//For Debug. Record the count value of recieved bytes! extern unsigned long g_tx_cnt;//For Debug. Record the count value of sended bytes! // #define BUFFER_LEN 128 // char* NumToStrEx(long Number,char*PStr,unsigned char Len); //FIFO Buffer void Iouart_FifoInit(void); unsigned char GetLen_RecvedData(void); unsigned char* GetTxbuf_RecvedData(void); unsigned char ReadByte_RxFiFo(void); int WriteByte_TxFifo(unsigned char *T,unsigned char len);//非阻塞 // void put_char(char *cp);//阻塞 void iouart1_send(char *pData,unsigned char pLen);//阻塞 void print_dat(char sp[],char len);//阻塞 // void IOUART1_Init(void); void Timer2Init(void); void Timer3Init(void); //-------------------------------------------------------------- #endif // <<< end of configuration section >>> myiouart.c: #include "myiouart.h" // char g_strtemp[32]={0}; unsigned long g_rx_cnt = 0;//For Debug. Record the count value of recieved bytes! unsigned long g_tx_cnt = 0;//For Debug. Record the count value of sended bytes! //RX static volatile unsigned char l_recv_byte=0; static volatile unsigned char l_recv_st=0; static volatile unsigned char l_recv_cnt=8; //TX static volatile unsigned char l_send_byte=0; static volatile unsigned char l_send_st=0; static volatile unsigned char l_send_cnt=8; static volatile unsigned char l_send_style=0;//根据send_style判断是通过缓存发送还是直接发送 // static volatile unsigned char l_channel=0; // //l=local g=global //FIFO Buffer unsigned char l_Txbuffer[8+BUFFER_LEN]={0}; volatile unsigned char l_Rxbuffer[8+BUFFER_LEN]={0}; volatile unsigned char l_RxDataLen=0; volatile unsigned char l_RxWrongRecord[2+BUFFER_LEN]={0};//接收数据出错记录BUFFER // void Iouart_FifoInit(void) { //仅记录一个BUFFER_LEN长度的接收错误记录 l_RxWrongRecord[0]=2;//sp:2->(2+BUFFER_LEN)-1 l_RxWrongRecord[1]=(2+BUFFER_LEN)-1;//end addr // l_RxDataLen=0; //Rx //接收数据的起始地址(使用相对地址从8->BUFFER_LEN-1) l_Rxbuffer[0]=8; l_Rxbuffer[1]=0; //接收数据的结束地址 l_Rxbuffer[2]=8; l_Rxbuffer[3]=0; //Buffer的结束地址 l_Rxbuffer[4]=(BUFFER_LEN-1)+8; l_Rxbuffer[5]=0; //接收数据的长度 l_Rxbuffer[6]=0; //Buffer的长度 l_Rxbuffer[7]=BUFFER_LEN; //Tx //Tx数据的起始地址 l_Txbuffer[0]=8; l_Txbuffer[1]=0; //Tx数据的结束地址 l_Txbuffer[2]=8; l_Txbuffer[3]=0; //Buffer的结束地址 l_Txbuffer[4]=(BUFFER_LEN-1)+8; l_Txbuffer[5]=0; //Tx数据的长度 l_Txbuffer[6]=0; //Buffer的长度 l_Txbuffer[7]=BUFFER_LEN; } /* 获取Buffer中接收到的数据长度 */ unsigned char GetLen_RecvedData(void) { return l_RxDataLen; } unsigned char* GetTxbuf_RecvedData(void) { return l_Txbuffer; } /* 从模拟串口读取一个字节数据 调用该函数前先判断GetRecvDataLen()返回值,否则在没有接收到数据时读到的数据为0 */ unsigned char ReadByte_RxFiFo(void) { unsigned char Data0=0; if(l_Rxbuffer[6]) { Data0 = l_Rxbuffer[l_Rxbuffer[0]]; // if(l_Rxbuffer[0]< l_Txbuffer[4]) { l_Rxbuffer[0]++; } else { l_Rxbuffer[0]=8; } // l_Rxbuffer[6]--; } //update rx len l_RxDataLen=l_Rxbuffer[6]; return Data0; } /* 从模拟串口发送一个数据Buffer,属于一次性连续发送! 若发送缓冲区满返回-1 */ int WriteByte_TxFifo(unsigned char *T,unsigned char len) { unsigned char i = 0; if(len) while(len--){ if(l_Txbuffer[6] < l_Txbuffer[7]) { l_Txbuffer[6]++;//TxLength l_Txbuffer[l_Txbuffer[2]]=T[i++]; if(l_Txbuffer[2]<l_Txbuffer[4]) { l_Txbuffer[2]++; } else { l_Txbuffer[2]=8; } } else { return -1;//发送缓冲区满 } } //开启发送需设置5个变量 l_send_byte = l_Txbuffer[l_Txbuffer[0]]; l_send_cnt = 9; l_send_style=1; LPC_TMR32B0->TCR = 1; l_send_st=1; //----------- return 1; } /* 数字转字符串函数 */ char* NumToStrEx(long Number,char*PStr,unsigned char Len) { unsigned long NumberT=0; unsigned char Count=0; if(Number<0) { *PStr='-'; Number=-Number; Count=1; } else if(Number==0) { *PStr='0'; *(PStr+1)=0; return PStr; } NumberT=Number; while(NumberT) { NumberT/=10; Count++; } if(Len<=Count) return 0; // *(PStr+Count--)=0; // while(Number) { *(PStr+Count--)='0'+Number; Number/=10; } return PStr; } void put_char(char *cp) { //LPC_GPIO2->IE &= ~(1<<7);LPC_GPIO2->IC=(1<<7); //NVIC_DisableIRQ(EINT2_IRQn); //LPC_GPIO3->IE &= ~(1<<1);LPC_GPIO3->IC=(1<<1); //--------------------------------------------- l_send_byte = *cp; l_send_cnt = 9; l_send_style = 0; l_send_st=1; // LPC_TMR16B1->TCR = 1;//start counter LPC_TMR32B0->TCR = 1;//根据使用的定时器选择 while(l_send_st){} //-------------------------------------------- //LPC_GPIO2->IE |= (1<<7); //NVIC_EnableIRQ(EINT2_IRQn); //LPC_GPIO3->IE |= (1<<1); } /* 阻塞发送字符串,不使用缓存 */ void iouart1_send(char *pData,unsigned char pLen) { while(pLen--) { put_char(pData++); } } /* 阻塞发送数据,不使用缓存 */ void print_dat(char sp[],char len) { LPC_GPIO0->DATA |= (1 << 7);//H 485TxMode //----------------------------------------- while(len--) { put_char(sp++); } //----------------------------------------- LPC_GPIO0->DATA &= ~(1 << 7); //L 485RxMode } void IOUART1_Init(void) { Iouart_FifoInit(); // LPC_SYSCON->SYSAHBCLKCTRL |= (1ul << 6); //UART3 [channel=0] & 4852 EN--P0.7 RX--P2.7 TX--P2.8 //TX PIO2_8 LPC_IOCON->MyIOUART_TX_PORT &= ~(0x07); /*IO功能*/ MyIOUART_TX_P->DIR |= (1 << MyIOUART_TX_P_INDEX); /*Output*/ MyIOUART_TX_P->DATA |= (1 << MyIOUART_TX_P_INDEX);//H //RX PIO2_7 LPC_IOCON->MyIOUART_RX_PORT &= ~(0x07);//IO fucntion MyIOUART_RX_P->DIR &= ~(1<<MyIOUART_RX_P_INDEX);//Input MyIOUART_RX_P->IS &= ~(1<<MyIOUART_RX_P_INDEX);//edge sensitive MyIOUART_RX_P->IEV &= ~(1<<MyIOUART_RX_P_INDEX);//falling edge MyIOUART_RX_P->IBE &= ~(1<<MyIOUART_RX_P_INDEX);// // NVIC_SetPriority(MyIOUART_RX_IRQn, 0); NVIC_EnableIRQ(MyIOUART_RX_IRQn); MyIOUART_RX_P->IC=(1<<MyIOUART_RX_P_INDEX); MyIOUART_RX_P->IE |= (1<<MyIOUART_RX_P_INDEX);//enable interrupt //sel uart chanel l_channel=0; g_rx_cnt=0; g_tx_cnt=0; // Timer3Init();//CT32B1 For Rx! Timer2Init();//CT32B0 For TX! } void RecvWrongSt(void) { //记录错误,并重新开启RX引脚接收中断 LPC_GPIO3->DATA &= ~(1 << 3);//L //Record wrong case.Just For Debug. l_RxWrongRecord[l_RxWrongRecord[0]]=IOUART_RECV_WRONG_ST; if(l_RxWrongRecord[0]<l_RxWrongRecord[1]) { l_RxWrongRecord[0]++; } // MyIOUART_RX_P->IC |= (1<<MyIOUART_RX_P_INDEX); MyIOUART_RX_P->IE |= (1<<MyIOUART_RX_P_INDEX); return; } void recv_interrupt(void) //Just For Echo! { //回显测试 unsigned char len = l_RxDataLen; while(len--) { unsigned char t= ReadByte_RxFiFo(); WriteByte_TxFifo(&t,1); } } void PIOINT2_IRQHandler(void)//下降沿触发中断 { LPC_GPIO3->DATA |= (1 << 3);//H //LED // if((MyIOUART_RX_P->DATA & (1<<MyIOUART_RX_P_INDEX)))//起始位电平(L)检测 { RecvWrongSt(); return; } // if(MyIOUART_RX_P->MIS&(1<<MyIOUART_RX_P_INDEX))//Rx 判断触发中断的引脚 { // MyIOUART_RX_P->IE &= ~(1<<MyIOUART_RX_P_INDEX);//disable interrupt MyIOUART_RX_P->IC |= (1<<MyIOUART_RX_P_INDEX);//clear interrupt flag // // l_RxDataLen = 0;//接收过程禁止读取数据 l_recv_cnt = 8; l_recv_st = 1; // start recv LPC_TMR32B1->TCR = 1; LPC_TMR32B1->IR = 1; // //LPC_GPIO2->IC=(1<<7);//clear interrupt flag //LPC_GPIO2->IE |= (1<<7);//enable interrupt //NVIC_EnableIRQ(EINT2_IRQn); // } // LPC_GPIO3->DATA &= ~(1 << 3);//L } //32位定时器 CT32B0/1 【注:32位定时器和16位定时器功能一样,仅仅将16改成32,并修改一下时钟使能位(C32B0=9;C32B1=10),即可】 void Timer2Init(void)//CT32B0 { LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9); //enable ct32b1 clk LPC_TMR32B0->CTCR &= ~(3);//timer[function sel] LPC_TMR32B0->MCR = 3;//enable interrupt and reset autoself //------------------------------------------------------------- LPC_TMR32B0->PR = 0;//16bits[max=2^16=65536] 48MHZ(sysahbclk)/48=1000KHZ //IOuart要支持高的波特率,PR值要设置尽量小,然后不断调试MR0的值即可达要想要的波特率,一开始 //调试不成功就是因为PR设为了100太大,导致9600接收总是失败 //------------------------------------------------------------- //One timer can gennerate four interrupts for MR0、MR1、MR2、MR3. LPC_TMR32B0->MR0 = SET_UART_BAUD; //-------------------------------------------------------------- LPC_TMR32B0->TCR = 2;//reset LPC_TMR32B0->IR =1;//clear interrupt flag LPC_TMR32B0->TCR = 1;//load cfg of ct16b1 // LPC_TMR32B0->TCR =2 ;//reset LPC_TMR32B0->TCR =0 ;//stop counter // NVIC_EnableIRQ(TIMER_32_0_IRQn); NVIC_SetPriority(TIMER_32_0_IRQn,1); } void Timer3Init(void)//CT32B1 { LPC_SYSCON->SYSAHBCLKCTRL |= (1<<10); //enable ct32b1 clk LPC_TMR32B1->CTCR &= ~(3);//timer[function sel] LPC_TMR32B1->MCR = 3;//enable interrupt and reset autoself //------------------------------------------------------------- LPC_TMR32B1->PR = 0;//10;//16bits[max=2^16=65536] 48MHZ(sysahbclk)/48=1000KHZ //IOuart要支持高的波特率,PR值要设置尽量小,然后不断调试MR0的值即可达要想要的波特率,一开始 //调试不成功就是因为PR设为了100太大,导致9600接收总是失败 //------------------------------------------------------------- //One timer can gennerate four interrupts for MR0、MR1、MR2、MR3. LPC_TMR32B1->MR0 = SET_UART_BAUD; //-------------------------------------------------------------- LPC_TMR32B1->TCR = 2;//reset LPC_TMR32B1->IR =1;//clear interrupt flag LPC_TMR32B1->TCR = 1;//load cfg of ct16b1 // LPC_TMR32B1->TCR =2 ;//reset LPC_TMR32B1->TCR =0 ;//stop counter // NVIC_EnableIRQ(TIMER_32_1_IRQn); NVIC_SetPriority(TIMER_32_1_IRQn,1); } // void TIMER32_0_IRQHandler (void)//For send destination. { static unsigned char tx_k=0; // unsigned char tx_s2; // LPC_GPIO0->DATA &= ~(1 << 3);//L LED LPC_TMR32B0->IR =1;//clear interrupt flag //TX if(l_send_st){ if(l_send_cnt) { if(l_send_cnt == 9)//start bit { if(l_channel==0) { MyIOUART_TX_P->DATA &= ~(1 << MyIOUART_TX_P_INDEX);//L } else if(l_channel==1) { } else if(l_channel==2) { } l_send_cnt=8; } else { switch(l_send_cnt) { case 8: tx_s2=0x01; break; case 7: tx_s2=0x02; break; case 6: tx_s2=0x04; break; case 5: tx_s2=0x08; break; case 4: tx_s2=0x10; break; case 3: tx_s2=0x20; break; case 2: tx_s2=0x40; break; case 1: tx_s2=0x80;tx_k=0; break; default: tx_s2=0x0; break; } //------------------------------------------------------------- if(l_channel==0) {//TX if(!(l_send_byte&tx_s2)) MyIOUART_TX_P->DATA &= ~(1 << MyIOUART_TX_P_INDEX);//L else MyIOUART_TX_P->DATA |= (1 << MyIOUART_TX_P_INDEX);//H } else if(l_channel==1) { } else if(l_channel==2) { } // l_send_cnt--; } } else { if(tx_k<=(MyUart_STOPBITS-1)) //1->两位停止位 0->1位停止位 { if(l_channel==0) { MyIOUART_TX_P->DATA |= (1 << MyIOUART_TX_P_INDEX);//H } else if(l_channel==1) { } else if(l_channel==2) { } // tx_k++; } else { tx_k=0;l_send_cnt = 9; if(l_send_style)//如果是FIFO发送 { l_send_style=0; //一个字节发送完 l_Txbuffer[6]--; if(l_Txbuffer[0]<l_Txbuffer[4]) l_Txbuffer[0]++; else l_Txbuffer[0]=8; //判断是否继续发送 if(l_Txbuffer[6]) {//连续发送 //restart timer l_send_byte = l_Txbuffer[l_Txbuffer[0]]; l_send_cnt = 9; l_send_st = 1; l_send_style = 1; } else { l_send_style = 0; //修改点1:此处导致出现最后一个字节发送两次!需添加: l_send_st=0; //reset //reset timer LPC_TMR32B0->TCR =2;//[0-stop counter 1-start 2-reset] LPC_TMR32B0->IR = 1; } } else { l_send_st=0; //reset //reset timer LPC_TMR32B0->TCR =2;//[0-stop counter 1-start 2-reset] LPC_TMR32B0->IR = 1; } } } } //-------------------------------- LPC_GPIO0->DATA |= (1 << 3);//LED } // void TIMER32_1_IRQHandler (void)//For Receive destination. { static unsigned char rx_k=0; static char stc_recv_check=0;//静态变量 static char rx_interrupt_flag=0; // unsigned char rx_s1; // LPC_GPIO0->DATA &= ~(1 << 3);//L LED LPC_TMR32B1->IR =1;//clear interrupt flag /// //RX if(l_recv_st) { if(l_recv_cnt) { switch(l_recv_cnt) { case 8: rx_s1=0;l_recv_byte=0; break; case 7: rx_s1=1; break; case 6: rx_s1=2; break; case 5: rx_s1=3; break; case 4: rx_s1=4; break; case 3: rx_s1=5; break; case 2: rx_s1=6; break; case 1: rx_s1=7;rx_k=0;stc_recv_check=0; break; default: rx_s1=0; break; } //------------------------------------------------------------- if(l_channel==0) {//Rx if(MyIOUART_RX_P->DATA & (1<<MyIOUART_RX_P_INDEX)) l_recv_byte+=(1<<rx_s1); } else if(l_channel==1) { } else if(l_channel==2) { } // l_recv_cnt--; } else { if(rx_k<=(MyUart_STOPBITS-1)) //1->两位停止位 0->1位停止位 { if(l_channel==0) {//Rx if(MyIOUART_RX_P->DATA & (1<<MyIOUART_RX_P_INDEX))//检测停止位(H)判断 { //stc_recv_check = IOUART_RECV_OK; stc_recv_check += 2; stc_recv_check += rx_k; } else { stc_recv_check +=3; //stc_recv_check = IOUART_RECV_WRONG_EN; } } else if(l_channel==1) { } else if(l_channel==2) { } // rx_k++; } else { rx_k=0;l_recv_cnt = 8; if(stc_recv_check <= 5) { stc_recv_check = IOUART_RECV_OK; } else { stc_recv_check = IOUART_RECV_WRONG_EN; } switch(stc_recv_check) { case IOUART_RECV_OK://成功接收一个字节 g_rx_cnt++; if(l_Rxbuffer[6] < l_Rxbuffer[7])//RxLength { l_Rxbuffer[6]++; l_Rxbuffer[l_Rxbuffer[2]]=l_recv_byte; if(l_Rxbuffer[2] < l_Rxbuffer[4]) { l_Rxbuffer[2]++; } else { l_Rxbuffer[2]=8; } //连续接收时要精确控制停止位 if(l_channel==0) { if(MyIOUART_RX_P->DATA & (1<<MyIOUART_RX_P_INDEX)) { l_recv_st=0; rx_interrupt_flag = 1;//模拟串口的接收中断 } else//检测到下一字节的起始位 {//连续接收 //连续收暂时不改变接收缓冲区的数据长度.//l_RxDataLen = 0;//注:连续接收时读取RX缓存区必须使其返回为0,否则会影响数据的接收! //l_recv_byte = 0;//必须清零,因为接收的结果是通过叠加的方式获得的 l_recv_cnt = 8; l_recv_st = 1; } } else if(l_channel==1) { } else if(l_channel==2) { } } else//接收缓存区满 { l_recv_st=0; } break; case IOUART_RECV_WRONG_EN: //Record wrong case.Just For Debug. l_RxWrongRecord[l_RxWrongRecord[0]]=IOUART_RECV_WRONG_EN; if(l_RxWrongRecord[0]<l_RxWrongRecord[1]) { l_RxWrongRecord[0]++; } // l_RxWrongRecord[l_RxWrongRecord[0]]=l_recv_byte; if(l_RxWrongRecord[0]<l_RxWrongRecord[1]) { l_RxWrongRecord[0]++; } // l_recv_st=0; break; default: l_recv_st=0; break; } // if(l_recv_st==0) { //开RX引脚中断 MyIOUART_RX_P->IC |= (1<<MyIOUART_RX_P_INDEX); //clear flag MyIOUART_RX_P->IE |= (1<<MyIOUART_RX_P_INDEX); //enable interrupt //update len l_RxDataLen = l_Rxbuffer[6]; // //reset timer LPC_TMR32B1->TCR = 2;//[0-stop counter 1-start 2-reset] LPC_TMR32B1->IR = 1; } } } } //-------------------------------- LPC_GPIO0->DATA |= (1 << 3);//LED // #if ENABLE_ECHO if(rx_interrupt_flag) { rx_interrupt_flag = 0;// recv_interrupt();//此函數必須立即返回!(用于回显测试) } #endif } /* */ /*USE EXAMPLE: */1、DrvIOUART.h+DrvIOUART.c+DrvIOUART1.h+DrvIOUART1.c;蓝色文件为库文件,绿色文件为实例化参考
2、资源占用:1个输入中断+1个定时器(针对实时要求不高的场合)
文件详细见下载,末尾。
在软件中,对.h文件的第一行添加"//<<< Use Configuration Wizard in Context Menu >>>",可使用其自带的配置功能,方便参数灵活设置。
2016/10/20
1、波特率可调节:支持收发波特率1200->57600
[注: 按r字符进行回显测试,9600才能稳定不出错]
19200(停止位为1)时:(单按时回显正常,按住不放回显会出错)
停止位为2时,情况一样。
结论:ZLG的模拟串口可以支持到57600,但连续接收数据过快时,baud>9600易出错。
两路模拟串口使用注意事项:
1、两个模拟串口分布在不同PORT LINE,即一个用PORT2,一个用PORT3,不能用同一个PORT。
2、各自使用自己的定时器。
3、每一个函数都要能立即退出,不要使其出现卡死现象。将每个函数想象成在一进就出、各不干扰、独立运行的状态执行。
4、发送过程中产生接收,则以接收的优先级为高。
增加发送自动退让,发送过程随时可能被接收中断,这样会导致发送的数据出现错误但接收的数据都是正常的。BUG.......................
周立功串口写的很好在只使用一个定时器的情况下,做到了无一错发,无一漏收,可连续收发,波特率稳定可调,这才是正真的高手!!!
增加接收数据计数,发送数据计数!
增加当接收过程终止发送时拉高TX引脚操作!
用串口调试助手测线序数据连续发送且能正常回显的最小时间,目前是300ms
测试串:12 34 56 78 90 1A 3E 5B 12 34 56 78 90 1A 3E 5B
5、对于连续接收情况的处理
根据精确的停止位的后一位电平高低判断是否为连续接收的情况
使用停止位判断,只能选择一位(k=0)或两位停止位(K=1)
没有校验位
要求连续收发10个字节以上,不行的话调节BAUD系数
6、一旦错过了起始位是否会一直出错!
myiouart:
特点
1、支持一个定时器多通道引脚分时复用,通过channel选择。
配置一个串口支持多个引脚发送,或接收通过channel变量选择哪一对引脚通道。
使用回显测试:
在secureCRT中: 按住两个按键不放看是否能正常显示,或同时按住多个看是否有乱码现象。当同时按住两个字符按键不放时,正常程序每次返回两个按下的字符,此时串口工作在连发状态下。
在串口调试助手中:连续发送多个字节看返回值是否正确。
V1.0 -- 只支持发送,接收波特率一高就有问题,4800接收
V1.1 -- 修改了收发(只是单字节收发),跟换了32位定时器1,并将驱动单独列在一个文件中。
V1.2 -- 增加了BUFFER功能
V1.3 -- 增加了连续收发,但发送会被接收终止,导致发送的数据错误
V1.4 --
V1.5 -- 改为双定器模拟串口
V1.6 -- 单定时器串口升级至V1.6使用了结构体操作方式
IO 模拟 UART 实现-ZLG
链接:https://pan.baidu.com/s/1OgQshNoEe5oI0_g5cQPXHg 密码:0svc
V1.5
链接:https://pan.baidu.com/s/11pwEpICOpuX6S5OYy6xUtg 密码:f5l9
V1.6
链接:https://pan.baidu.com/s/1UimbCwUY3uINvajBQ5d8HQ 密码:m044
