MM32F0140 DMA学习笔记

dma简介
dma全称为direct memory access,即直接存储器访问。在进行dma传输前,cpu将总线控制权交给dma,通过共享系统总线,实现无需cpu参与的快速数据传输,能够直接将数据从一个地址空间复制到另一个地址空间,dma在数据传输结束后将总线控制权交回cpu。
mm32f0140内置5路通用dma,可以管理设备到存储器、存储器到设备与存储器到存储器的数据传输,每个通道都有专门的硬件dma请求逻辑,也可以通过软件配置来触发每个通道。
mm32f0140的dma模块所支持的外设类型包括uart、i2c、spi、adc与通用、高级和基础定时器,当dma从外设产生的请求通过逻辑或输入dma控制器时,为避免冲突,在一个通道中,同时只能有一个外设dma请求有效,详细各通道dma请求如图1所示。
图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所示。
图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 crh |= gpio_pinmode_in_floating; /* pa10 floating input. */ gpioa->afrh |= (gpio_af_1 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 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; }}  
图3.实验现象


微雪电子IMX179超清USB摄像头简介
车用内存:区别质量与功能安全为何如此重要
AMAZINGIC晶焱简述静电防护议题与保护方案
安徽移动完成GSM/NB-IoT动态频谱共享测试验证,实现多载波动态扩容
一文理解相位补偿
MM32F0140 DMA学习笔记
解读三星危机文化:被“居安思危”毒害了?!
工业半导体市场排名_ADI上升至第二
罗德与施瓦茨将出席伦敦展会 世健获易德龙“21年优秀供应商”称号
遥控电扇系统红外遥控发射电路原理图
ST与伟创力合作推出零空载功耗充电平台
CD4053的原理与主要作用
rfid如何协助保障矿山的安全
实现一个双端队列的步骤简析
基于SPCE061A单片机的信号分析系统的总体设计
使用万用电表简单准确地测量电
真黑科技!伪装成电灯泡的智能安防相机
在115200波特率下想发送800个字符需要多少时间呢?
iPhone 12真的值得入手吗?
3D封装技术开始成为巨头角逐的重要战场