0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MM32F0140 DMA学习笔记

jf_pJlTbmA9 来源:jf_pJlTbmA9 作者:jf_pJlTbmA9 2023-09-18 16:57 次阅读

DMA简介

DMA全称为Direct Memory Access,即直接存储器访问。在进行DMA传输前,CPU将总线控制权交给DMA,通过共享系统总线,实现无需CPU参与的快速数据传输,能够直接将数据从一个地址空间复制到另一个地址空间,DMA在数据传输结束后将总线控制权交回CPU。

MM32F0140内置5路通用DMA,可以管理设备到存储器、存储器到设备与存储器到存储器的数据传输,每个通道都有专门的硬件DMA请求逻辑,也可以通过软件配置来触发每个通道。

MM32F0140的DMA模块所支持的外设类型包括UARTI2C、SPI、ADC与通用、高级和基础定时器,当DMA从外设产生的请求通过逻辑或输入DMA控制器时,为避免冲突,在一个通道中,同时只能有一个外设DMA请求有效,详细各通道DMA请求如图1所示。

wKgZomUD7deAA_WyAAI_-gCmRpA486.png 图1.各通道DMA外设请求

DMA配置

DMA的配置涉及到传输模式、数据宽度、外设地址、存储器地址、通道优先级、数据传输数量、中断使能、自动重装载及指针增量。

传输模式

存储器到外设模式

配置DMA_CCRx寄存器(x由1 ~ 7)的DIR位选择传输方向,该位置1,传输方向为从存储器读;DMA_CCRx寄存器的MEM2MEM位置0,关闭存储器到存储器模式。

外设到存储器模式

配置DMA_CCRx寄存器的DIR位选择传输方向,该位置0,传输方向为从外设读;DMA_CCRx寄存器的MEM2MEM位置0,关闭存储器到存储器模式。

存储器到存储器模式

数据传输方向为外设地址到存储器地址:DMA_CCRx寄存器的MEM2MEM位置1,使能存储器到存储器模式;DIR位置0,从外设读。

数据传输方向为存储器地址到外设地址:DMA_CCRx寄存器的MEM2MEM位置1,使能存储器到存储器模式;DIR位置1,从存储器读。

注意,存储器到存储器模式不能与循环模式同时使用。

循环模式

若需要循环读写缓冲区或进行连续的数据传输,则可以进入循环模式。配置DMA通道x配置寄存器(DMA_CCRx)中的CIRC位为1,使能循环模式。在循环模式下,若DMA传输数量递减为0,则重新加载先前配置的数值,继续进行DMA数据传输。

自动重新加载

DMA通道x配置寄存器(DMA_CCRx)的ARE位控制自动重装载,若ARE位置1,则使能自动重装载传输数量,当DMA通道x传输数量寄存器(DMA_CNDTRx)中数值为0时,会自动将DMA_CNDTRx寄存器中的值加载为之前配置的数值;若ARE位置0,则禁止自动重装载传输数量。

数据宽度

DMA的数据宽度配置包含:存储器数据宽度配置与外设数据宽度配置,可独立配置为字节、半字、全字。

存储器数据宽度由DMA通道x配置寄存器(DMA_CCRx)的MSIZE位控制,MSIZE[1:0]为00则数据宽度为8bit,MSIZE[1:0]为01则数据宽度为16bit,MSIZE[1:0]为10则数据宽度为32bit。

外设数据宽度配置由DMA通道x配置寄存器(DMA_CCRx)的PSIZE位控制,可配置为8bit, 16bit或32bit,MSIZE位与PSIZE位如图2所示。

wKgaomUD7dqAa9wmAADV7C_O_Kk769.png 图2.数据宽度对应位

存储器/外设地址

DMA的地址配置包含:存储器地址的配置与外设地址的配置。

对DMA通道x存储器地址寄存器(DMA_CMARx)进行赋值,从而配置存储器地址,存储器地址可作为数据传输的源或目标。

对DMA通道x外设地址寄存器(DMA_CPARx)进行赋值,从而配置外设地址,外设地址是外设数据寄存器的基地址,作为数据传输的源或目标。

源和目标地址必须根据各自配置的数据传输宽度对齐。

指针增量

指针增量配置:每次传输后指针自动向后递增或保持不变。

操作DMA通道x配置寄存器(DMA_CCRx)的MINC位,若MINC位置1,则DMA配置为存储器地址递增模式,存储器的访问地址可以按照步长累加,不需要每次都去设置访问地址;若MINC位置0,则每次DMA传输固定访问同一个地址。

设置DMA通道x配置寄存器(DMA_CCRx)的PINC位,若PINC位置1,则DMA配置为外设地址递增模式,外设的访问地址可以按照步长累加;若PINC位置0,则每次DMA传输固定访问同一个地址。

外设或存储器配置为增量模式时,下一个要传输的地址将是前一个地址加上步长,步长取决于所选的数据宽度,首个传输的地址存放在DMA通道x外设地址寄存器(DMA_CPARx)/DMA通道x存储器地址寄存器(DMA_CMARx)中。

优先级

仲裁器根据通道请求的优先级来启动外设或存储器的访问,优先处理软件优先级高的请求,当软件优先级相同时,默认编号更低的通道优先处理。每个通道的优先级可在DMA通道x配置寄存器(DMA_CCRx)中的PL位配置,优先级分为四种:最高优先级(PL[1:0]=11)、高优先级(PL[1:0]=10)、中等优先级(PL[1:0]=01)与低优先级(PL[1:0]=00)。

数据传输数量

数据传输数量的最大值为65535,将数据传输数量值赋值到DMA通道x传输数量寄存器(DMA_CNDTRx),每次传输后,DMA_CNDTRx递减,表示剩余多少次DMA传输。

当数值递减为0时,数据全部传输完毕。若对应通道配置为自动重加载模式时,DMA_CNDTRx寄存器中的内容将被自动重新加载为之前配置时的数值。

中断

每个通道支持3种事件标志:DMA半传输(HTIF)、DMA传输完成(TCIF)和DMA传输出错(TEIF),各通道单独的中断请求由这3种事件标志逻辑或起来。可通过配置DMA通道x配置寄存器(DMA_CCRx)的TCIE位为1使能传输完成中断,配置HTIE位为1使能半传输中断,配置TEIE位为1使能传输错误中断。

实验

本实验演示DMA burst搬运模式的中断行为,配置DMA的传输模式为存储器到存储器模式且传输方向为从存储器读,开启自动重装载,宽度配置为全字,指针递增,优先级为低优先级,设置两数组为DMA数据传输地址,配置传输数量为16。DMA配置完成后,进行DMA数据传输,使能DMA通道,当产生半传输中断,设置半传输标志为true,当产生传输完成中断,设置传输完成标志为true,清除中断标志位。本实验可通过串口调试工具观察数据传输现象,当DMA传输已到一半时串口打印"half",当DMA传输完成时串口打印"done"。

启用外设时钟 enable_clock()

实验使用DMA1,并通过UART串口显示实验结果,因此需要启用DMA与UART的外设时钟。

void enable_clock()
{
    /* Enable UART1 clock. */
    RCC->APB2ENR |= RCC_APB2_PERIPH_UART1;
    /* Enable GPIOA clock. */
    RCC->AHB1ENR |= RCC_AHB1_PERIPH_GPIOA;
    /* Enable DMA1 clock. */
    RCC->AHB1ENR |= RCC_AHB1_PERIPH_DMA1;
}

配置引脚 pin_init()

实验现象通过串口显示,因此配置UART的TX(PA9)与RX(PA10)引脚。

void pin_init()
{
    /* Setup PA9, PA10. */
    GPIOA->CRH  = ~GPIO_CRH_MODE9_MASK;
    GPIOA->CRH |= GPIO_PinMode_AF_PushPull;     /* PA9 multiplexed push-pull output. */
    GPIOA->AFRH  = ~GPIO_AFRH_AFR_MASK;
    GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE9_SHIFT);   /* Use AF1. */

    GPIOA->CRH  = ~GPIO_CRH_MODE10_MASK;
    GPIOA->CRH |= GPIO_PinMode_In_Floating;     /* PA10 floating input. */
    GPIOA->AFRH |= (GPIO_AF_1 << GPIO_CRH_MODE10_SHIFT);    /* Use AF1. */
}

UART初始化 uart_init()

初始化UART,配置时钟频率、波特率、数据长度、停止位、传输模式及是否使用校验。

void uart_init()
{
    /* Clear the corresponding bit to be used. */
    UART1->CCR  = ~( UART_CCR_PEN_MASK | UART_CCR_PSEL_MASK | UART_CCR_SPB0_MASK | UART_CCR_SPB1_MASK | UART_CCR_CHAR_MASK );
    UART1->GCR  = ~( UART_GCR_AUTOFLOWEN_MASK | UART_GCR_RXEN_MASK | UART_GCR_TXEN_MASK );
    /* WordLength. */
    UART1->CCR |= UART_CCR_CHAR_MASK;
    /* XferMode. */
    UART1->GCR |= (UART_XferMode_RxTx << UART_GCR_RXEN_SHIFT);
    /* Setup baudrate, BOARD_DEBUG_UART_FREQ = 48000000u, BOARD_DEBUG_UART_BAUDRATE = 9600u. */
    UART1->BRR = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) / 16u;
    UART1->FRA = (BOARD_DEBUG_UART_FREQ / BOARD_DEBUG_UART_BAUDRATE) % 16u;
    /* Enable UART1. */
    UART1->GCR |= UART_GCR_UARTEN_MASK;
}

DMA初始化 dma_init()

操作DMA_CCRx寄存器,配置输出传输模式、传输方向、外设和存储器的增量模式、外设和存储器的数据宽度、通道优先级和指针增量;操作DMA_CNDTRx寄存器,配置DMA传输数量,DMA传输完成一次,该数值减1,且在DMA传输期间DMA_CNDTRx寄存器不可被写入;操作DMA_CPARx寄存器,配置外设寄存器地址,由于本实验配置DMA传输方向为从存储器读,因此该外设地址在DMA传输时作为目标地址;操作DMA_CMARx寄存器,配置数据存储器的地址,DMA传输时从该存储器地址加载数据。

void dma_init()
{
    uint32_t ccr = 0u;
    ccr |= DMA_CCR_DIR_MASK;  /* Data transmission direction is: read from memory. */
         | DMA_CCR_MEM2MEM_MASK;  /* Xfer mode: memory to memory. */
         | DMA_CCR_ARE_MASK;   /* Enable automatic reloading. */
         | DMA_CCR_PINC(DMA_AddrIncMode_IncAfterXfer)  /* DMA_AddrIncMode_IncAfterXfer=1u, peripheral increment mode. */
         | DMA_CCR_MINC(DMA_AddrIncMode_IncAfterXfer)  /* Memory increment mode. */
         | DMA_CCR_PSIZE(DMA_XferWidth_32b)  /* DMA_XferWidth_32b = 2u, peripheral size 32 bits. */
         | DMA_CCR_MSIZE(DMA_XferWidth_32b)  /* Memory size 32 bits. */
         | DMA_CCR_PL(DMA_Priority_Low)  /* DMA_Priority_Low = 0u, low priority. */
         ;
    DMA1->CH[0].CCR = ccr;  /* channel number = 0 refer to DMA1_CH1. */
    DMA1->CH[0].CNDTR = DMA_BUFF_COUNT;  /* DMA_BUFF_COUNT = 16u, data transmission quantity. */
    DMA1->CH[0].CPAR = (uint32_t)app_dma_buff_to;  /* Set arry app_dma_buff_to as peripheral address. */
    DMA1->CH[0].CMAR = (uint32_t)app_dma_buff_from;  /* Set arry app_dma_buff_from as memory address. */
}

使能DMA enable_dma()

操作DMA_CCRx寄存器的ENABLE位,使能通道1,在通道使能后可进行DMA传输。

void enable_dma()
{
    DMA1->CH[0].CCR |= DMA_CCR_EN_MASK;  /* Enable DMA1 channel 1. */
}

使能中断 enable_interrupt()

操作DMA_CCRx寄存器的TCIE位置1,使能传输完成中断;HTIE位置1,使能半传输中断。

void enable_interrupt()
{
    DMA1->CH[0].CCR |= (DMA_CHN_INT_XFER_HALF_DONE   0xEu);  /* DMA half transfer interrupt. */
    DMA1->CH[0].CCR |= (DMA_CHN_INT_XFER_DONE   0xEu);  /* DMA end of transfer interrupt. */
}

配置NVIC NVIC_EnableIRQ()

使用Cortex-M0 core_cm0.h头文件中的NVIC_EnableIRQ使能中断,由于DMA初始化时配置使用通道1,因此使用DMA1通道1全局中断DMA1_CH1_IRQn.

NVIC_EnableIRQ(DMA1_CH1_IRQn)

编写中断服务程序DMA1_CH1_IRQHandler()

读DMA中断状态寄存器(DMA_ISR)获取当前中断状态,当产生半传输中断,令定义的半传输标志app_dma_xfer_half_done为true;当产生传输完成中断,令定义的传输完成标志app_dma_xfer_done为true。操作DMA中断标志清除寄存器(DMA_IFCR)对应位置1,清除对应中断。

void DMA1_CH1_IRQHandler()
{
    uint32_t flags = DMA1->ISR   0xFu;

    if (flags   DMA_CHN_INT_XFER_HALF_DONE)  /* DMA half transfer interrupt. */
    {
        app_dma_xfer_half_done = true;
    }

    if (flags   DMA_CHN_INT_XFER_DONE)  /* DMA end of transfer interrupt. */
    {
        app_dma_xfer_done = true;
    }

    DMA1->IFCR = (flag   0xFu);  /* Clear interrupt. */
}

main()函数

main()函数结合上述操作,初始化DMA,设置变量app_dma_xfer_done为传输完成标志,设置变量app_dma_xfer_half_done为DMA已传输一半的标志,设置数组app_dma_buff_from[]为源,即DMA传输时从该存储器地址加载数据,设置数组app_dma_buff_to[]为数据存储地址,设置变量DMA_BUFF_COUNT为数据传输数量,传输数量为16,每次传输后,该数值递减,当数值递减为0时,数据传输完毕,配置自动重加载模式,数据传输数量会被自动重新加载为之前配置时的数值。当检测到半传输中断标志产生,串口打印"half",检测到传输完成中断标志产生,串口打印"done"。本实验在串口输入数据时进行DMA传输,将源地址app_dma_buff_from[]中的数据通过DMA传输到app_dma_buff_to[]地址中。实验现象如图3所示。

int main()
{
    uint8_t c;

    enable_clock();
    pin_init();
    uart_init();

    printf("rndma_burst_interrupt example.rn");

    dma_init();
    NVIC_EnableIRQ(DMA1_CH1_IRQn);
    enable_interrupt();

    for (uint32_t i = 0u; i < DMA_BUFF_COUNT; i++)
    {
        app_dma_buff_from[i] = i;
        app_dma_buff_to[i] = 0u;
    }

    while (1)
    {
        c = getchar();
        putchar(c);

        for (uint32_t i = 0u; i < DMA_BUFF_COUNT; i++)
        {
            app_dma_buff_to[i] = 0u;
        }
        app_dma_xfer_done = false;
        app_dma_xfer_half_done = false;

        enable_dma();

        while (!app_dma_xfer_half_done)
        {}
        printf("half.rn");
        app_dma_xfer_half_done = false;        

        while (!app_dma_xfer_done)
        {}
        printf("done.rn");
        app_dma_xfer_done = false;
    }
}

wKgZomUD7eCAUyx_AADBaK6JcXM006.png 图3.实验现象

审核编辑:彭菁

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 存储器
    +关注

    关注

    38

    文章

    7148

    浏览量

    161985
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10442

    浏览量

    206549
  • dma
    dma
    +关注

    关注

    3

    文章

    535

    浏览量

    99019
收藏 人收藏

    评论

    相关推荐

    灵动微课堂 (第204讲) | MM32F0140 DMA 学习笔记

    从一个地址空间复制到另一个地址空间,DMA在数据传输结束后将总线控制权交回CPU。MM32F0140内置5路通用DMA,可以管理设备到存储器、存储器到设备与存储器到存储器的数据传输,每个通道都有专门
    发表于 03-17 19:04

    基于MM32F0140开发板的独立看门狗(IWDG)设计笔记

    1、MM32F0140学习笔记——独立看门狗(IWDG)  独立看门狗(IWDG)的设计初衷是为了检测和解决由软件错误所引起的故障,与窗口看门狗的主要区别在于独立看门狗可以作为一个处于主程序之外,由
    发表于 09-15 16:43

    MM32F0140 DMA学习笔记

    从一个地址空间复制到另一个地址空间,DMA在数据传输结束后将总线控制权交回CPU。MM32F0140内置5路通用DMA,可以管理设备到存储器、存储器到设备与存储器到存储器的数据传输,每个通道都有专门
    发表于 10-12 15:42

    MM32F0140 产品手册(中文版)

    MM32F0140 产品手册(中文版)
    发表于 02-22 18:45 0次下载
    <b class='flag-5'>MM32F0140</b> 产品手册(中文版)

    MM32F0140 勘误表(中文版)

    MM32F0140 勘误表(中文版)
    发表于 02-22 18:47 0次下载
    <b class='flag-5'>MM32F0140</b> 勘误表(中文版)

    MM32F0140 勘误表(英文版)

    MM32F0140 勘误表(英文版)
    发表于 02-22 18:48 0次下载
    <b class='flag-5'>MM32F0140</b> 勘误表(英文版)

    基于MM32F0140的UDS Bootloader学习笔记

    基于MM32F0140的UDS Bootloader学习笔记
    的头像 发表于 10-30 17:11 351次阅读
    基于<b class='flag-5'>MM32F0140</b>的UDS Bootloader<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>

    MM32F0140学习笔记——CRC

    MM32F0140学习笔记——CRC
    的头像 发表于 11-10 18:27 284次阅读
    <b class='flag-5'>MM32F0140</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>——CRC

    MM32F0140 FlexCAN一致性测试 (2)

    MM32F0140 FlexCAN一致性测试 (2)
    的头像 发表于 11-10 18:23 377次阅读
    <b class='flag-5'>MM32F0140</b> FlexCAN一致性测试 (2)

    MM32F0140 FlexCAN一致性测试(1)

    MM32F0140 FlexCAN一致性测试 (1)
    的头像 发表于 11-10 17:50 267次阅读
    <b class='flag-5'>MM32F0140</b> FlexCAN一致性测试(1)

    MM32F0140学习笔记——窗口看门狗(WWDG)

    MM32F0140学习笔记——窗口看门狗(WWDG)
    的头像 发表于 10-27 09:45 319次阅读
    <b class='flag-5'>MM32F0140</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>——窗口看门狗(WWDG)

    MM32F0140学习笔记——FlexCAN 控制器局域网

    MM32F0140学习笔记——FlexCAN 控制器局域网
    的头像 发表于 10-27 09:25 1119次阅读
    <b class='flag-5'>MM32F0140</b><b class='flag-5'>学习</b><b class='flag-5'>笔记</b>——FlexCAN 控制器局域网

    MM32F0140 SPI学习笔记

    MM32F0140 SPI学习笔记
    的头像 发表于 09-26 16:51 352次阅读
    <b class='flag-5'>MM32F0140</b> SPI<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>

    MM32F0140 UART学习笔记

    MM32F0140 UART学习笔记
    的头像 发表于 09-26 16:45 414次阅读
    <b class='flag-5'>MM32F0140</b> UART<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>

    MM32F0140 GPIO学习笔记

    MM32F0140 GPIO学习笔记
    的头像 发表于 09-26 16:42 270次阅读
    <b class='flag-5'>MM32F0140</b> GPIO<b class='flag-5'>学习</b><b class='flag-5'>笔记</b>