MCU模拟UART口

群里交流,有人问用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();
    }

 

 

 

 

 

你可能感兴趣的:(单片机)