freeelectron 发表于 2022-4-15 11:01

【AT-START-F425测评】+ 串口空闲中断与DMA

<p><span style="font-size:24px;">0、前言</span></p>

<p><span style="font-size:16px;">上节使用串口空闲中断和接收中断实现了串口不定长数据接收,本文使用DMA的方式接收串口数据。</span></p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">1、两种方式特点分析</span></p>

<p><span style="font-size:16px;">串口接收中断的特点:每接收一个数据,需要进一次中断,会不断打断正常程序的执行,在接收数据期间,cpu会不断切换上下文,消耗cpu的时间。</span></p>

<p><span style="font-size:16px;">使用DMA接收的优势:配置好DMA之后,让DMA控制器去接收,不需要cpu参与,节省了cpu的时间,提高了cpu的利用率。</span></p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">2、如何判断串口数据接收完成</span></p>

<p><span style="font-size:16px;">这里使用空闲中断,当产生空闲中断的时候,就认为是一帧数据传输完成了。</span></p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">3、关于AT32F425的DMA</span></p>

<p><span style="font-size:16px;">可以看出有1个DMA控制器,支持7个通道;</span></p>

<p><span style="font-size:16px;">支持8位、16位、32位数据宽度;</span></p>

<p><span style="font-size:16px;">最大支持65535个数据传输;</span></p>

<p><span style="font-size:16px;">DMA支持的外设ADC、I2C、SPI、TIME、USART等。</span></p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">4、DMA通道配置</span></p>

<p><span style="font-size:16px;">&nbsp;可以看出,任何一个通道的请求来源,都可以弹性配置,而不是固定的几个,这也是AT32F425的DMA的一个特色之处。</span></p>

<p>&nbsp;</p>

<p><span style="font-size:24px;">5、代码实现</span></p>

<p><span style="font-size:16px;">(1)缓冲区定义</span></p>

<pre>
<code>#define UART_MAX_BUFF_LEN    255

uint8_t UartBuff;
uint8_t UartIdle=0;
uint8_t UartLen=0;</code></pre>

<p>(2)串口初始化</p>

<pre>
<code>void UartInit(void)
{
        gpio_init_type gpio_init_struct;
           crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);

        /* enable the usart1 and gpio clock */
        crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);

        gpio_default_para_init(&amp;gpio_init_struct);

        /* configure the usart1 tx/rx pin */
        gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
        gpio_init_struct.gpio_out_type= GPIO_OUTPUT_PUSH_PULL;
        gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
        gpio_init_struct.gpio_pins = GPIO_PINS_6 | GPIO_PINS_7;
        gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
        gpio_init(GPIOB, &amp;gpio_init_struct);

        /* config usart1 iomux */
        gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE6, GPIO_MUX_0);
        gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE7, GPIO_MUX_0);


        /* config usart1 nvic interrupt */
        nvic_irq_enable(USART1_IRQn, 3, 0);

        /* configure usart1 param */
        usart_init(USART1, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);
        usart_parity_selection_config(USART1, USART_PARITY_NONE);
    usart_hardware_flow_control_set(USART1,USART_HARDWARE_FLOW_NONE);

        usart_transmitter_enable(USART1, TRUE);
        usart_receiver_enable(USART1, TRUE);
          
        usart_dma_receiver_enable(USART1, TRUE);

        usart_interrupt_enable(USART1, USART_IDLE_INT, TRUE);

        usart_enable(USART1, TRUE);
       
        void UartDmaRxInit(void);
        UartDmaRxInit();
}</code></pre>

<p>(3)串口1接收DMA配置</p>

<p>这里传输方向是外设到内存,外设就是串口,内存是数据缓冲区;</p>

<p>接收到数据后要保存起来,因此内存每次自增。</p>

<pre>
<code>void UartDmaRxInit(void)
{
dma_init_type dma_init_struct;

/* enable dma1 clock */
crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
       
dma_reset(DMA1_CHANNEL1);
dma_default_para_init(&amp;dma_init_struct);
dma_init_struct.buffer_size = UART_MAX_BUFF_LEN;
dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_base_addr = (uint32_t)UartBuff;
dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
dma_init_struct.memory_inc_enable = TRUE;
dma_init_struct.peripheral_base_addr = (uint32_t)&amp;USART1-&gt;dt;
dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
dma_init_struct.peripheral_inc_enable = FALSE;
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
dma_init_struct.loop_mode_enable = FALSE;
dma_init(DMA1_CHANNEL1, &amp;dma_init_struct);

/* config flexible dma for usart1 rx */
dma_flexible_config(DMA1, FLEX_CHANNEL1, DMA_FLEXIBLE_UART1_RX);

dma_channel_enable(DMA1_CHANNEL1, TRUE);
}</code></pre>

<p>(4)串口中断</p>

<p>这里只用到了空闲中断,产生空闲中断后,读取接收到的数据长度。</p>

<pre>
<code>void USART1_IRQHandler(void)
{
        if(usart_flag_get(USART1,USART_IDLEF_FLAG)!= RESET)
        {
                UartDmaRxData();   
                usart_data_receive(USART1);
        }
}</code></pre>

<p>(5)串口DMA接收</p>

<pre>
<code>void UartDmaRxData(void)
{
        dma_channel_enable(DMA1_CHANNEL1, FALSE); //先暂时关闭dma
        dma_flag_clear(DMA1_FDT1_FLAG);
       
        UartLen=UART_MAX_BUFF_LEN-dma_data_number_get(DMA1_CHANNEL1); //获取接收的长度
               
        dma_data_number_set(DMA1_CHANNEL1, UART_MAX_BUFF_LEN); //重新设置长度

        UartIdle=1;//一帧数据接收完成标志
}</code></pre>

<p>(6)应用层处理</p>

<pre>
<code>void UartPro(void)
{
        if(UartLen&amp;&amp;UartIdle)
        {
                printf("Recv:%d,[",UartLen);
                UartSend(UartBuff,UartLen);

                printf("]\r\n");
               
                UartLen=0;
                UartIdle=0;
                dma_channel_enable(DMA1_CHANNEL1, TRUE); //重新打开DMA
        }
}</code></pre>

<p>(7)其他代码</p>

<pre>
<code>void UartSend(uint8_t *data,uint8_t len)
{
        for(uint8_t i=0; i&lt;len;i++)
        {
                while (RESET == usart_flag_get(USART1, USART_TDC_FLAG));
                usart_data_transmit(USART1, data);
        }
}

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
        while(usart_flag_get(USART1, USART_TDC_FLAG) == RESET);
        usart_data_transmit(USART1, (uint8_t) ch);
       
    return ch;
}</code></pre>

<p><span style="font-size:24px;">6、测试</span></p>

<p> &nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

lugl4313820 发表于 2022-4-15 11:32

对串口你是整得熟烂了呀,这对新接手的小伙伴是很好的教材,学习了。辛苦了哈

freeelectron 发表于 2022-4-18 11:19

lugl4313820 发表于 2022-4-15 11:32
对串口你是整得熟烂了呀,这对新接手的小伙伴是很好的教材,学习了。辛苦了哈

<p><img height="48" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/lol.gif" width="48" /></p>
页: [1]
查看完整版本: 【AT-START-F425测评】+ 串口空闲中断与DMA