打印
[开发板]

【CW32L031CxTx StartKit评估板测评】5.串口的使用

[复制链接]
120|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创# @21小跑堂  
CW32L031 内部集成 3 个通用异步收发器 (UART),评估板上集成了一个USB转串口,不过用的是比较古老的MINIUSB,以前的MP3和MP4上经常会用到这个接口,现在几乎绝迹了,连接的是PA08 和PA09,对应UART1

简单做个收发测试,使用轮询方式接收串口数据以\n作为结束符,然后将接收到的数据再通过串口发送回去
void uart1_init()
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};
    __RCC_GPIOA_CLK_ENABLE();
    __RCC_UART1_CLK_ENABLE();
    PA08_AFx_UART1TXD();
    PA09_AFx_UART1RXD();
    GPIO_InitStructure.Pins = GPIO_PIN_8;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = GPIO_PIN_9;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
   
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_Over = USART_Over_16;
    USART_InitStructure.USART_Source = USART_Source_PCLK;
    USART_InitStructure.USART_UclkFreq = SystemCoreClock;
    USART_InitStructure.USART_StartBit = USART_StartBit_FE;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(CW_UART1, &USART_InitStructure);
}

int32_t main(void)
{
    uint8_t uart_msg[10];
    uint8_t uart_msg_len = 0,uart_msg_index = 0;
    RCC_HSI_48M_init();
    RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
    RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
    uart1_init();
    while (1)
    {
        do
        {
            while(USART_GetFlagStatus(CW_UART1, USART_FLAG_RC) == RESET);
            USART_ClearFlag(CW_UART1, USART_FLAG_RC);
            if(USART_GetFlagStatus(CW_UART1, USART_FLAG_PE|USART_FLAG_FE))
            {
                USART_ClearFlag(CW_UART1, USART_FLAG_PE|USART_FLAG_FE);
                uart_msg_len = 0x00;
            }
            else
            {
                uart_msg[uart_msg_len] = USART_ReceiveData_8bit(CW_UART1);
                uart_msg_len++;
            }
        }
        while(uart_msg[uart_msg_len-1] != '\n' && uart_msg_len < 10);
        uart_msg_index = 0;
        while(uart_msg_index < uart_msg_len)
        {
            USART_SendData_8bit(CW_UART1, uart_msg[uart_msg_index]);
            uart_msg[uart_msg_index] = 0;
            while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXE) == RESET);
            uart_msg_index += 1;
        }
        while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET);
        uart_msg_len = 0;
    }
}
运行效果

接下来实现一下printf重定向到串口,这样以后就方便输出调试信息了,在MDK中实现这个功能最简单的方法就是先在工程设置中勾选Use MacroLib

包含stdio.h后重写fputc就行了
int fputc(int ch, FILE *f)
{
    while(USART_GetFlagStatus(CW_UART1, USART_FLAG_TXBUSY) == SET);
    USART_SendData_8bit(CW_UART1,ch&0xFF);
    return ch;
}

int32_t main(void)
{
    uint8_t testnum = 0;
    RCC_HSI_48M_init();
    RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
    RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
    uart1_init();
    while (1)
    {
        printf("printf重定向测试 %d",testnum++);
        yuyy_delay_ms(1000);
    }
}
运行效果

接下来试试中断模式下的数据收发,CW32L031串口支持的中断如下

CW32L031的串口有个内置的定时器,可以实现接收空闲检测,这个就方便了很多,不用自己再用定时器检测接收超时,可以实现不定长的数据接收
void uart1_init()
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};
    __RCC_GPIOA_CLK_ENABLE();
    __RCC_UART1_CLK_ENABLE();
    PA08_AFx_UART1TXD();
    PA09_AFx_UART1RXD();
    GPIO_InitStructure.Pins = GPIO_PIN_8;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = GPIO_PIN_9;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
   
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_Over = USART_Over_16;
    USART_InitStructure.USART_Source = USART_Source_PCLK;
    USART_InitStructure.USART_UclkFreq = SystemCoreClock;
    USART_InitStructure.USART_StartBit = USART_StartBit_FE;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(CW_UART1, &USART_InitStructure);
    USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
    USART_SetAutoReload(CW_UART1,480);
    USART_ITConfig(CW_UART1, USART_IT_RC, ENABLE);
    USART_ITConfig(CW_UART1, USART_IT_TIMOV, ENABLE);
    NVIC_SetPriority(UART1_IRQn, 0);
    NVIC_EnableIRQ(UART1_IRQn);
}

#define UART_BUFFER_LEN 20
uint8_t uart_buffer[UART_BUFFER_LEN] = {0};
uint8_t uart_rxindex = 0;
uint8_t uart_rxlen = 0;
uint8_t uart_txindex = 0;
uint8_t uart_txlen = 0;

void readrxtotxbuffer()
{
    uart_txlen += uart_rxlen;
    uart_rxlen = 0;
    USART_ITConfig(CW_UART1, USART_IT_TXE, ENABLE);
}

void UART1_IRQHandler(void)
{
    if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
    {
        uart_buffer[uart_rxindex++] = USART_ReceiveData_8bit(CW_UART1);
        if(uart_rxindex == UART_BUFFER_LEN)
            uart_rxindex = 0;
        uart_rxlen++;
        USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
    }
    if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV) != RESET || uart_rxlen == 10)
    {
        USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
        USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
        readrxtotxbuffer();
    }
    if(USART_GetITStatus(CW_UART1, USART_IT_TXE) != RESET)
    {
        if(uart_txlen > 0)
        {
            USART_SendData_8bit(CW_UART1, uart_buffer[uart_txindex++]);
            if(uart_txindex == UART_BUFFER_LEN)
                uart_txindex = 0;
            uart_txlen--;
        }
        else
        {
            USART_ITConfig(CW_UART1, USART_IT_TXE, DISABLE);
        }
        USART_ClearITPendingBit(CW_UART1, USART_IT_TXE);
    }
}
需要注意的是当uart定时器溢出后需要再次配置定时器,不然后面不会再触发空闲中断,另外这个空闲时间的判定似乎有问题,原来配置的是48000,按照手册描述空闲时间=48000/48000000=1ms,但实际时间有500ms左右
运行结果

利用这个定时器还能实现自动侦测波特率

来实验一下,直接使用例程里的代码
void uart1_init()
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    USART_InitTypeDef USART_InitStructure = {0};
    __RCC_GPIOA_CLK_ENABLE();
    __RCC_UART1_CLK_ENABLE();
    PA08_AFx_UART1TXD();
    PA09_AFx_UART1RXD();
    GPIO_InitStructure.Pins = GPIO_PIN_8;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.Pins = GPIO_PIN_9;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);
   
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_Over = USART_Over_16;
    USART_InitStructure.USART_Source = USART_Source_PCLK;
    USART_InitStructure.USART_UclkFreq = SystemCoreClock;
    USART_InitStructure.USART_StartBit = USART_StartBit_FE;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No ;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(CW_UART1, &USART_InitStructure);
    USART_SetAutoReload(CW_UART1, 0xFFFFFF);
    USART_TimerModeConfig(CW_UART1, USART_TimerMode_AutoBaudRate1);
    USART_ITConfig(CW_UART1, USART_IT_TIMOV | USART_IT_BAUD, ENABLE);
    NVIC_SetPriority(UART1_IRQn, 0);
    NVIC_EnableIRQ(UART1_IRQn);
}
uint8_t uart_baud_fin = 0;
uint8_t uart_ov_count = 0;

void readrxtotxbuffer()
{
    uart_txlen += uart_rxlen;
    uart_rxlen = 0;
    USART_ITConfig(CW_UART1, USART_IT_TXE, ENABLE);
}

void UART1_IRQHandler(void)
{
    uint32_t uart_baud_count = 0;
    if(uart_baud_fin == 0)
    {
        if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV))
        {
            USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
            uart_ov_count += 1;
        }
        if(USART_GetITStatus(CW_UART1, USART_IT_BAUD))
        {
            uart_baud_count = (0x1000000*uart_ov_count + USART_GetCounter(CW_UART1)) / 4;
            CW_UART1->BRRI = (uint16_t)(uart_baud_count >> 4);
            CW_UART1->BRRF = (uint16_t)(uart_baud_count & 0x0F);
            USART_ClearITPendingBit(CW_UART1, USART_IT_BAUD);
            USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
            USART_ITConfig(CW_UART1, USART_IT_RC, ENABLE);
            USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
            USART_SetAutoReload(CW_UART1, 480);
            uart_ov_count = 0;
            uart_baud_fin = 1;
        }
    }
    else
    {
        if(USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)
        {
            uart_buffer[uart_rxindex++] = USART_ReceiveData_8bit(CW_UART1);
            if(uart_rxindex == UART_BUFFER_LEN)
                uart_rxindex = 0;
            uart_rxlen++;
            USART_ClearITPendingBit(CW_UART1, USART_IT_RC);
        }
        if(USART_GetITStatus(CW_UART1, USART_IT_TIMOV) != RESET || uart_rxlen == 10)
        {
            USART_ClearITPendingBit(CW_UART1, USART_IT_TIMOV);
            USART_TimerModeConfig(CW_UART1,USART_TimerMode_Idle);
            readrxtotxbuffer();
        }
        if(USART_GetITStatus(CW_UART1, USART_IT_TXE) != RESET)
        {
            if(uart_txlen > 0)
            {
                USART_SendData_8bit(CW_UART1, uart_buffer[uart_txindex++]);
                if(uart_txindex == UART_BUFFER_LEN)
                    uart_txindex = 0;
                uart_txlen--;
            }
            else
            {
                USART_ITConfig(CW_UART1, USART_IT_TXE, DISABLE);
            }
            USART_ClearITPendingBit(CW_UART1, USART_IT_TXE);
        }
    }
}
运行效果

复位MCU修改上位机串口波特率

如果改变了消息头波特率计算就会出错


接下来简单说一下是如何实现的,先来了解一下串口数据的结构

先是起始帧为低电平,计数器在下降沿开始计数,当遇到上升沿停止计数,这时累积的计数结果就是起始帧加上开头这几个为0的bit的总计数,以上面的为例用F8作为消息头消息结构如下
0 00011111 1
用总计数结果除以4就得到了1个bit所用的计数,波特率计数器和UART定时器的时钟源都是UCLK,可以认为它们的频率相同,之后用这个结果重新设置波特率计数器就能达到自动侦测波特率的功能
如果上位机单独改变了消息头比如F0(00001111),这时候加上起始帧就有5个为0的bit,这时程序里还是按照4个去计算就会造成计算出的波特率不准确,因此在实际使用时要协商好作为判断波特率的消息头




使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

86

主题

451

帖子

4

粉丝