不到断气不罢休 发表于 2025-1-10 17:58:23

(STM32条记)十二、DMA的根本知识与用法 第三部分

我用的是正点的STM32F103来进行学习,板子和教程是野火的指南者。
之后的这个系列条记开头未标明的话,用的也是这个板子和教程。


三、DMA步伐验证

1、DMA 存储器到存储器模式实验

(1)DMA结构体解释

typedef struct
{
        uint32_t DMA_PeripheralBaseAddr;         // 外设地址
        uint32_t DMA_MemoryBaseAddr;                 // 存储器地址
        uint32_t DMA_DIR;                                         // 传输方向
        uint32_t DMA_BufferSize;                         // 传输数目
        uint32_t DMA_PeripheralInc;                 // 外设地址增量模式
        uint32_t DMA_MemoryInc;                         // 存储器地址增量模式
        uint32_t DMA_PeripheralDataSize;         // 外设数据宽度
        uint32_t DMA_MemoryDataSize;                 // 存储器数据宽度
        uint32_t DMA_Mode;                                         // 模式选择
        uint32_t DMA_Priority;                                 // 通道优先级
        uint32_t DMA_M2M;                                         // 存储器到存储器模式
} DMA_InitTypeDef;

[*] DMA_PeripheralBaseAddr:
外设地址,设定 DMA_CPAR 寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为此中一个存储器地址。
[*] DMA_Memory0BaseAddr:
存储器地址,设定 DMA_CMAR 寄存器值;一般设置为我们自界说存储区的首地址。
[*] DMA_DIR:
传输方向选择,可选外设到存储器、存储器到外设。它设定 DMA_CCR 寄存器的DIR 位的值。这里并没有存储器到存储器的方向选择,当利用存储器到存储器时,只必要把此中一个存储器当作外设利用即可。
https://i-blog.csdnimg.cn/direct/02ed9700dc14464a89059768459060b7.png
[*] DMA_BufferSize:
设定待传输数据数目,初始化设定 DMA_CNDTR 寄存器的值。
这个DMA_BufferSize 就是要传输的次数。
这个并不是指字节巨细,而是指DMA的传输次数,传输的字节巨细由DMA_PeripheralDataSize设置。
DMA传输数据时,从DMA_MemoryBaseAddr+0地址开始放,直到DMA_MemoryBaseAddr+DMA_BufferSize-1,此时 DMA_CNDTR 变成0,传输结束。
[*] DMA_PeripheralInc:
如果设置为 DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定 DMA_CCR 寄存器的 PINC 位的值;一般外设都是只有一个数据寄存器,以是一般不会使能该位。
https://i-blog.csdnimg.cn/direct/cc5958ac10934aee94025dd60bbe862b.png
[*] DMA_MemoryInc:
如果设置为 DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定 DMA_CCR 寄存器的 MINC 位的值;我们自界说的存储区一般都是存放多个数据的,以是要使能存储器地址自动递增功能。
https://i-blog.csdnimg.cn/direct/8650e2882f9a404c935c54b7214c4c3c.png
[*] DMA_PeripheralDataSize:
外设数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 PSIZE 位的值。
https://i-blog.csdnimg.cn/direct/91a82232f4034f919edd356e97246824.png
[*] DMA_MemoryDataSize:
存储器数据宽度,可选字节 (8 位)、半字 (16 位) 和字 (32 位),它设定DMA_CCR 寄存器的 MSIZE 位的值。当外设和存储器之间传数据时,双方的数据宽度应该设置为一致巨细。
https://i-blog.csdnimg.cn/direct/f52c977665da4f648208a38e6c028b60.png
[*] DMA_Mode:
DMA 传输模式选择,可选一次传输Normal或者循环传输Circular,它设定 DMA_CCR 寄存器的CIRC 位的值。例程我们的 ADC 收罗是持续循环进行的,以是利用循环传输模式。
https://i-blog.csdnimg.cn/direct/673bc665850c4f739581fc7250ec737c.png
[*] DMA_Priority:
软件设置通道的优先级,有 4 个可选优先级分别为非常高、高、中和低,它设定 DMA_CCR 寄存器的 PL 位的值。DMA 通道优先级只有在多个 DMA 通道同时利用时才故意义,如果是单个通道,优先级可以任意设置。
https://i-blog.csdnimg.cn/direct/6614673598a040999a043fa0cd8a4e4f.png
[*] DMA_M2M:
存储器到存储器模式,利用存储器到存储器时用到,设定 DMA_CCR 的位 14 MEN2MEN 即可启动存储器到存储器模式。
https://i-blog.csdnimg.cn/direct/7db666b0a8e24b8289061d036dd06989.png
(2)DMA结构体初始化

                DMA_InitTypeDef DMA_InitStructure;

                // 开启DMA时钟
                RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
                // 源数据地址
            DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)ShuZu_YouShu;        //数组有32个数,每个数都是一个字,且使用const定义(存储在FLASH)
                // 目标地址
                DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)ShuZu_MeiShu;        //数组有32个数,每个数都是一个字
                // 方向:外设到存储器(这里的外设是内部的FLASH)       
                DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;
                // 传输大小       
                DMA_InitStructure.DMA_BufferSize                         = 32;                                                //因为数组有32个数,所以设32
                // 外设(内部的FLASH)地址递增          
                DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Enable;
                // 内存地址递增
                DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
                // 外设数据单位       
                DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Word;        //因为每个数大小都是一个字,所以宽度设为Word
                // 内存数据单位
                DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Word;       //因为每个数大小都是一个字,所以宽度设为Word
                // DMA模式,一次或者循环模式
                DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal ;                        //一次传输模式
                //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
                // 优先级:高       
                DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
                // 使能内存到内存的传输
                DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Enable;                        //因为本次是FLASH传输到SRAM,所以是存储器到存储器
                // 配置DMA通道                  
                DMA_Init(DMA_CHANNEL, &DMA_InitStructure);
            //清除DMA数据流传输完成标志位
            DMA_ClearFlag(DMA_FLAG_TC);
            // 使能DMA
                DMA_Cmd(DMA_CHANNEL,ENABLE);
此中,

[*]需开启DMA时钟才能利用DMA功能:
// 开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);

[*]ShuZu_YouShu界说如下:
uint32_t ShuZu_YouShu= {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
                                                        0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
                                                        0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
                                                        0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
                                                        0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
                                                        0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
                                                        0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
                                                        0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};

[*]需清除 DMA 标记位后再利用DMA功能,防止不须要的干扰:
//清除DMA数据流传输完成标志位
    DMA_ClearFlag(DMA_FLAG_TC);
(3)步伐验证

DMA传输过后,在DEBUG界面可以看到,ShuZu_YouShu的数字传输到了ShuZu_MeiShu中
https://i-blog.csdnimg.cn/direct/8457e57f379642e5b9826a411a763675.png
2、DMA 存储器到外设模式实验

具体实举动 将数组中的数字 传输至 串口
(1)串口初始化

   代码解释均放入注释中,正文不再复述
/**
* @briefUSART GPIO 配置,工作参数配置
* @param无
* @retval 无
*/
void USART_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;

        // 打开串口GPIO的时钟
        DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
       
        // 打开串口外设的时钟
        DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

        // 将USART Tx的GPIO配置为推挽复用模式
        GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

// 将USART Rx的GPIO配置为浮空输入模式
        GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
       
        // 配置串口的工作参数
        // 配置波特率 为 115200
        USART_InitStructure.USART_BaudRate = 115200;
        // 配置 数据字长 为 8
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        // 配置 停止位 为 1
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        // 配置 校验位 为 0
        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(DEBUG_USARTx, &USART_InitStructure);       
       
        // 使能串口
        USART_Cmd(DEBUG_USARTx, ENABLE);          
}
(2)DMA结构体初始化

由于数据是从存储器到串口,以是设置存储器为源地址,串口的数据寄存器为目标地址。
要发送的数据有很多且都先存储在存储器中,则存储器地址指针递增;串口数据寄存器只有一个,则外设地址地址不变。
双方数据单元设置成一致,传输模式可选一次或者循环传输,只有一个 DMA 哀求,优先级任意设。
最后调用 DMA_Init 函数把这些参数写到 DMA 的寄存器中,然后使能 DMA开始传输。
void USARTx_DMA_Config(void)
{
                DMA_InitTypeDef DMA_InitStructure;
       
                // 开启DMA时钟
                RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
                // 设置DMA源地址:串口数据寄存器地址*/
                DMA_InitStructure.DMA_PeripheralBaseAddr         = USART_DR_ADDRESS;
                // 内存地址(要传输的变量的指针)
                DMA_InitStructure.DMA_MemoryBaseAddr                 = (u32)ShuZu_YouShu;
                // 方向:从内存到外设       
                DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;
                // 传输数量
                DMA_InitStructure.DMA_BufferSize                         = 5000;                // 传输数量        设为 5000,这个数随便设,只要不超过数组大小就可
                // 外设地址不增          
                DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
                // 内存地址自增
                DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
                // 外设数据单位       
                DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;        //因为每次传输1bit,所以设为Byte
                // 内存数据单位
                DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;               //因为每次传输1bit,所以设为Byte
                // DMA模式,一次或者循环模式
                DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal ;                                //一次传输模式
                //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;       
                // 优先级:中       
                DMA_InitStructure.DMA_Priority                                 = DMA_Priority_Medium;
                // 禁止内存到内存的传输
                DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
                // 配置DMA通道                  
                DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);               
                // 使能DMA
                DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);
}
此中,

[*]外设地址DMA_PeripheralBaseAddr为串口地址(需查阅开发手册),即:
https://i-blog.csdnimg.cn/direct/aff3fcbebe6849588530338bfba20adc.png
https://i-blog.csdnimg.cn/direct/6da4dacd896942fcab1aebe8536af115.png
// 外设寄存器地址
#define         PERIPH_BASE         ((uint32_t)0x40000000)
#define         APB2PERIPH_BASE   (PERIPH_BASE + 0x10000)
#define        USART1_BASE         (APB2PERIPH_BASE + 0x3800)
#define        USART_DR_ADDRESS    (USART1_BASE+0x04)

[*]ShuZu_YouShu数组添补为 A-Z共26个字母,再加一个回车:
/*填充将要发送的数据*/
for(i=0; i < 5000; i++)
{
    ShuZu_YouShu       = 'A'+ i%27;
    if(ShuZu_YouShu == '[')
        {
                ShuZu_YouShu = '\n';
        }
}
(3)步伐验证

利用USART_DMACmd函数打开USART 的DMA哀求。
/* USART1 向 DMA发出TX请求 */
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
它接收三个参数:


[*]第一个参数用于设置串口外设,可以是 USART1/2/3 和 UART4/5 这 5 个参数可选。
[*]第二个参数设置串口的具体 DMA 哀求,有串口发送哀求 USART_DMAReq_Tx 和接收哀求 USART_DMAReq_Rx 可选。
[*]第三个参数用于设置启动哀求 ENABLE 或者关闭哀求 DISABLE。
运行该函数后 USART 的DMA 发送传输就开始了,根据设置存储器的数据会发送到串口。
验证结果如下:
https://i-blog.csdnimg.cn/direct/91e4cc2a7536448c84bd37d2d83285ef.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: (STM32条记)十二、DMA的根本知识与用法 第三部分