所谓的DMA指的是:直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
两个DMA控制器有12个通道(DMA1有7个通道,DMA2有5个通道),每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
简单的来说,DMA功能指的就是,不需要程序控制,单片机内部自己可以控制数据从一个地方到另一个地方的传输。下面直接通过代码来看DMA功能如何实现。
#include "dma.h"
u16 DMA1_MEM_LEN; //保存DMA每次数据传送的长度
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量
void MYDMA_Config(DMA_Channel_TypeDef *DMA_Chx, u32 cpar, u32 cmar, u16 cndtr)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
DMA_DeInit(DMA_Chx); //将DMA的通道1寄存器重设为缺省值
DMA1_MEM_LEN = cndtr;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设基地址
DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_Init(DMA_Chx, &DMA_InitStructure);
}
void MYDMA_Enable(DMA_Channel_TypeDef* DMA_CHx)
{
DMA_Cmd(DMA_CHx,DISABLE);
DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);
DMA_Cmd(DMA_CHx,ENABLE);
}
这里使用了一个通用的DMA初始化,没有具体说明数据是从哪个内存发送到哪个外设,当哪个外设需要使用DMA功能时,在初始化的时候设置外设地址和内存地址就可以了。比如这里要使用串口发送的DMA功能。
#define SEND_BUF_SIZE 8200 //发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍.
u8 SendBuff[SEND_BUF_SIZE];
const u8 TEXT_TO_SEND[] = {"DMA 串口实验,缓冲区的数据,用DMA功能,通过串口2发送出来。"};
int main(void)
{
u16 i;
u8 t = 0;
u8 j, mask = 0;
float pro = 0;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
UART2_Init(115200);
LED_Init();
KEY_Init();
//MYDMA_Config(DMA1_Channel4, (u32)&USART1->DR, (u32)SendBuff, SEND_BUF_SIZE); //DMA1通道4,外设为串口1,存储器为SendBuff,长度SEND_BUF_SIZE.
MYDMA_Config(DMA1_Channel7, (u32)&USART2->DR, (u32)SendBuff, SEND_BUF_SIZE); //DMA1通道7,外设为串口2,存储器为SendBuff,长度SEND_BUF_SIZE.
j = sizeof(TEXT_TO_SEND);
for(i = 0; i < SEND_BUF_SIZE; i++) //填充数据到SendBuff
{
if(t >= j) //加入换行符
{
if(mask)
{
SendBuff[i] = 0x0a;
t = 0;
}
else
{
SendBuff[i] = 0x0d;
mask++;
}
}
else
{
mask = 0;
SendBuff[i] = TEXT_TO_SEND[t];
t++;
}
}
printf("DMA test!!!rn");
while(1) //串口1打印信息 通过DMA功能将SendBuff中的数据,通过串口2发送出来
{
t = KEY_Sacn(0);
if(t == KEY1_PRES)
{
printf("Start Transimit....");
printf("rnDMA DATA:rn");
//串口1 DMA 发送
// USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE); //使能串口1的DMA发送
// MYDMA_Enable(DMA1_Channel4); //开始一次DMA传输
//串口2 DMA 发送
USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); //使能串口1的DMA发送
MYDMA_Enable(DMA1_Channel7); //开始一次DMA传输
//等待DMA传输完成,此时我们来做另外一些事,点灯
//实际应用中,传输数据期间,可以执行另外的任务
while(1)
{
//串口1 DMA 判断
// if(DMA_GetFlagStatus(DMA1_FLAG_TC4) != RESET) //判断通道4传输完成
// {
// DMA_ClearFlag(DMA1_FLAG_TC4); //清除通道4传输完成标志
// break;
// }
//串口2 DMA 判断
if(DMA_GetFlagStatus(DMA1_FLAG_TC7) != RESET) //判断通道7传输完成
{
DMA_ClearFlag(DMA1_FLAG_TC7); //清除通道7传输完成标志
break;
}
//pro = DMA_GetCurrDataCounter(DMA1_Channel4);
pro = DMA_GetCurrDataCounter(DMA1_Channel7);
pro = 1 - pro / SEND_BUF_SIZE;
pro *= 100;
printf("%f %%rn", pro);
}
printf("100%%");
printf("rnTransimit Finished!rn");
}
i++;
delay_ms(10);
if(i == 20)
{
LED0 = !LED0;
i = 0;
}
}
}
在初始化的时候,将串口数据发送寄存器作为外设地址,将存储发送数据的地址作为内存地址。数据的传输方向是从内存读取数据然后发送到外设地址中去。也就是一旦开启DMA传输功能之后,单片机就会自动将数组中的数据依次传输到串口发送数据寄存器中。这样数据就自动从串口发送出来了。不需要程序控制。
这里分别使用串口1和串口2进行了测试,每按一次按键,就启动一次DMA数据传输。在传输数据的过程中模拟程序执行其他操作。当DMA数据传输完成后,会打印出当前传输了多少字节的数据。
上一篇:STM32F103单片机使用DMA功能读取ADC采样数据
下一篇:STM32F103使用DAC功能输出正弦波
推荐阅读最新更新时间:2024-11-07 10:55
推荐帖子
- uClinux BOA SERVER CGI 支持中文吗?
- 我在用CGI返回中文时printf(中文\\n);浏览器在该行显示Invalidmultibyteformatstring在网上搜了一下有一种说法如下在MIME头里加上对字符集charset:gb2312现在我的MIME头如下:printf(Content-type:text/html\\ncharset:gb2312\\nPragma:no-cache\\n\\n);仍然是Invalidmultibyteformatstring。于是我想应该是我的u
- zengliwen1987 Linux与安卓
- 隔离变压器是一种1比1的变压器
- 隔离变压器是一种1比1的变压器隔离变压器是一种1比1的变压器keyiRe:隔离变压器是一种1比1的变压器xiexiele回复:隔离变压器是一种1比1的变压器顶,好东西呀!回复:隔离变压器是一种1比1的变压器怎么下载不了啊回复:隔离变压器是一种1比1的变压器看看再说.........................回复:隔离变压器是一种1比1的变压器
- wangqingtao 电源技术
- 精彩贴片集成电路的焊接图文教程
- 本帖最后由qwqwqw2088于2014-5-1511:03编辑 焊接心得作为一名电子工程师,如果不会拿烙铁焊接,真的说不过去。而现在很多年青的工程师确确实实在忽略这方面动手能力的培养或很少有机会自己焊接板子,心里只想着学ARM,学Linux,而换个电阻、电容有时都要找焊接工人,更不用提TSOP等密集型的贴片IC了。所以在此希望我们大伙都重视这些基础方便的训练,自己能焊的尽量自己焊,学着焊,如果有条件可以多请教那些焊接工人,他们都会有自己的技巧和心得。
- qwqwqw2088 PCB设计
- Nand Flash中的特殊硬件结构
- 由于nandflash相对其他常见设备来说,比较特殊,所以,特殊的设备,也有特殊的设计,所以,有些特殊的硬件特性,就有比较解释一下:1.页寄存器(PageRegister):由于NandFlash读取和编程操作来说,一般最小单位是页,所以,nandflash在硬件设计时候,就考虑到这一特性,对于每一片,都有一个对应的区域,专门用于存放,将要写入到物理存储单元中去的或者刚从存储单元中读取出来的,一页的数据,这个数据缓存区,本质上就是一个buffer,但是只是名字叫法不同,datas
- 13691982107 医疗电子
- 小工具解放双手的年代
- 这就是一张图片涵盖了射频工作者的用心都是免费的数据准不准需要自己验证小工具解放双手的年代总结的很详细,都是一些很实用的设计工具,工程师都经常要用的. 干我们这个必须借助工具啊不然头发掉得快是呀,有工具可以提高工作效率,明确方向,减少错误. 你也做这方面工作吗 一起交流分享学习讨论这个还得归功于写代码的隔空感谢码代码的兄弟
- btty038 RF/无线
- 【DIY创意LED】WS2812效果显示文件
- 这次将LED的效果显示单独拿出来,可以编写各种效果,就看大家的创造力了。一个效果文件的基本程序结构如下(这是一个红色呼吸灯的效果):fromcommonimport*defrun():clear(np)n=0while1:clear(np,(abs(32-n),0,0))n=(n+1)%64sleep_ms(100) 每个效果文件可以impo
- dcexpert 单片机
设计资源 培训 开发板 精华推荐
- LT3467AES67 ±15V 双路输出升压转换器的典型应用,具有输出断开连接
- 【创意PCB】pcb灯笼
- DC814A-G,使用 LTC6905-80 17MHz 至 170MHz 固定频率硅振荡器的演示板
- 具有固定输出电压和精密使能引脚的 ADP5024 降压稳压器的典型应用电路
- LT8570EMS8E-1 700kHz、-15V 输出反相转换器的典型应用接受 5 至 35V 输入
- LTC2992IMS 双向 30V 至 300V 高侧功率监视器的典型应用
- 伏虎单手摇杆
- LM1875功放板
- LT6656BIS6-3.3、3.3V 低功率 ADC 电压基准的典型应用
- ADR395A 5 Vout 微功率、低噪声精密电压基准的典型应用
- 报名抽取京东卡|2021慕尼黑华南电子展观众注册进行中
- Mouser&TE直播:解锁智能家居部件新技术,探索连接黑科技
- 如何用3个关键步骤,来确保下一代设计安全性,深入解读嵌入式设备DeepCover加密控制器,看视频答题赢好礼!
- 追更有惊喜:解救被FSM折磨过的你,justd0解析LSM6DSOX有限状态机官方例程
- 【ST板卡来了(下)】 品读STM32实战经验,汲取FAE经验闯关去抽奖
- “搜器件”小程序又添新功能!
- 跟帖免费申请UFUN学习板开始啦,跟着张进东学习&进阶模拟、工程技能
- 直播【英飞凌应用于变频家电和中小功率工业变频控制领域的产品:iMOTION™】
- 预约有礼:预约理想配电网络的电源完整性设计等直播,涨知识赢好礼