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"
// MyIouartConfig [2017.2.20]
// 注:双定时器模拟串口!CT32B0+CT32B1
//ECHO <0=> No <1=> Yes
//回显测试功能:
//打开回显功能后,模拟串口将自动返回自己所接收到的数据,仅作测试串口功能使用!
#define ENABLE_ECHO 0
// COM_BAUDRATE <1200=> 1200 <9600=> 9600 <57600=> 57600 <115200=> 115200
// 串口通讯波特率配置
// 默认为9600!
#define IOUART_BAUDRATE 9600
//
//--------------------------------------------------------------
#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]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<IS &= ~(1<IEV &= ~(1<IBE &= ~(1<IC=(1<IE |= (1<DATA &= ~(1 << 3);//L
//Record wrong case.Just For Debug.
l_RxWrongRecord[l_RxWrongRecord[0]]=IOUART_RECV_WRONG_ST;
if(l_RxWrongRecord[0]IC |= (1<IE |= (1<DATA |= (1 << 3);//H //LED
//
if((MyIOUART_RX_P->DATA & (1<MIS&(1<IE &= ~(1<IC |= (1<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]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<两位停止位 0->1位停止位
{
if(l_channel==0)
{//Rx
if(MyIOUART_RX_P->DATA & (1<DATA & (1<IC |= (1<IE |= (1<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