iot-fan

联系: iotfan123#163.com
注意:
1,本博客之内容来源于网上收集以及相关技术人员提供,如果有侵犯到您的权益,请电邮我沟通;
2,本博客之内容乃分享,交流,学习,研究之目的,作者不对内容的真实性,有效性,及时性负责,也不对因本博客的任何内容导致的任何后果负责;
3,本博客之内容禁止转发到CSDN网站,转到别的网站请保留出处.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

适用芯片

  • CH579/ch578/ch577
  • CH573/CH571
  • CH583/CH582/CH581
  • CH569/CH565(理论上适用)

UART 外设特点

  • 串口的时钟来至系统主频的整数分频
  • 收发分别有8个字节的硬件FIFO,支持fifo阈值中断
  • 接收支持超时中断

UART初始化

官网的历程中,初始化写的比较简单,下面我们以ch579 为例

void ch57x_uart3_init(uint8_t remap) {
    if(remap){
        //重映射
        GPIOPinRemap( ENABLE, RB_PIN_UART3 );
        //TXD gpio初始化
        GPIOB_SetBits(bTXD3_);
        GPIOB_ModeCfg(bTXD3_, GPIO_ModeOut_PP_5mA);
        //RXD gpio初始化
        GPIOB_SetBits(bRXD3_);
        GPIOB_ModeCfg(bRXD3_, GPIO_ModeIN_PU);
    } else {
        //不使能重映射
        GPIOPinRemap( DISABLE, RB_PIN_UART3 );
        //TXD gpio初始化
        GPIOA_SetBits(bTXD3);
        GPIOA_ModeCfg(bTXD3, GPIO_ModeOut_PP_5mA);
        //RXD gpio初始化
        GPIOA_SetBits(bRXD3);
        GPIOA_ModeCfg(bRXD3, GPIO_ModeIN_PU);
    }
    //uart3 init
    UART3_DefInit();
    UART3_BaudRateCfg(115200);

    //enable interupt
    UART3_INTCfg(ENABLE, RB_IER_RECV_RDY | RB_IER_LINE_STAT);
#if defined(CH579)
    NVIC_EnableIRQ( UART3_IRQn );
#else
    PFIC_EnableIRQ(UART3_IRQn);
#endif
}

UART接收处理

uint16_t user_rx_buffer_length_mask = 1024-1;
uint16_t user_rx_buffer_write_index = 0;
uint8_t uart_rx_buffer[1024];

void UART3_IRQHandler(void){
    switch( UART3_GetITFlag() ){
        case UART_II_LINE_STAT:        // 线路状态错误
            UART3_GetLinSTA();
            break;
        case UART_II_RECV_RDY:
        case UART_II_RECV_TOUT:
            while(R8_UART3_RFC) {
                 uart_rx_buffer[user_rx_buffer_write_index & user_rx_buffer_length_mask] = R8_UART3_RBR;
          user_rx_buffer_write_index += 1;
            }
            break;
        case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
            break;
        case UART_II_MODEM_CHG: // 只支持串口0
            break;
        default:
            break;
     }
}

UART自带的UART超时处理(必须RX的触发设置>1字节,否则永远不会产生UART_II_RECV_TOUT,这不是WCH推荐的用法):

uint16_t user_rx_buffer_length_mask = 1024-1;
uint16_t user_rx_buffer_write_index = 0;
uint8_t uart_rx_buffer[1024];

//UART_II_RECV_TOUT的触发前提是当前rx fifo里面有数据,且串口持续了4个字节的时间没有数据进来
//不是每次接收都会触发UART_II_RECV_TOUT,如果我们在UART_II_RECV_RDY 每次都把fifo的数据读完
//如果一定要每次接收UART_II_RECV_TOUT都产生,就必须保证rxfifo 里面的数据不能为空

/*** Keep at least 1 byte in the RX fifo and UART_II_RECV_TOUT will be triggered at the end of every reception .***/
void UART3_IRQHandler(void){
    switch( UART3_GetITFlag() ){
        case UART_II_LINE_STAT:        // 线路状态错误
            UART3_GetLinSTA();
            break;
        case UART_II_RECV_RDY:          // 数据达到设置触发点 ,这里可以留一个在接收fifo里面,保证每次都能产生接收超时
            while( R8_UART3_RFC > 1) {  // 这个方式必须保证uart的接收触发中断是大于1字节的
                 uart_rx_buffer[user_rx_buffer_write_index & user_rx_buffer_length_mask] = R8_UART3_RBR;
          user_rx_buffer_write_index += 1;
            }
            break;
        case UART_II_RECV_TOUT:         // 接收超时,暂时一帧数据接收完成
            //for(uint8_t i=0;i < R8_UART3_RFC;i++) //There is an issue here
            while(0 != R8_UART3_RFC) {
                 uart_rx_buffer[user_rx_buffer_write_index & user_rx_buffer_length_mask] = R8_UART3_RBR;
          user_rx_buffer_write_index += 1;
            }
            break;
        case UART_II_THR_EMPTY: // 发送缓存区空,可继续发送
            break;
        case UART_II_MODEM_CHG: // 只支持串口0
            break;
        default:
            break;
     }
}

UART 查询发送

//tx process
//本查询函数需要扔到主循环不断调用
while(R8_UART1_TFC < UART_FIFO_SIZE){
    //判断发送软件缓冲区,是否空,如果不空,就一个一个读出来,填到硬件fifo里
    if((user_tx_buffer_write_index - user_tx_buffer_read_index )& user_tx_buffer_length_mask) {
        //把软件缓冲区的数据填到uart的硬件发送fifo里
        R8_UART1_THR = uart_tx_buffer[user_tx_buffer_read_index & user_tx_buffer_length_mask] ;  
        user_tx_buffer_read_index += 1;
    }else{
        break;
    }
}

UART 中断发送

//开始发送,这里,我们需要先向串口的发送fifo里面写入数据,然后串口的发送fifo空的时候才会报中断上来,接下来我们在中断服务程序里面处理就好了
void uart_start_send(void) {
    //这里涉及到中断中修改标志位,我们关中断后进行查标志位
    __disable_irq();
    if(false == uart1_tx_on_going) {
        __enable_irq();
        //由于写fifo的操作非常快,我们这里直接while循环即可
        while(R8_UART1_TFC < UART_FIFO_SIZE){
            //发送缓冲区是否有数据,这里我们用软件实现了一个缓冲区,如果有数据,就把数据取出来放到uart的fifo里
            if((user_rx_buffer_write_index - user_rx_buffer_read_index )& user_rx_buffer_length_mask) {
                R8_UART1_THR = uart_rx_buffer[user_rx_buffer_read_index & user_rx_buffer_length_mask] ;  
                user_rx_buffer_read_index += 1;
                uart1_tx_on_going = true;
            }else{
                break;
            }
        }
    }else{
        __enable_irq();
    }
}


//中断服务程序中的处理,这里主要关注 UART_II_THR_EMPTY 里面的处理
void UART1_IRQHandler(void) {
    UINT8 i;
    //GPIOA_SetBits(GPIO_Pin_0);
    //GPIOA_ResetBits(GPIO_Pin_0);
    switch( UART1_GetITFlag() ) {
        case UART_II_LINE_STAT:        // 线路状态错误
            UART1_GetLinSTA();
            break;
        case UART_II_RECV_RDY:          // 数据达到设置触发点
        case UART_II_RECV_TOUT:         // 接收超时,暂时一帧数据接收完成
            while(0 != R8_UART1_RFC) {
                uart_rx_buffer[user_rx_buffer_write_index & user_rx_buffer_length_mask] = R8_UART1_RBR;
                user_rx_buffer_write_index += 1;
            }
            break;
        case UART_II_THR_EMPTY:         //发送缓存区空,可继续发送
		                                //这个事件产生只能说fifo是空的了,但是串口的发送缓冲区可能还有正在发送的数据在发送,如果是想要控制RS485之类的信号,请在这个事件产生时候,查询( R8_UART1_LSR & RB_LSR_TX_ALL_EMP ) 标志位
            while(R8_UART1_TFC < UART_FIFO_SIZE){
                if((user_tx_buffer_write_index - user_tx_buffer_read_index )& user_tx_buffer_length_mask) {
                    R8_UART1_THR = uart_tx_buffer[user_tx_buffer_read_index & user_tx_buffer_length_mask] ;  
                    user_tx_buffer_read_index += 1;
                }else{
                    uart1_tx_on_going = false;
                    break;
                }
            }
            break;
        case UART_II_MODEM_CHG:         // 只支持串口0
            break;
        default:
            break;
    }
}

BLE工程中UART收发处理:

CH57x的ble协议栈是查询方式实现的,实际应用中,中断在微观上会打断BLE的运行,所以能少用中断就少用中断,在中断张能少占用时间就少占用一些,
但是而ch57x的uart 又没有dma,只有有8个直接的fifo,所以,
在没有串口流控(CTS RTS)的情况下,建议uart的接收使用中断方式,而uart发送使用查询方式

Q&A

1.休眠情况下UART能收发数据吗

UART依赖于高频时钟,休眠下高频时钟会停止,所以收会停止

2.开启休眠,串口打印输出不完整,总是后面丢几个字节或者乱码

由于芯片存在硬件fifo和发送缓冲区,在进入睡眠时候fifo的数据或者发送缓冲区数据可能还没发完,
我们可以等待每个串口的 R8_UARTx_LSR寄存器 的 RB_LSR_TX_ALL_EMP位有效后才标志真的发送完成

支持的波特率

波特率计算与误差

image
image
image
image
image

posted on 2020-08-05 13:47  iot-fan  阅读(4595)  评论(0编辑  收藏  举报