-
2021-12-16 17:14:09
STM32的IO模拟串口
一、为什么要用STM32的普通IO口模拟串口?
随着工程的增大,开发者们可能在一开始不能完全利用好STM32的资源,造成串口资源不足。例如:STM32F103C8T6只有3个串口,在不改动硬件资源的情况下,因此只能想到用IO口模拟串口的时序。
二、参考程序
代码如下(示例):
IO2USART.c
#include "IO2USART.h" /** *软件串口的实现(IO模拟串口) * 波特率:9600 1-8-N * TXD : PB4 * RXD : PB5 * 使用外部中断对RXD的下降沿进行触发,使用定时器4按照9600波特率进行定时数据接收。 * Demo功能: 接收11个数据,然后把接收到的数据发送出去 */ #define BuadRate_9600 100 u8 len = 0; //接收计数 u8 USART_buf[11]; //接收缓冲区 u8 recvStat = COM_STOP_BIT; u8 recvData = 0; void IO_TXD(u8 Data) { u8 i = 0; OI_TXD = 0; delay_us(BuadRate_9600); for(i = 0; i < 8; i++) { if(Data&0x01) OI_TXD = 1; else OI_TXD = 0; delay_us(BuadRate_9600); Data = Data>>1; } OI_TXD = 1; delay_us(BuadRate_9600); } void USART_Send(u8 *buf, u8 len) { u8 t; for(t = 0; t < len; t++) { IO_TXD(buf[t]); } } void IOConfig(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PC端口时钟 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //SoftWare Serial TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_4); //SoftWare Serial RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource5); EXTI_InitStruct.EXTI_Line = EXTI_Line5; EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发中断 EXTI_InitStruct.EXTI_LineCmd=ENABLE; EXTI_Init(&EXTI_InitStruct); NVIC_InitStructure.NVIC_IRQChannel= EXTI9_5_IRQn ; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority =2; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM4_Int_Init(u16 arr,u16 psc) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //时钟使能 //定时器TIM4初始化 TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位 TIM_ClearITPendingBit(TIM4, TIM_FLAG_Update); TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断 //中断优先级NVIC设置 NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //TIM4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级1级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级1级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器 } void EXTI9_5_IRQHandler(void) { if(EXTI_GetFlagStatus(EXTI_Line5) != RESET) { if(OI_RXD == 0) { if(recvStat == COM_STOP_BIT) { recvStat = COM_START_BIT; TIM_Cmd(TIM4, ENABLE); } } EXTI_ClearITPendingBit(EXTI_Line5); } }
总结
提示:亲测稳定运行,需要完整工程联系QQ:2858054751
更多相关内容 -
单片机串口时序与TTL电平
2022-01-22 15:33:01串口是一个广义的概念,这是单讲单片机的串口UART,以及单片机的TTL电平,主要是记录一下自己忘了还能再看一下。 1、TTL电平标准 输出 L: <0.8V ; H:>2.4V。 输入 L: <1.2V ; H:>2.0V TTL器件...串口是一个广义的概念,这是单讲单片机的串口UART,以及单片机的TTL电平,主要是记录一下自己忘了还能再看一下。
1、TTL电平标准
输出 L: <0.8V ; H:>2.4V。
输入 L: <1.2V ; H:>2.0V
TTL器件输出低电平要小于0.8V,高电平要大于2.4V。输入,低于1.2V就认为是0,高于2.0就认为是1。于是TTL电平的输入低电平的噪声容限就只有(0.8-0)/2=0.4V,高电平的噪声容限为(5-2.4)/2=1.3V。
2、UART通信时序
引用一张网图,单说上面的ttl电平,发送一个字节数据,需要1个起始位,8个数据位以及1个停止位,共发送10bit,起始位是低电平,停止位用高电平。假设波特率是37600,那么发送1bit的时间就是1000000/37600约等于26us,高电平保持26us就是1,低电平保持26us就是0,通常低位先发。比如a3h,则是b1100(3) 0101(a)。
-
GPIO模拟UART串口时序
2018-05-03 00:23:40GPIO模拟UART串口时序模拟时序:平台:Cortex-M0与FPGA UART的区别: FPGA发送、接收使用的是独立的时序,并行处理易于实现。而单片机只能使用定时器来模拟时序,并通过外部下降沿中断触发启动,实时性受到限制;...GPIO模拟UART串口时序
模拟时序:
平台:Cortex-M0
与FPGA UART的区别:
FPGA发送、接收使用的是独立的时序,并行处理易于实现。而单片机只能使用定时器来模拟时序,并通过外部下降沿中断触发启动,实时性受到限制;对于实时性要求较高的应用,需要同时处理发送和接收时(全双工)1路UART需要使用2个定时器;而半双工应用可以只使用一个定时器即可实现。基于50MHZ的M0一般9600是可以实现的,在向上估计会不稳定。主要是应用于对于特定设计临时增加低速串口通信,降低成本。
示例代码:
V1.5:双定时器方案
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%10; 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: */
V1.6:单定时器方案
1、DrvIOUART.h+DrvIOUART.c+DrvIOUART1.h+DrvIOUART1.c;蓝色文件为库文件,绿色文件为实例化参考
2、资源占用:1个输入中断+1个定时器(针对实时要求不高的场合)
文件详细见下载,末尾。
使用小技巧:
在
软件中,对.h文件的第一行添加"//<<< Use Configuration Wizard in Context Menu >>>",可使用其自带的配置功能,方便参数灵活设置。
附录:
1、IOUART模拟串口调试记录
2016/10/20
1、波特率可调节:支持收发波特率1200->57600
[注: 按r字符进行回显测试,9600才能稳定不出错]
19200(停止位为1)时:(单按时回显正常,按住不放回显会出错)
停止位为2时,情况一样。
结论:ZLG的模拟串口可以支持到57600,但连续接收数据过快时,baud>9600易出错。
2、两路模拟串口实现
两路模拟串口使用注意事项:
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变量选择哪一对引脚通道。3、测试模拟串口性能方法:
使用回显测试:
在secureCRT中: 按住两个按键不放看是否能正常显示,或同时按住多个看是否有乱码现象。当同时按住两个字符按键不放时,正常程序每次返回两个按下的字符,此时串口工作在连发状态下。
在串口调试助手中:连续发送多个字节看返回值是否正确。
4、Myiouart_Lpc11c14_V1程序DEBUG记录:
V1.0 -- 只支持发送,接收波特率一高就有问题,4800接收
V1.1 -- 修改了收发(只是单字节收发),跟换了32位定时器1,并将驱动单独列在一个文件中。
V1.2 -- 增加了BUFFER功能
V1.3 -- 增加了连续收发,但发送会被接收终止,导致发送的数据错误
V1.4 --
V1.5 -- 改为双定器模拟串口
V1.6 -- 单定时器串口升级至V1.6使用了结构体操作方式
参考:
链接:https://pan.baidu.com/s/1OgQshNoEe5oI0_g5cQPXHg 密码:0svc
下载:
链接:https://pan.baidu.com/s/11pwEpICOpuX6S5OYy6xUtg 密码:f5l9
链接:https://pan.baidu.com/s/1UimbCwUY3uINvajBQ5d8HQ 密码:m044
-
串口UART 时序 和数据传输
2019-07-31 23:06:03UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。 UART通信协议 UART作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输。其中每一位(Bit)的意义...
UART(Universal Asynchronous Receiver / Transmitter:通用异步收发传输器)是一种通用串行数据总线,用于异步通信。
该总线双向通信,可以实现全双工传输和接收。
1、UART 通信协议
UART作为异步串口 通信协议的一种,工作原理是将传输数据的每个字符1 bit 接 1 bit 地传输。
其中每一位(it)的意义如下:序号 bit 位 描述 1 起始位
先发出一个 逻辑"0"
的信号,表示传输字符的开始
2 数据位
紧跟在 起始位
之后
数据位的个数可以是 4、5、6、7、8 等,构成一个字符
从最低位
开始传送,靠时钟
定位3 奇偶校验位
数据位加上这一位后,使得 "1"
的位数应为偶数
(偶校验)或奇数
(奇校验),以此来校验数据传送的正确性
4 停止位
一个字符数据的 结束标志
,可以是1位
、1.5位
、2位
的高电平
5 空闲位
处于 逻辑"1"
的状态,表示当前线路上没有数据传送
(1)UART协议传输时序(
低位在前
)如图所示:
(2)UART发送数据 “0x50 0x50”(0101 0000) 在示波器的图形显示:
2、UART 默认都是
高电平
TX,RX 引脚初始化完成,进入 等待通信状态(空闲状态),都是高电平。
-
串口通讯协议的时序讲解
2019-07-06 18:55:16串口通讯(Serial Communication)是一种非常常用的串行...我们常说的UART、RS232、RS422、RS485都是采用了这种通讯协议,其接口时序都是一致的,只是具体的物理层的电平的不同。 因此,无论是RS232协议,还是RS48... -
UART串口协议时序图
2021-08-31 13:16:45为了在编写协议的时候更加方便,笔者使用逻辑分析仪读取了UART串口协议的时序,记录如下。 UART协议 波特率 起始位 数据位 奇偶校验位 停止位 1. 波特率9600,数据位8,无奇偶校验位,停止位1。 单字节情况: 多... -
串口UART时序和数据传输
2020-04-02 11:20:30UART(Universal Asynchronous Receiver/Transmitter:通用异步收发传输器)是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。 UART通信协议 UART作为异步串口通信协议的一种... -
串行通讯的时序分析
2016-03-13 09:59:39通信协议按时间分为:同步通信 和异步通信,按发送数据的位宽可分为串行通信和并行通信,串行通信里按通信数据传输的方向机是否同时收发程序通信又分为:单工,半双工,全双工。 区别:同步和异步: 同步是面向... -
基于FPGA的串口通信时序设计
2020-07-18 22:13:03这篇文章主要总结一下串口通信的收发时序,IIC是一种半双工的通信协议,收发共用一条数据线,串口是一种全双工的通信协议,和IIC存在比较大的区别,所以就把串口也总结一下。 -
基于FPGA技术的RS 232接口时序电路设计
2021-01-19 22:26:50随着FPGA技术的高速发展,一些常见的接口电路的时序电路可以通过FPGA实现,通过这种设计可减少电路系统元件的数量,提高系统集成度和可靠性。详细阐述了如何通过FPGA实现RS 232接口的时序逻辑设计。 关键词:FPGA... -
uart接口的时序分析
2020-04-17 02:05:56数据传送速率用波特率来表示, 指单位时间内... 传输时序如下图 在UART中,信号线上共有两种状态, 分别用逻辑1(高电平)和逻辑0(低电平)来区分 在空闲时, 数据线应该保持在逻辑高电平状态 其中... -
EDA/PLD中的基于FPGA技术的RS 232接口时序电路设计
2020-10-21 04:42:48随着FPGA技术的高速发展,一些常见的接口电路的时序电路可以通过FPGA实现,通过这种设计可减少电路系统元件的数量,提高系统集成度和可靠性。详细阐述了如何通过FPGA实现RS 232接口的时序逻辑设计。 关键词:FPGA... -
串口问题排查-时序异常
2020-07-31 14:55:27采用新塘处理器NUC972 使用该芯片所有的串口 UART1~UART10 测试发现串口数据读取异常 采用测试串口终端SSCOM 问题还原步骤 内核已经配置了所有的串口功能,串口PIN复用正常 设备启动可以识别10个串口 ~ # ls /dev/... -
DHT11时序图之章鹏制作
2017-05-11 11:50:3911111 -
串行外设接口(SPI)总线时序介绍
2020-07-25 15:33:51SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议。 -
SPI接口原理与时序
2020-05-14 19:11:40SPI接口原理与时序 SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的串行通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供... -
串行外设接口(SPI)总线时序详解
2020-08-11 09:05:58SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,...SPI是一个环形总线结构,由ss(cs)、sck、sdi、sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换。 -
项目开发中遇到接收串口数据时序混乱的问题
2022-03-15 14:11:07项目开发中遇到接收串口数据时序混乱的问题 刷卡模块出现响应很慢的情况,或者说时序不一样的情况,很可能是开了两个应用同时在给串口发送数据。出现这种情况的解决办法: 1:用TLL转USB链接刷卡器,循环给刷卡器... -
FPGA学习-UART串口发送单字节(UART时序分析+真正的FPGA设计看图写代码)
2021-12-12 14:24:41首先看UART发送时序图: 要发送一个完整字节,需要“1位起始位+8位数据位+1位停止位”,图上的第11位,是确认一个字节发送完的一位。 重点是每一位之间的发送时间需要保持一致,也就是bps_clk的每个高脉冲之间的... -
串口通信时序的位序是先发低位
2021-11-01 12:08:14串口比较轴,先发低位: 参考人家I2C,就是先发高位: -
串口系统简图、时序图
2018-05-21 14:14:15串口系统简图串口线程时序 -
电路时序图有什么作用
2021-05-25 03:09:40什么是时序电路虽然每个数字电路系统可能包含有组合电路,但是在实际应用中绝大多数的系统还包括存储元件,我们将这样的系统描述为时序电路。时序电路,是由最基本的逻辑门电路加上反馈逻辑回路(输出到输入)或器件... -
RS232与RS485时序分析.docx
2020-03-18 12:08:32经常遇到初学者,对单片机串行通讯出了问题不知道如何办的情况。其实最有效的调试方法是用示波器观察收发数据的波形。通过观察波形可以确定发送正确与否; -
2.3 基于FPGA的UART协议实现(二)UART传输时序分析
2019-08-11 22:22:392.3.2 UART传输时序分析 ... 图2 33 串口时序 表2 7 串口时序说明表 在串口的总线上“高电平”是默认的状态,当一帧数据的开... -
【STM32笔记4】UART的时序解读与硬件运用(即硬件UART的实现,另有硬件UART运用和软件UART运用/硬件串口和...
2020-04-15 14:33:191、UART(Universal Asynchronous Receiver Transmitter)总线是异步串口,故一般比I2C、SPI两种同步串口的结构要复杂很多,一般由波特率产生器(产生的波特率等于传输波特率的16倍)、UART接收器、UART发送器组成,硬件... -
基于STM32的12864串行时序的实现
2015-07-21 22:47:04以下是12864液晶串行时序图,下面就根据这个图来分析一下12864串行时序的实现,只有真正弄清楚了时序图才能真正了解串行传输的原理。 从图上可以看出串行传输时需要用到CS,SCLK,SID三根信号线,但是... -
常用通信时序之UART、IIC、SPI(基于STM32)
2021-07-02 13:54:08类 型:串行异步通信 总线定义: TXD:发送数据线 RXD:接收数据线 数据传输: ①接收方RX初始电平为1 ②带起始位0和停止位1 ③先发送低位再高位 ④传输数据位以字节为单位,一次只能传输一字节 ⑤以波特率为时基... -
数字音频接口时序简介
2020-05-09 23:47:46最近在做音频这块,接触了挺多接口,收集了一下各种音频接口的介绍,包括: PCM、I2S、TDM、PDM,一般通用的就这四种。 1.PCM(脉冲编码调制) 接口定义都是一样的 分别是: 位时钟信号(BCLK),同步信号(LRCK),... -
基于单片机89C51的异步串行接口键盘设计
2020-12-06 18:58:50摘 要:介绍了计算机异步串行接口键盘的特殊设计要求和工作原理,结合开发过程,讨论分析了89C51芯片的结构、功能特点以及应用中需要注意的问题,并给出了计算机串行接口键盘的硬件原理图及软件流程图。 关键词:...