串口UARAT的实现和解析

这次来记录下串口USART的实现

串口是非常常用的通讯方式,其它就不说什么了,记录下一些东西吧。
之前用过GD32F450发送串口,手册上写着波特率可以达到12M。我配置成为8倍过采样率,转换芯片是FT232,实现了12M的波特率。用来传输图像和通讯,数据也没有丢。一般说波特率像9600,115200是比较常见的,就是1秒传送这么多位,按照1个字节8位来算,9600的波特率1秒最多只传送1200个字节,在有些地方对速率就不够了。

/**
* @brief  串口1配置
* @param  波特率
* @retval None
* @note   配置:8M,8bit,1bit停止位,无奇偶校验,接收使用中断,发送启动DMA
*/
void USART1_Config(uint32_t uart_baudrate)
{
    rcu_periph_clock_enable(USART1_TX_GPIO_CLK);
    /* enable USART clock */
    rcu_periph_clock_enable(USART1_CLK);

    /* connect port to USARTx_Tx RX */
    gpio_af_set(USART1_TX_GPIO_PORT,USART1_TX_GPIO_AF, USART1_TX_GPIO_PIN);
    gpio_af_set(USART1_RX_GPIO_PORT,USART1_RX_GPIO_AF, USART1_RX_GPIO_PIN);

    /* configure USART Tx as alternate function push-pull */
    gpio_mode_set(USART1_TX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP,USART1_TX_GPIO_PIN);
    gpio_output_options_set(USART1_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,USART1_TX_GPIO_PIN);

    /* configure USART Rx as alternate function push-pull */
    gpio_mode_set(USART1_RX_GPIO_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP,USART1_RX_GPIO_PIN);
    gpio_output_options_set(USART1_RX_GPIO_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,USART1_RX_GPIO_PIN);

    /* USART configure */
    usart_deinit(USART1_PORT);
    usart_oversample_config(USART1_PORT,USART_OVSMOD_8);
    usart_baudrate_set(USART1_PORT,uart_baudrate);
//    USART_BAUD(USART1_PORT)=0x10;

    usart_receive_config(USART1_PORT, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART1_PORT, USART_TRANSMIT_ENABLE);

    usart_dma_receive_config(USART1_PORT,USART_DENR_ENABLE);
    usart_dma_transmit_config(USART1_PORT,USART_DENT_ENABLE);


    usart_interrupt_enable(USART1_PORT, USART_INT_IDLE);  //开启空闲接收中断

    usart_enable(USART1_PORT);
    nvic_irq_enable(USART_IRQ, 0,2);
    usart_dma_config();
}
```/**
 
void USART1_Tx_DMA_Config(void)
{
   // uint32_t USARTBuffer;

    dma_single_data_parameter_struct dma_init_struct;

    /* deinitialize DMA channel7(USART0 tx) */
    dma_deinit(DMA1, DMA_CH7);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
    dma_init_struct.memory0_addr =0;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    dma_init_struct.number =0;
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH7, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA1, DMA_CH7);
    dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI4);
    dma_channel_disable(DMA1, DMA_CH7);  //暂不开启DMA
}
/*!
    \brief      configure USART DMA
    \param[in]  none
    \param[out] none
    \retval     none
*/
void usart_dma_config(void)
{
    dma_single_data_parameter_struct dma_init_struct;
    /* enable DMA1 */
    rcu_periph_clock_enable(RCU_DMA1);

    /* deinitialize DMA channel7(USART0 tx) */
    dma_deinit(DMA1, DMA_CH7);
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPH;
    dma_init_struct.memory0_addr =0;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    dma_init_struct.number =0;
    dma_init_struct.periph_addr = USART0_DATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH7, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA1, DMA_CH7);
    dma_channel_subperipheral_select(DMA1, DMA_CH7, DMA_SUBPERI4);
    dma_channel_disable(DMA1, DMA_CH7);  //暂不开启DMA
    /* deinitialize DMA channel2(USART0 rx) */
    dma_deinit(DMA1, DMA_CH2);
    dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;
    dma_init_struct.memory0_addr = (uint32_t) UART_Buffer;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_8BIT;
    dma_init_struct.number = UART_BUFFER_SIZE;
    //    dma_init_struct.periph_addr =  USART0_DATA_ADDRESS;
    dma_init_struct.periph_addr =(uint32_t)& USART_DATA(USART1_PORT);
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH2, &dma_init_struct);
    /* configure DMA mode */
    dma_circulation_disable(DMA1, DMA_CH2);
    dma_channel_subperipheral_select(DMA1, DMA_CH2, DMA_SUBPERI4);
    dma_channel_enable(DMA1, DMA_CH2);
}

/**
 * @brief  重启启动串口接收
 * @param
 * @retval
 */
void DMA_Enable(uint16_t ndtr)
{

    dma_channel_disable(DMA1, DMA_CH2);                      //关闭DMA
    while(dma_flag_get(DMA1, DMA_CH2, DMA_INTF_FTFIF)!=RESET)
    {
        dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INTC_FTFIFC); //清标志
    }

    dma_transfer_number_config(DMA1,DMA_CH2,ndtr);          //设置长度
    dma_channel_enable(DMA1, DMA_CH2);                       //开DMA
}
/**
* @brief  手动启动串口1TX对应的DMA
* @param  发送数据长度
* @param  发送数据的buffer指针
* @retval None
* @note   None
*/
void USART1_DMA_Send(uint8_t *buffer,uint16_t len)
{
    dma_channel_disable(DMA1, DMA_CH7);
    dma_interrupt_flag_clear(DMA1, DMA_CH7, DMA_INTC_FTFIFC); //清标志

    dma_memory_address_config(DMA1, DMA_CH7,DMA_MEMORY_0,(uint32_t)buffer);
    dma_transfer_number_config(DMA1, DMA_CH7,len);
    /* enable DMA channel7 */
    dma_channel_enable(DMA1, DMA_CH7);   //开启DMA传输

//    while(dma_flag_get(DMA1, DMA_CH7, DMA_INTF_FTFIF)!=RESET)  //传输完成
//    {
//        dma_interrupt_flag_clear(DMA1, DMA_CH7, DMA_INTC_FTFIFC); //清标志
//    }

}
/**
 * @brief	串口DMA接收
 * @param
 * @retval
 */
void USART_IRQHandler(void)
{
    uint8_t  rc_tmp;
    uint16_t rc_len;

    if((RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)) &&
            (RESET != usart_flag_get(USART0	, USART_FLAG_IDLE)))
    {
        rc_tmp=USART_STAT0(USART0);
        rc_tmp=USART_DATA(USART0);

        dma_channel_disable(DMA1, DMA_CH2);
        dma_interrupt_flag_clear(DMA1, DMA_CH2,DMA_INTC_FTFIFC);
        dma_interrupt_flag_clear(DMA1, DMA_CH2,DMA_INTC_TAEIFC);
        rc_len =UART_BUFFER_SIZE - DMA_CHCNT(DMA1, DMA_CH2);
			  testAAA=rc_len;
        USART1_RECRIVE(UART_Buffer, rc_len);   //数据解析
        DMA_Enable(UART_BUFFER_SIZE);
    }
}

因为需要发送的数据量有点大,串口的发送使用了DMA,是单次的DMA。每发完一次后自己手动重启发送。
接收则使用DMA+空闲帧中断。
如此串口的发送和接收都可以依靠DMA来完成,大大减少CPU。

串口接收有好几种方式
1.DMA+空闲中断
个人认为这是一种比较好的方式,要求更高的还将DMA的双缓存做了

2.字节中断
以字节的方式接收,接收不定长的 时候根据协议来判定。原理就是先检验头,然后再检验数据长度,一直接收数据量直达达到数据长度,之后进行校验。

3.开定时器来判断一帧是否结束
也可以用定时器来判断,每个字节的间隔肯定比帧间隔长。可以用这个来判断一帧是否结束

也可以做成串口的环形队列,就是用一个结构体数组,包含了数组和长度。在一帧数据接受完成后,将数据和长度放到结构体数组中,同时结构体数据的下标更新。如果是跑逻辑,在主循环里查询队列里面是或有新数据。如果是跑操作系统,则可以直接使用队列,将一帧数据接受完成后的结构体直接放到队列中,在相应的任务里进行处理。

在串口解析这一块,一般的写法是`

switch (Function)
case xx:
break;
case xx
break;
case xx
break

还有另外一种更好的机制,有限状态机FSM。

简单说就是创建结构体数组,结构体包含了当前的状态,触发条件,执行的动作,下个个状态。执行的动作使用函数指针。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值