DMA基础
直接存储访问(Direct Memory Access,DMA),允许不同速度的硬件装置之间的通信,不依赖于CPU。下面两张图形象地描述了DMA方式与非DMA方式的区别:
DMAC具有独立的控制三大总线(数据总线、地址总线、控制总线)来访问存储器和I/O端口的能力,它能像CPU一样提供数据传送所需的地址信息和读写控制信息。DMAC和CPU都挂在系统总线上,当进入DMA方式时,DMAC成为总线主控。在总线上,可以控制其他部件的部件成为总线主控或主控(bus master),被控制部件称为从控(slave)。任意时刻,总线上只有一个主控。DMA操作之前,应先对DMAC编程,把要传送的数据块长度、数据块在存储器中的起始地址,数据传送方向等信息发送给DMAC。
CC2530的DMA
CC2530中的DMA控制器协调所有的DMA传送,确保DMA请求和CPU存储器访问之间按照优先级协调、合理地进行。DMA控制器含有若干可编程的DMA通道,用来实现存储器-存储器的数据传送。DMA控制器控制整个XDATA存储空间的数据传送,由于大多数SFR寄存器(特殊寄存器)映射到DMA存储器空间,这些灵活的DMA通道的操作能够以创新的方式减轻CPU的负担,而在CC2530的数据手册中2.2.3物理存储器这节描述了哪个SFR寄存器映射到XDATA存储空间。
DMA控制器的主要功能如下:
- 5个独立的DMA通道
- 3个可以配置的DMA通道优先级
- 32个可以配置的传送触发事件
- 源地址和目标地址的独立控制
- 单独传送、数据块传送和重复传送模式
- 支持传输数据的长度域,设置可变传输长度
- 即可以工作在子模式,又可以工作在字节模式
DMA操作
DMA配置参数
DMA配置安装
/********************************************************
* IAR编译环境中位域字段默认取向采用小端模式,
* 配置结构体声明前使用#pragma bitfields=reversed取反向,
* 声明结束后恢复IAR默认方式
********************************************************/
#pragma bitfields = reversed
typedef struct {
uint8 SRCADDRH; //源地址高字节
uint8 SRCADDRL; //源地址低字节
uint8 DESTADDH; //目的地址高字节
uint8 DESTADDL; //目的地址低字节
uint8 VLEN:3; //变成传输模式
uint8 LENH:5; //传输长度高字节
uint8 LENL:8; //传输长度低字节
uint8 WORDSIZE:1; //字节传输或字传输
uint8 TMODE:2; //传输模式
uint8 TRIG:5; //触发时间选择
uint8 SRCINC:2; //源地址增量方式选择
uint8 DESTINC:2; //目的地址增量方式选择
uint8 IRQMASK:1; //中断屏蔽位
uint8 M8:1; //字节传输模式时用,8或7bit传输,仅仅适合在字节传输模式下
uint8 PRIORITY:2; //优先级选择
}DMA_DESC;
#pragma bitfields = default
停止DMA传输
DMA中断
DMA寄存器
/******************************************
* 基础实验只需要添加以下头文件
******************************************/
#include <ioCC2530.h>
#define uint8 unsigned char //或typedef unsigned char uint;
#define uint16 unsigned int
/********************************************************
* IAR编译环境中位域字段默认取向采用小端模式,
* 配置结构体声明前使用#pragma bitfields=reversed取反向,
* 声明结束后恢复IAR默认方式
********************************************************/
#pragma bitfields=reversed
typedef struct {
uint8 SRCADDRH; //源地址高字节
uint8 SRCADDRL; //源地址低字节
uint8 DESTADDRH; //目的地址高字节
uint8 DESTADDRL; //目的地址低字节
uint8 VLEN:3; //变长传输模式
uint8 LENH:5; //传输长度高字节
uint8 LENL:8; //传输长度低字节
uint8 WORDSIZE:1; //字节传输或字传输
uint8 TMODE:2; //传输模式
uint8 TRIG:5; //触发时间选择
uint8 SRCINC:2; //源地址增量方式选择
uint8 DESTINC:2; //目的地址增量方式选择
uint8 IRQMASK:1; //中断屏蔽位
uint8 M8:1; //字节传输模式时用,8或7bit传输,仅仅适合在字节传输模式下
uint8 PRIORITY:2; //优先级选择
}DMA_DESC;
#pragma bitfields=default
#define DATA_AMOUNT 32
/*****************************************************
* CC2530数据手册中对DMA数据结构的介绍,对以下常量赋值
****************************************************/
#define DMA_VLEN_USE_LEN 0x00 // Use LEN for transfer count
#define DMA_WORDSIZE_BYTE 0x00 // Transfer a byte at a time
#define DMA_WORDSIZE_WORD 0x01 // Transfer a 16-bit word at a time
#define DMA_TMODE_SINGLE 0x00 // Transfer a single byte/word after each DMA trigger 单个传输模式
#define DMA_TMODE_BLOCK 0x01 // Transfer block of data (length len) after each DMA trigger 块传输模式
#define DMA_TMODE_SINGLE_REPEATED 0x02 // Transfer single byte/word (after len transfers, rearm DMA) 重复单个传输模式
#define DMA_TMODE_BLOCK_REPEATED 0x03 // Transfer block of data (after len transfers, rearm DMA) 重复块传输模式
#define DMA_TRIG_NONE 0 // No trigger, setting DMAREQ.DMAREQx bit starts transfer 无触发
#define DMA_SRCINC_0 0x00 // Increment source pointer by 0 bytes/words after each transfer源地址递增0字节/字
#define DMA_SRCINC_1 0x01 // Increment source pointer by 1 bytes/words after each transfer源地址递增1字节/字
#define DMA_DESTINC_1 0x01 // Increment destination pointer by 1 bytes/words after each transfer目标地址递增1字节/字
#define DMA_IRQMASK_DISABLE 0x00 // Disable interrupt generation 通道的中断屏蔽
#define DMA_IRQMASK_ENABLE 0x01 // Enable interrupt generation upon DMA channel done 通道的中断使能
#define DMA_M8_USE_8_BITS 0x00 // Use all 8 bits for transfer count 采用所有8位作为传输长度
#define DMA_M8_USE_7_BITS 0x01 // Use 7 LSB for transfer count 采用字节地7位作为传输长度
#define DMA_PRI_LOW 0x00 // Low, CPU has priority 低优先级,CPU优先
#define DMA_PRI_GUARANTEED 0x01 // Guaranteed, DMA at least every second try 保证级,DMA至少在每秒一次的尝试中优先
#define DMA_PRI_HIGH 0x02 // High, DMA has priority 高优先级,DMA优先
#define DMA_PRI_ABSOLUTE 0x03 // Highest, DMA has priority. Reserved for DMA port access.
#define DMAARM_DMAARM0 0x01
#define DMAREQ_DMAREQ0 0x01
#define DMAIRQ_DMAIF0 0x01
#define NOP() asm("NOP")
//DMA配置参数
static DMA_DESC dmaConfig0;
//此数据是用来复制到内存的其他区域
static char data[DATA_AMOUNT] = "DMA man trigger!";
//用来保存复制来的数据区域
static char copy[DATA_AMOUNT];
//数据长度
void UartInit(void)
{
PERCFG = 0x00;
P0SEL = 0x0c;
P2DIR &= ~0xc0;
U0CSR |= 0x80;
U0GCR |= 11;
U0BAUD |= 216;
UTX0IF = 0;
}
void UartSendString(char *Data, uint16 len)
{
uint16 j;
for (j = 0; j < len; j++)
{
U0DBUF = *Data++;
for (; 0 == UTX0IF;);
UTX0IF = 0;
}
}
void Delay_ms(uint16 ms)
{
uint16 i,j;
for(i = 0; i < ms; i++)
{
for(j = 0;j < 1774; j++);
}
}
void main()
{
CLKCONCMD &= ~0x40;
for(; CLKCONSTA & 0x40;);
CLKCONCMD &= ~0X47;
UartInit();
UartSendString(data,sizeof(data));
dmaConfig0.SRCADDRH = ((uint16)&data >> 8) & 0x00FF; //获取到data地址(源地址)的高8位地址
dmaConfig0.SRCADDRL = ((uint16)&data) & 0x00FF; //获取到data地址(源地址)的低8位地址
dmaConfig0.DESTADDRH = ((uint16)© >> 8) & 0x00FF; //获取到copy地址(目的地址)的高8位地址
dmaConfig0.DESTADDRL = ((uint16)©) & 0x00FF; //获取到copy地址(目的地址)的低8位地址
dmaConfig0.VLEN = DMA_VLEN_USE_LEN; //设置可变长度为0
dmaConfig0.LENH = (DATA_AMOUNT >> 8) & 0x00FF; //获取传输长度的高5位
dmaConfig0.LENL = (DATA_AMOUNT) & 0x00FF; //获取传输长度的低8位
dmaConfig0.WORDSIZE = DMA_WORDSIZE_BYTE; //设置为字节传输模式,赋值0
dmaConfig0.TMODE = DMA_TMODE_BLOCK; //块传输模式
dmaConfig0.TRIG = DMA_TRIG_NONE; //无触发模式,即无触发源
dmaConfig0.SRCINC = DMA_SRCINC_1; //源地址1个字节/字递增
dmaConfig0.DESTINC = DMA_DESTINC_1; //目的地址1个字节/字递增
dmaConfig0.IRQMASK = DMA_IRQMASK_DISABLE; //通道的中断屏蔽
dmaConfig0.M8 = DMA_M8_USE_8_BITS; //采用所有8位作为传输长度
dmaConfig0.PRIORITY = DMA_PRI_HIGH; //高优先级,DMA优先
DMA0CFGH = ((uint16)&dmaConfig0 >> 8) & 0x00FF; //DMA通道0配置地址高8位地址
DMA0CFGL = ((uint16)&dmaConfig0) & 0x00FF; //DMA通道0配置地址低8位地址
/*DMA进入工作模式通道0*/
DMAARM |= DMAARM_DMAARM0;//为了任何DMA传输能够在该通道上发生,该位必须置1,对于非重复传输模式,一旦完成传送,该位自动清0
/*一个通道准备工作(即获得配置数据)的时间需要9个系统时钟*/
NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP(); // 9 NOPs
/*DMA通道0传送请求,即触发DMA传送*/
DMAREQ |= DMAREQ_DMAREQ0;//设置为1,激活DMA通道0(与一个触发事件具有相同的效果),当DMA传输开始清除该位
/*等待DMA通道0传送完毕*/
for (; !(DMAIRQ & DMAIRQ_DMAIF0););//当DMA通道0传送完成,DMAIRQ:DMAIF0位置1,与上DMAIRQ_DMAIF0(0x01),取非后为0退出循环
/*清除中断标志*/
DMAIRQ = ~DMAIRQ_DMAIF0;
Delay_ms(5);
UartSendString("DMA transfer after copy-->",sizeof("DMA transfer after copy-->"));
Delay_ms(5);
UartSendString(copy,sizeof(copy));
for (;;);
}