群里交流,有人問用IO口怎么模擬UART,19年做過一個專案,其中就有這個功能,測驗后,
效果還可以,做下記錄吧,希望能夠對大家有所幫助,
一、設計思路
由于我的專案需要模擬UART能夠同時實作RX和TX,因此利用兩個Timer和GPIO去實作:
Timer1中斷+GPIO 實作TX;
Timer8中斷+GPIO中斷 實作RX,
采用模塊化設計,把驅動層和上層應用分離,采用基于介面變成,把驅動層分離:
驅動需要提供介面:
typedef void timer_stop(void);
typedef void timer_start(uint8_t chPrescaler);
typedef void gpio_irq_stop(void);
typedef void gpio_irq_start(void);
#define SOFTWARE_UART_TX_X(__VALUE) LOG_TXD_X(__VALUE)
#define SOFTWARE_UART_RX_X() LOG_RXD_READ()
提供給上層的介面:
void software_uart_tx_init(software_uart_parameter_t* ptUartParameter,timer_stop* ptTimerStop,timer_start* ptTimerStart);
void software_uart_rx_init(software_uart_parameter_t* ptUartParameter,timer_stop* ptTimerStop,timer_start* ptTimerStart,gpio_irq_stop* ptGpioIrqStop,gpio_irq_start* ptGpioIrqStart);
bool software_uart_rx_is_busy(software_uart_parameter_t* ptUartParameter);
bool software_uart_tx_is_busy(software_uart_parameter_t* ptUartParameter);
void software_uart_tx(software_uart_parameter_t* ptUartParameter,uint8_t* chBuffer,uint16_t hwSize);
void software_uart_tx_irq(software_uart_parameter_t* ptUartParameter);
void software_uart_rx_gpio_irq(software_uart_parameter_t* ptUartParameter);
fsm_rt_t software_uart_rx_timer_irq(software_uart_parameter_t* ptUartParameter,uint8_t* pchData);
二、TX實作
UART格式:115200/8/1/N,
UART格式:開始位(低電平),資料位(8位,先發低位),校驗位(沒有),停止位(高電平),
1》計算定時器定時時間:
定時器的頻率為:f1
波特率為:baudrate
發送一個 bit 時間:1s/baudrate
定時器計一個數時間:1/f1
定時器計數=發送一個 bit 時間/定時器計一個數時間 - 1 = (f1/baudrate) - 1
注意:定時器計數范圍不能超過其最大值,
2》發送原理
(1)首先用戶查詢現在UART是否發送忙,如過忙,則等待;不忙,則呼叫
software_uart_tx(software_uart_parameter_t* ptUartParameter,uint8_t* chBuffer,uint16_t hwSize);
把要發送的資料和長度給底層驅動;software_uart_tx同時啟動定時器;
(2)在定時器中斷里面處理發送邏輯:
a. 首先發送起始位;
b. 然后依次發送資料位(先發低bit);
c. 再發送停止位(沒有校驗位);
d. 判斷是否發送完成,如果完成,則停止timer;如果沒有發送完成則繼續a b c流程,
switch(this.tSoftwarUartTxState){
case UART_TX_START:
this.tTxByte.chByte = this.pchuartTxBuffer[this.hwUartTxCnt];
this.hwUartTxCnt++;
this.chTxUartBitCnt=0;
this.tSoftwarUartTxState = UART_TX_TXING_START_BIT;
case UART_TX_TXING_START_BIT:
SOFTWARE_UART_TX_X(0);
this.tSoftwarUartTxState = UART_TX_TXING_BYTE;
break;
case UART_TX_TXING_BYTE:
switch(this.chTxUartBitCnt){
case 0:
SOFTWARE_UART_TX_X(this.tTxByte.tBit0);
this.chTxUartBitCnt++;
break;
case 1:
SOFTWARE_UART_TX_X(this.tTxByte.tBit1);
this.chTxUartBitCnt++;
break;
case 2:
SOFTWARE_UART_TX_X(this.tTxByte.tBit2);
this.chTxUartBitCnt++;
break;
case 3:
SOFTWARE_UART_TX_X(this.tTxByte.tBit3);
this.chTxUartBitCnt++;
break;
case 4:
SOFTWARE_UART_TX_X(this.tTxByte.tBit4);
this.chTxUartBitCnt++;
break;
case 5:
SOFTWARE_UART_TX_X(this.tTxByte.tBit5);
this.chTxUartBitCnt++;
break;
case 6:
SOFTWARE_UART_TX_X(this.tTxByte.tBit6);
this.chTxUartBitCnt++;
break;
case 7:
SOFTWARE_UART_TX_X(this.tTxByte.tBit7);
this.chTxUartBitCnt++;
break;
default:
SOFTWARE_UART_TX_X(1);
this.chTxUartBitCnt=0;
this.tSoftwarUartTxState = UART_TX_TXING_END_BIT;
}
break;
case UART_TX_TXING_END_BIT:
if(this.hwUartTxCnt < this.hwUartTxNum){
this.tSoftwarUartTxState = UART_TX_START;
}else{
this.bTxBusy = 0;
this.ptUartTxTimerStop();
}
break;
default:
SOFTWARE_UART_TX_X(1);
this.bTxBusy = 0;
this.ptUartTxTimerStop();
}
三、接收原理
接收相對復雜些,因為不知道是什么時候來資料,因此不能用查詢模式,只能用接收模式,
(1)GPIO配置
由于UART的TX空閑態是高電平,起始位是低電平,因此設定為下降沿觸發,
在中斷里面處理,也很簡單:停止GPIO中斷;以計算出定時時間的一半啟動定時器;
this.tSoftwareUartRxState = UART_RX_START;
this.ptUartRxGpioStop();
this.ptUartRxTimerStart(0);
(2)定時器中斷處理
a. 首先判斷當前電平是不是低電平;如果是,則以計算出定時時間初始化定時器;如果否,則關閉定時器,啟動GPIO中斷;
b. 接收8個bit;
c. 如果接收完成8bit,則關閉定時器,啟動GPIO中斷;
switch(this.tSoftwareUartRxState){
case UART_RX_START:
if(SOFTWARE_UART_RX_X()){
this.ptUartRxTimerStop();
this.ptUartRxGpioStart();
}else{
this.ptUartRxTimerStart(1);
}
this.chRxUartBitCnt=0;
this.tSoftwareUartRxState = UART_RX_RXING_BYTE;
break;
case UART_RX_RXING_BYTE:
switch(this.chRxUartBitCnt){
case 0:
this.tRxByte.tBit0 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 1:
this.tRxByte.tBit1 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 2:
this.tRxByte.tBit2 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 3:
this.tRxByte.tBit3 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 4:
this.tRxByte.tBit4 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 5:
this.tRxByte.tBit5 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 6:
this.tRxByte.tBit6 = (SOFTWARE_UART_RX_X())?1:0;
break;
case 7:
this.tRxByte.tBit7 = (SOFTWARE_UART_RX_X())?1:0;
break;
}
this.chRxUartBitCnt++;
if(this.chRxUartBitCnt >= 8){
this.tSoftwareUartRxState = UART_RX_END;
}
break;
case UART_RX_END:
this.ptUartRxTimerStop();
this.ptUartRxGpioStart();
if(NULL != pchData){
*pchData = this.tRxByte.chByte;
}
return fsm_rt_cpl;
default:
this.ptUartRxTimerStop();
this.ptUartRxGpioStart();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/278940.html
標籤:其他
上一篇:不受局域網限制的遙控小車(esp8266+l298n+點燈科技平臺)
下一篇:51單片機準雙向口存在的問題
