TI【LP_MSPM0L1306开发板】测评——DMA使用以及示例学习
<h1>DMA使用以及示例学习</h1><div>DMA(Direct Memory Access)作为嵌入式控制器中标准外设,在源地址和目标地址之间传递数据,过程不需要CPU的干预,这提升了外设模块的数据传输速率。</div>
<div>TI的DMA控制器具有以下特点:</div>
<ul>
<li>最多可达16个独立的传输通道;</li>
<li>可以配置的DMA通道优先级;</li>
<li>支持8位(byte),16位(short word)、32位(word)和64位(long word)或者混合大小(byte 和 word)传输;</li>
<li>支持最大可达64K任意数据类型的数据块传输;</li>
<li>可配置的DMA传输触发源;</li>
<li>6种灵活的寻址模式;</li>
<li>单次或者块传输模式;</li>
</ul>
<div>MSPM0L1306共有3个DMA通道,各个通道可以独立配置,多种多样的数据传输模式可以适应不同应用场景的数据传输需要。</div>
<div>通过查看TI的数据手册,DMA功能除了常见的内存与外设间的地址寻址方式,还提供了Fill Mode和Table Mode两种拓展模式,DMA通道分为基本类型和全功能类型两种。</div>
<div></div>
<div>通过查看数据手册,只有全功能类型的DMA通道支持重复传输、提前中断以及拓展模式,基本功能的DMA通道支持基本的数据传输和中断,但是足够满足简单的数据传输要求。</div>
<h2>Block传输模式</h2>
<div>在Block模式下,数据的传输和常见的DMA传输一样,实现源地址和目标地址之间的数据传输,源地址指针和目标地址指针是否增减、数据的大小和中断都可以配置。</div>
<div>该模式下TI提供的示例程序dma_block_transfer_LP_MSPM0L1306_nortos_ticlang,实现从Flash中的数据到SRAM中的数据传输过程。SysConfig中对DMA的配置如下:</div>
<div></div>
<div>源代码如下,逻辑简单,DMA完成从Flash中传递数据到SRAM中,然后对比数据传输结果是否正确,并置位相关的标志位。</div>
<div>
<pre>
<code class="language-cpp">#include "ti_msp_dl_config.h"
#define DMA_TRANSFER_SIZE_WORDS (16)
const uint32_t gSrcData = {0x00000000, 0x10101010,
0x20202020, 0x30303030, 0x40404040, 0x50505050, 0x60606060, 0x70707070,
0x80808080, 0x90909090, 0xA0A0A0A0, 0xB0B0B0B0, 0xC0C0C0C0, 0xD0D0D0D0,
0xE0E0E0E0, 0xF0F0F0F0};
uint32_t gDstData;
volatile bool gChannel0InterruptTaken = false;
volatile bool gVerifyResult;
int main(void)
{
/*
* Initialize the DMA peripheral and set up a transaction according to
* the parameters defined in ti_msp_dl_config.h
*/
SYSCFG_DL_init();
/* Setup interrupts on device */
DL_SYSCTL_disableSleepOnExit();
NVIC_EnableIRQ(DMA_INT_IRQn);
/* Configure DMA source, destination and size */
DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gSrcData);
DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gDstData);
DL_DMA_setTransferSize(
DMA, DMA_CH0_CHAN_ID, sizeof(gSrcData) / sizeof(uint32_t));
DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
/* Start block transfer */
gChannel0InterruptTaken = false;
DL_DMA_startTransfer(DMA, DMA_CH0_CHAN_ID);
/* Wait for transfer */
while (gChannel0InterruptTaken == false) {
__WFE();
}
gVerifyResult = true;
for (int i = 0; i < DMA_TRANSFER_SIZE_WORDS; i++) {
gVerifyResult &= gSrcData == gDstData;
}
/* Breakpoint to inspect verification result */
__BKPT(0);
while (1) {
__WFI();
}
}
void DMA_IRQHandler(void)
{
/* Example interrupt code -- just used to break the WFI in this example */
switch (DL_DMA_getPendingInterrupt(DMA)) {
case DL_DMA_EVENT_IIDX_DMACH0:
gChannel0InterruptTaken = true;
break;
default:
break;
}
}</code></pre>
<p> </p>
</div>
<div></div>
<div>在CCS中打卡调试窗口,gSrcData中的数据在程序运行前填入了初始值,gDstData初始化为全为0的数组。</div>
<div></div>
<div>程序完成DMA操作后,gSrcData中的数据传输到gDstData中。</div>
<h2>Fill模式</h2>
<div>Fill模式,源地址改为了一个用户指定的数据内容(手册中描述为“pattern”),在该模式下,目标地址中的内容会被修改为用用指定的数据内容,可以用于内存的特定初始化操作。</div>
<div>该模式下TI提供的示例程序dma_fill_data_LP_MSPM0L1306_nortos_ticlang,实现以某个固定数值对目标区内的内存进行初始化操作。SysConfig中对DMA的配置如下:</div>
<div></div>
<div>工程的源码如下,DMA在中断中检测是否完成数据传输,并对其进行校验</div>
<div>
<pre>
<code class="language-cpp">#include "ti_msp_dl_config.h"
#define DMA_FILL_SIZE_WORDS (16)
const uint32_t gFillPattern = 0xF711BEEF;
uint32_t gDstData;
volatile bool gChannel0InterruptTaken = false;
volatile bool gVerifyResult = false;
int main(void)
{
/*
* Initialize the DMA peripheral and set up a transaction according to
* the parameters defined in ti_msp_dl_config.h
*/
SYSCFG_DL_init();
/* Setup interrupts on device */
DL_SYSCTL_disableSleepOnExit();
NVIC_EnableIRQ(DMA_INT_IRQn);
/* Configure DMA source, destination and size */
DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, gFillPattern);
DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gDstData);
DL_DMA_setTransferSize(
DMA, DMA_CH0_CHAN_ID, sizeof(gDstData) / sizeof(uint32_t));
DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
/* Start fill data */
gChannel0InterruptTaken = false;
DL_DMA_startTransfer(DMA, DMA_CH0_CHAN_ID);
/* Wait for transfer */
while (gChannel0InterruptTaken == false) {
__WFE();
}
gVerifyResult = true;
for (int i = 0; i < DMA_FILL_SIZE_WORDS; i++) {
gVerifyResult &= (gDstData == gFillPattern);
}
/* Breakpoint to inspect verification result */
__BKPT(0);
while (1) {
__WFI();
}
}
void DMA_IRQHandler(void)
{
/* Example interrupt code -- just used to break the WFI in this example */
switch (DL_DMA_getPendingInterrupt(DMA)) {
case DL_DMA_EVENT_IIDX_DMACH0:
gChannel0InterruptTaken = true;
break;
default:
break;
}
}</code></pre>
<p> </p>
</div>
<div>在CCS中打卡调试窗口,gFillPattern作为常量初始化为0xF711BEEF(),gDstData初始化为全为0的数组。</div>
<div></div>
<div>程序完成DMA操作后,gSrcData中的数据被初始化为gFillPattern的值。</div>
<h2>Table模式</h2>
<div>在Table模式中,DMA控制器从源地址读取两个数据(第一个数据为写入数据的地址,第二个数据位写入数据的内容),向目标地址写入一个数据。该特点在根据数据表初始化指定数据时,对数据表进行解析时不需要占用CPU的资源,从而节省CPU的资源。</div>
<div>TI提供的dma_table_transfer_LP_MSPM0L1306_nortos_ticlang中,初始化了目标地址和数据组成的表格数组作为源数据,目标地址指向变量再该模式下根据表格中的数据进行初始化。</div>
<div></div>
<div>工程的源码如下,DMA在中断中检测是否完成数据传输,并对其进行校验</div>
<div>
<pre>
<code class="language-cpp">#include "ti_msp_dl_config.h"
#define DMA_TABLE_ENTRIES (4)
volatile bool gChannel0InterruptTaken;
volatile uint32_t gDstData1;
volatile uint32_t gDstData2;
volatile uint32_t gDstData3;
volatile uint32_t gDstData4;
__attribute__((aligned(8)))
const uint32_t gTableData = {(uint32_t) &gDstData1,
0x10101010, (uint32_t) &gDstData2, 0x20202020, (uint32_t) &gDstData3,
0x30303030, (uint32_t) &gDstData4, 0x40404040};
volatile bool gVerifyResult;
int main(void)
{
bool compare = true;
/*
* Initialize the DMA peripheral and set up a transaction according to
* the parameters defined in ti_msp_dl_config.h
*/
SYSCFG_DL_init();
/* Configure DMA source, destination and size */
DL_DMA_setSrcAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTableData);
DL_DMA_setDestAddr(DMA, DMA_CH0_CHAN_ID, (uint32_t) &gTableData);
DL_DMA_setTransferSize(DMA, DMA_CH0_CHAN_ID, DMA_TABLE_ENTRIES);
DL_DMA_enableChannel(DMA, DMA_CH0_CHAN_ID);
/* Setup interrupts on device */
DL_SYSCTL_disableSleepOnExit();
NVIC_EnableIRQ(DMA_INT_IRQn);
gChannel0InterruptTaken = false;
/* Start fill data */
DL_DMA_startTransfer(DMA, DMA_CH0_CHAN_ID);
while (false == gChannel0InterruptTaken) {
__WFE();
}
compare &= (gTableData == gDstData1);
compare &= (gTableData == gDstData2);
compare &= (gTableData == gDstData3);
compare &= (gTableData == gDstData4);
gVerifyResult = compare;
/* Breakpoint to inspect verification result */
__BKPT(0);
while (1) {
__WFI();
}
}
void DMA_IRQHandler(void)
{
/* Example interrupt code -- just used to break the WFI in this example */
switch (DL_DMA_getPendingInterrupt(DMA)) {
case DL_DMA_EVENT_IIDX_DMACH0:
gChannel0InterruptTaken = true;
break;
default:
break;
}
}
</code></pre>
<p> </p>
</div>
<div>在CCS中打卡调试窗口,gTableData数组为指定的目标地址和数据的组合,gDstDatan(n=1,2,3,4)为DMA数据传输的目标变量。</div>
<div></div>
<div>再数据传输完成后,可以看到数据被成功初始化为表格中指定的数据。</div>
<h2>总结</h2>
<div>TI的DMA控制器支持基本寻址和扩展寻址方式,给用户设计应用程序提供了更多的灵活性。TI提供的DMA功能示例演示了在某个寻址地址模式下的数据传输示例,基于这些例子,在使用别的外设时作为参考,实现数据的高效传递。</div>
<p>感谢楼主提供的技术分享,先收藏学习再发表个人意见,顶起来</p>
<p>学习一下,感谢楼主的分享!小白,先收藏起来,学习!</p><br/>
页:
[1]