【DMA】存储器到外设器模式实验
文章目录
- 前言
- 一、硬件设计
- 二、软件设计
- main.c
- bsp_led.c
- bsp_led.h
- bsp_debug_usart.c
- bsp_debug_usart.h
- bsp_dma.c
- bsp_dma.h
- Delay.c
- Delay.h
- 三、实现效果
- 四、过程错误总结
- 223-D: function "USART_DMA_Config" declared implicitlyUSART_DMA_Config();....Usermain.c(20): error: #20: identifier "SENDBUFF_SIZE" is undefined
前言
DMA 存储器到外设传输模式非常方便把存储器数据传输外设数据寄存器中,这在 STM32 芯片
向其他目标主机,比如电脑、另外一块开发板或者功能芯片,发送数据是非常有用的。 RS-232 串
口通信是我们常用开发板与 PC 端通信的方法。我们可以使用 DMA 传输把指定的存储器数据转
移到 USART 数据寄存器内,并发送至 PC 端,在串口调试助手显示。
一、硬件设计
存储器到外设模式使用到 USART1 功能,具体电路设置参考 USART 章节,无需其他硬件设计。
二、软件设计
main.c
#include "stm32f4xx.h"
#include "stdio.h"
#include "bsp_led.h" //注意这里的目录结构
#include "Delay.h"
#include "bsp_dma.h"
#include "bsp_debug_usart.h"
int main(void)
{
/*定义该变量用于接收比较值*/
uint16_t i;
Debug_USART_Config();
USART_DMA_Config();
LED_Config();
printf("
USART1 DMA TX测试
");
/*填充将要发送的数据*/
for(i = 0; i < SENDBUFF_SIZE; i++)
{
SendBuff[i] = 'A';
}
/*USART1 向 DMA 发出TX请求*/
USART_DMACmd(DEBUG_USART, USART_DMAReq_Tx, ENABLE);
/*此时CPU是空闲的,可以干其他事情
例如同时控制LED*/
while(1)
{
LED1_TOGGLE;
Delay_ms(1000);
}
}
/*********************************************END OF FILE**********************/
bsp_led.c
#include
#include "bsp_led.h"
void LED_Config(void)
{
//注意:所以变量必须放在代码块开头
GPIO_InitTypeDef GPIO_InitStructure;
/*打开时钟*/
RCC_AHB1PeriphClockCmd( LED1_GPIO_CLK|LED2_GPIO_CLK|LED3_GPIO_CLK, ENABLE);
/*配置GPIO结构体
引脚,模式,速率
*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = LED1_PIN ;
GPIO_Init(LED1_GPIO_PORT , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED2_PIN ;
GPIO_Init(LED2_GPIO_PORT , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED3_PIN;
GPIO_Init(LED3_GPIO_PORT , &GPIO_InitStructure);
/*关闭RGB*/
LED_RGBOFF;
}
bsp_led.h
#include
#include "bsp_led.h"
void LED_Config(void)
{
//注意:所以变量必须放在代码块开头
GPIO_InitTypeDef GPIO_InitStructure;
/*打开时钟*/
RCC_AHB1PeriphClockCmd( LED1_GPIO_CLK|LED2_GPIO_CLK|LED3_GPIO_CLK, ENABLE);
/*配置GPIO结构体
引脚,模式,速率
*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = LED1_PIN ;
GPIO_Init(LED1_GPIO_PORT , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED2_PIN ;
GPIO_Init(LED2_GPIO_PORT , &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LED3_PIN;
GPIO_Init(LED3_GPIO_PORT , &GPIO_InitStructure);
/*关闭RGB*/
LED_RGBOFF;
}
bsp_debug_usart.c
#include "stm32f4xx.h"
#include "bsp_debug_usart.h"
#include "bsp_dma.h"
/*为什么没有报错但是有红色提示错误?
像 Keil、STM32CubeIDE 这类编辑器,会通过静态代码分析来提前识别潜在问题。
但它的检查逻辑可能不够智能,会把一些合法代码误判为错误。*/
uint8_t SendBuff[SENDBUFF_SIZE];
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Debug_USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
/*使能USART GPIO时钟*/
RCC_APB2PeriphClockCmd(DEBUG_USART_DMA_CLK, ENABLE);
/*连接PXx到USART_TX*/
GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT, DEBUG_USART_TX_SOURCE, DEBUG_USART_TX_AF);
/*连接PXx到USART_RX*/
GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT, DEBUG_USART_RX_SOURCE, DEBUG_USART_RX_AF);
/*GPIO初始化*/
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*配置TX9为复用*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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( DEBUG_USART, &USART_InitStructure);
/*嵌套向量中断控制器NVIC配置*/
NVIC_Configuration();
/*USART_IT_RXNE:接收数据寄存器不为空触发中断*/
USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);
/*使能串口*/
USART_Cmd(DEBUG_USART, ENABLE);
}
void USART_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitSturcture;
/*开启DMA时钟*/
RCC_AHB1PeriphClockCmd (DEBUG_USART_CLK, ENABLE);
/*复位初始化DMA数据流*/
DMA_DeInit(DEBUG_USART_DMA_STREAM);
/*确保DMA数据流复位完成*/
while(DMA_GetCmdStatus (DEBUG_USART_DMA_STREAM) != DISABLE)
{}
/*程序运行到这里的时候,DMA2的Stream7已经复位完成
usart1 tx对应dma2,通道4,数据流7
数据源:串口数据寄存器地址
挑选存储器的基地址给数据流
*/
DMA_InitSturcture.DMA_Channel = DEBUG_USART_DMA_CHANNEL;
DMA_InitSturcture.DMA_PeripheralBaseAddr = DEBUG_USART_DR_BASE;
DMA_InitSturcture.DMA_Memory0BaseAddr = (uint32_t)SendBuff;
DMA_InitSturcture.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitSturcture.DMA_BufferSize = SENDBUFF_SIZE;
DMA_InitSturcture.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitSturcture.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitSturcture.DMA_Mode = DMA_Mode_Circular;
DMA_InitSturcture.DMA_Priority = DMA_Priority_Medium;
DMA_InitSturcture.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitSturcture.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitSturcture.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitSturcture.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DEBUG_USART_DMA_STREAM, &DMA_InitSturcture);
DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE);
while(DMA_GetCmdStatus(DEBUG_USART_DMA_STREAM) != ENABLE)
{}
/*执行到这里DMA数据流有效*/
}
bsp_debug_usart.h
#ifndef BSP_DEBUG_USART_H
#define BSP_DEBUG_USART_H
#include
/*串口号*/
#define DEBUG_USART USART1
/*时钟总线,波特率*/
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_BAUDRATE 115200
/*时钟总线,波特率*/
#define DEBUG_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_PIN GPIO_Pin_9
#define DEBUG_USART_TX_AF GPIO_AF_USART1
#define DEBUG_USART_TX_SOURCE GPIO_PinSource9
#define DEBUG_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_PIN GPIO_Pin_10
#define DEBUG_USART_RX_AF GPIO_AF_USART1
#define DEBUG_USART_RX_SOURCE GPIO_PinSource10 //引脚源:实现外设与GPIO绑定
/*DMA*/
#define DEBUG_USART_DR_BASE (USART1_BASE + 0x04)
#define SENDBUFF_SIZE 5000
#define DEBUG_USART_DMA_CLK RCC_AHB1Periph_DMA2
#define DEBUG_USART_DMA_CHANNEL DMA_Channel_4
#define DEBUG_USART_DMA_STREAM DMA2_Stream7
/*数据源*/
void NVIC_Configuration(void);
void Debug_USART_Config(void);
void USART_DMA_Config(void);
extern uint8_t SendBuff[SENDBUFF_SIZE];
#endif /*BSP_DEBUG_USART_H*/
bsp_dma.c
#include
#include "bsp_dma.h"
#include "Delay.h"
#include "bsp_led.h"
/*定义DMA数据传输源,使用const使其存储在内部flash上*/
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE] =
{
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传输目标存储器*/
uint32_t aDST_Buffer[BUFFER_SIZE];
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
/*定义一个超时数字*/
__IO uint32_t timeout = TIMEOUT_MAX;
/*使能DMA时钟*/
RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);
/*复位DMA1/DMA2寄存器到初始值*/
DMA_DeInit(DMA_STREAM);
/*确保数据流复位成功*/
while(DMA_GetCmdStatus(DMA_STREAM) != DISABLE )
{
}
/*选择通道:需参考DMA通道映射表*/
DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
/*选择源数据地址*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
/*选择目标数据地址*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
/*传输方向选择*/
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
/*数据数目*/
DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;
/*使能自动递增功能*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
/*使能自动递增功能*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*源数据是字大小 1字 = 4字节 = 32位*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
/*DMA模式:单次发送和循环发送,存储器到存储器之间不能使用循环模式*/
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*DMA优先级:多个DMA数据流才有意义,我们这里只有一个设置成最高优先级也可以*/
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
/*DMA的FIFO寄存器模式:直接传输或者FIFO模式*/
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
/*DMA的FIFO阈值*/
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*DMA的存储器突发模式:直接传输直接选择单次*/
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*DMA的外设突发:直接传输直接选择单次*/
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*完成DMA参数配置*/
DMA_Init(DMA_STREAM, &DMA_InitStructure);
// /*DMA使能*/
DMA_ClearFlag( DMA_STREAM, DMA_FLAG_TCIF);
/*使能DMA数据流,开始DMA传输数据*/
DMA_Cmd(DMA_STREAM, ENABLE);
/*检测DMA数据流是否有效并带有超时检测功能*/
timeout = TIMEOUT_MAX;
while((DMA_GetCmdStatus(DMA_STREAM) == DISABLE) && (timeout-- > 0))
{}
if(timeout == 0)
{
while(1)
{
LED_RED;
Delay_ms(1000);
LED_RGBOFF;
Delay_ms(1000);
}
}
}
uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength)
{
while(BufferLength--)
{
//判断两个数据源是否相等
if(*pBuffer != *pBuffer1)
{
//不相等返回0
return 0;
}
pBuffer++;
pBuffer1++;
}
/*完成判断且数据相对*/
return 1;
}
bsp_dma.h
#ifndef BSP_DMA_H
#define BSP_DMA_H
#include
#include "bsp_dma.h"
#define BUFFER_SIZE 32
#define TIMEOUT_MAX 10000
#define DMA_STREAM DMA2_Stream0
#define DMA_CHANNEL DMA_Channel_0
#define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF DMA_FLAG_TCIF0
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
extern uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength);
void DMA_Config(void);
#endif /*BSP_DMA_H*/
Delay.c
#include
#include "Delay.h"
/**
* @brief 软件延时函数(毫秒级)
* @param ms: 要延时的毫秒数(基于168MHz系统时钟校准)
* @note 该函数仅适用于STM32F407系统时钟为168MHz的情况,若时钟频率不同需调整循环次数
*/
void Delay_ms(unsigned ms)
{
uint32_t i, j;
for (i = 0; i < ms; i++)
{
// 循环次数校准:168MHz下,该循环约1毫秒
for (j = 0; j < 18000; j++);
}
}
/**
* @brief 软件延时函数(微秒级)
* @param us: 要延时的微秒数(基于168MHz系统时钟校准)
*/
void Delay_us(unsigned us)
{
uint32_t i;
for (i = 0; i < us * 18; i++); // 168MHz下,该循环约1微秒
}
Delay.h
#ifndef DELAY_H
#define DELAY_H
void Delay_ms(unsigned ms);
void Delay_us(unsigned us);
#endif
三、实现效果
效果没问题!!!
四、过程错误总结
223-D: function “USART_DMA_Config” declared implicitlyUSART_DMA_Config();…Usermain.c(20): error: #20: identifier “SENDBUFF_SIZE” is undefined
没有找到宏定义,找了半天,首先错误路径包含问题都没问题,最后发现我从别的文件复制过来的bsp_debug_usart.c和.h文件导入时,直接导入到目录下,但在ide里进行修改会改变源文件中的.c和.h,而对本文件.c和.h文件没有进行修改,重新复制粘贴覆盖文件即可









