赞
踩
void dac_init() //DAC初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE); // 使能引脚时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC,ENABLE); // 使能DAC时钟
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4 ;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;//模拟量输入
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4 );//输出高
DAC_InitStructure.DAC_Trigger=DAC_Trigger_None;//不使用触发功能
DAC_InitStructure.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用三角波
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0; //屏蔽 幅值设置
DAC_InitStructure.DAC_OutputBuffer=DAC_OutputBuffer_Disable;//关闭缓存
DAC_Init(DAC_Channel_1,&DAC_InitStructure);//初始化DAC通道1
DAC_Cmd(DAC_Channel_1,ENABLE);//使能DAC1_1
DAC_SetChannel1Data(DAC_Align_12b_R,0);//12位 右对齐 写0数据,该函数定义于 stm32f10x_dac.c
// 变量 DAC_Align_12b_R 定义于stm32f10x_dac.h
}
// 设置输出电压
void Dac1_Set_Vol(double vol)
{
float temp=vol;
temp/=1000;
temp=temp*4096/3.3;
DAC_SetChannel1Data(DAC_Align_12b_R,temp);
}
extern u8 IIC_Channel;
u8 MCP4725_Write_flag = 0;
u8 MCP4725_Flag = 0;
/***************************************************************************
** 函数名称 : MCP4725_Write_DAC_EEPROM
** 功能描述 : 向DAC芯片MCP4725的EEPROM写入数据
** 输入变量 :
IICChanel : IIC通道
date ;写入数据,范围为0~65535,对应十六进制0~0xFFFF,对应0到满量程
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210224
** 说 明 : 无
***************************************************************************/
void MCP4725_Write_DAC_EEPROM(u8 n,u16 date)
{
u8 temp;
u8 DeviceAddr; // 芯片设备地址
u16 t=0;
// 选择芯片
if(n==0){DeviceAddr= 0xc0;IIC_Channel=2;}
if(n==1){DeviceAddr= 0xc2;IIC_Channel=2;}
//sprintfU4( " 1 \r\n"); // 调试代码
do
{
IIC_Start();
//sprintfU4( " 2 \r\n");
IIC_WRITE_BYTE(DeviceAddr);//写从属地址
if(IIC_Recelve_Ack()==0) // 第一次写入
{
IIC_WRITE_BYTE(0x60);
if(IIC_Recelve_Ack()==0)
{
temp = date/256;
//sprintfU4( "%d \r\n",temp);
IIC_WRITE_BYTE(temp);
//sprintfU4( "%d 1 \r\n",temp);
if(IIC_Recelve_Ack()==0)
{
temp = date%256;
IIC_WRITE_BYTE(temp);
// sprintfU4( "%d \r\n",temp);
if(IIC_Recelve_Ack()==0) // 第二次写入
{
IIC_WRITE_BYTE(0x60);
if(IIC_Recelve_Ack()==0)
{
temp = date/256;
IIC_WRITE_BYTE(temp);
//sprintfU4( "%d 3\r\n",temp);
if(IIC_Recelve_Ack()==0)
{
temp = date%256;
IIC_WRITE_BYTE(temp);
if(IIC_Recelve_Ack()==0)
{
//sprintfU4( "%d 4\r\n",temp);
MCP4725_Write_flag = 1; // MCP4725_Write_flag 写1,表示数据写入成功
MCP4725_Flag=0;
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
else {MCP4725_Flag = 1;t++;}
}
while((MCP4725_Flag == 1)&&(t<800));
if(t>=800){MCP4725_Write_flag = 0;} // MCP4725_Write_flag 写0,表示数据写入失败
IIC_Stop();
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
}
芯片共包含5个可编程寄存器:
每次传输的数据以16位的方式加载到AD9833 .
芯片唯一的16位控制寄存器(CR)描述如下:
AD9833包含两个28位频率寄存器和两个12位相位寄存器。
寄存器 | 大小 | 描述 |
---|---|---|
FREQ0 | 28 bits | 频率寄存器,当FSELECT位= 0时,该寄存器将输出频率定义为MCLK频率的一个分数。 |
FREQ1 | 28 bits | 当FSELECT= 1时,该寄存器将输出频率定义为MCLK频率的一个分数。 |
PHASE0 | 12 bits | 相位偏移寄存器,当PSELECT= 0时,该寄存器的内容被添加到相位累加器的输出。 |
PHASE1 | 12 bits | 当PSELECT= 1时,该寄存器的内容被添加到相位累加器的输出。 |
其中:
其中:
下表9举例了一个往FREQ0寄存器写入28 Bits数据的例子:
- 写入
0010 0000 0000 0000
即0x2000
到控制寄存器,表示准备往FREQX寄存器写入数据- 先写入FREQ0寄存器的低14位
0100 0000 0000 0000
即0x4000
,D15、D14 = 01表示写入的频率寄存器为FREQ0.- 后写入FREQ0寄存器的高14位
0111 1111 1111 1111
即0x7FFF
,D15、D14 = 01表示写入的频率寄存器为FREQ0.- 那么写入到FREQ0的数据就是
1111 1111 1111 1100 0000 0000 0000
即0xFFFC000
下表10和11举例了一个往FREQ、FREQ1寄存器独立写入14 Bits数据的例子:
对表10:
- 写入
0000 0000 0000 0000
即0x0000
到控制寄存器,表示准备往FREQX寄存器独立写入14位LSB数据- 写入FREQ1寄存器的低14位
1011 1111 1111 1111
即0xBFFF
,D15、D14 = 10表示写入的频率寄存器为FREQ1. 被写入到FREQ1的低14位数据位为0011 1111 1111 1111
即0x3FFF
.对表11:
- 写入
0001 0000 0000 0000
即0x1000
到控制寄存器,表示准备往FREQX寄存器独立写入14位MSB数据- 写入FREQ0寄存器的低14位
0100 0000 1111 1111
即0x40FF
,D15、D14 = 01表示写入的频率寄存器为FREQ0. 被写入到FREQ1的低14位数据位为0000 0000 1111 1111
即0x00FF
.
控制寄存器中的OPBITEN (D5)和MODE(D1)位用于决定AD983的输出。下表15 描述了两个控制寄存器位搭配下的输出波形。
如:
- 将当前VOUT波形设置为三角波,只需向AD9833控制寄存器写入
0000 0000 0000 0010
即0x0002
即可- 将当前VOUT波形设置为正弦波(默认为正弦波),只需向AD9833控制寄存器写入
0000 0000 0000 0000
即0x0000
即可- 将当前VOUT波形设置为当前DAC的MSB直接输出(特定频率的方波),只需向AD9833控制寄存器写入
0000 0000 0010 1000
即0x0028
即可
将当前VOUT波形设置为当前DAC的MSB/2直接输出(特定频率除以2的方波),只需向AD9833控制寄存器写入0000 0000 0010 0000
即0x0020
即可
输出正弦波的峰峰值是固定的,约600mV,且正弦波也不是标准的正弦波,波谷不是负电压而是0V,结合 节2.2 频率和相位寄存器 中正弦波输出频率、相位偏移公式,知输出正弦波的公式为:
其中:
代码索引:
void AD9833_Init(void)
void AD9833_Write(u16 Data)
void AD9833_Out(u32 Freq_value,u16 Phase_value,u8 cyc_Mhz)
void Delay_AD9833(u32 i)
// 头文件,根据对应GPIO而定
#define AD9833_NSS(n) {if(n==0)GPIO_ResetBits(GPIOD, GPIO_Pin_2);else GPIO_SetBits(GPIOD, GPIO_Pin_2);} // FSYNC
#define AD9833_SCLK(n) {if(n==0)GPIO_ResetBits(GPIOD, GPIO_Pin_3);else GPIO_SetBits(GPIOD, GPIO_Pin_3);}
#define AD9833_SDTA(n) {if(n==0)GPIO_ResetBits(GPIOD, GPIO_Pin_0);else GPIO_SetBits(GPIOD, GPIO_Pin_0);}
/***************************************************************************
** 函数名称 : AD9833_Init
** 功能描述 : AD9833芯片初始化
** 输入变量 : 无
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210525
** 说 明 : 无
***************************************************************************/
void AD9833_Init(void)
{
/* 配置IO口 代码块 开始 */
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); //打开GPIO时钟
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_3; // PD0 - SDA, PD2 - FSYNC, PD3 - SCLK
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* 配置IO口 代码块 结束 */
AD9833_Write(0x2100); //写寄存器,AD9833写1复位
AD9833_Write(0x4000); //对频率寄存器0 的LSB进行清零
AD9833_Write(0x4000); //对频率寄存器0 的MSB进行清零
AD9833_Write(0x2900); //写寄存器,AD9833写1复位
AD9833_Write(0x8000); //对频率寄存器1 的LSB进行清零
AD9833_Write(0x8000); //对频率寄存器1 的MSB进行清零
AD9833_Write(0xD000); //对相位寄存器0 进行清零,16位
AD9833_Write(0xF000); //对相位寄存器1 进行清零,16位
}
/***************************************************************************
** 函数名称 : AD9833_Write
** 功能描述 : 向AD9833写入16位数据
** 输入变量 : Data:16位数据
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210525
** 说 明 : 无
***************************************************************************/
void AD9833_Write(u16 Data)
{
u8 i;
AD9833_SCLK(1);
AD9833_SDTA(1);
AD9833_NSS(1);
Delay_AD9833(2);
AD9833_NSS(0);
for(i=0; i<16; i++)
{
if(Data & 0x8000)
{
AD9833_SDTA(1);
}
else
{
AD9833_SDTA(0);
}
AD9833_SCLK(0);
AD9833_SCLK(1);
Data = Data<<1;
}
AD9833_NSS(1);
AD9833_SCLK(0);
}
/***************************************************************************
** 函数名称 : AD9833_Out
** 功能描述 : AD9833输出正弦波波形函数
** 输入变量 :
Freq_value:输出波形频率
Phase_value:输出波形相位
cyc_Mhz:MCLK引脚晶振频率
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210525
** 说 明 : 无
***************************************************************************/
void AD9833_Out(u32 Freq_value,u16 Phase_value,u8 cyc_Mhz)
{
u32 dds;
u16 dds1,dds2;
dds = Freq_value * (268.435456/cyc_Mhz);
dds1 = dds & 0x3fff;
dds1 |= 0x4000;
dds = dds >> 14;
dds2 = dds & 0x3fff;
dds2 |= 0x4000;
AD9833_Write(0x2000); // 写控制寄存器,进入写频率寄存器模式
AD9833_Write(dds1); // 写频率寄存器,先写低14位
AD9833_Write(dds2); // 后写高14位
AD9833_Write(Phase_value); // 写相位寄存器,0xC000表示无相位偏移
}
/***************************************************************************
** 函数名称 : Delay_AD9833
** 功能描述 : 粗略的延时函数
** 输入变量 : i:延时时长
** 返 回 值 : 无
** 最后修改人 : xxx
** 最后更新日期: 20210525
** 说 明 : 无
***************************************************************************/
void Delay_AD9833(u32 i)
{
u8 d;
while(i>0)
{
d=6;
while(d--);
i--;
}
}
extern u8 IIC_Channel;
u8 AD5693_Write_flag = 0;
u8 Write_Flag = 0;
void AD5693_Write_DAC_EEPROM(u8 n,u8 j,u16 date)
{
u8 temp;
u8 DeviceAddr;
u8 Command;
u16 t=0;
if((n==0)&&(j==0)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x00;}
//地址编码为0,写命令,无操作
if((n==0)&&(j==1)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x10;}
//地址编码为0,写命令,写入输入寄存器
if((n==0)&&(j==2)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x20;}
//地址编码为0,写命令,更新 DAC寄存器
if((n==0)&&(j==3)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x30;}
//地址编码为0,写命令,写入寄存器和更新DAC寄存器
if((n==0)&&(j==4)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x40;}
//地址编码为0,写命令,写入控制寄存器
if((n==1)&&(j==0)){DeviceAddr= 0x9c;IIC_Channel=2;Command=0x00;}
//地址编码为1,写命令,无操作
if((n==1)&&(j==1)){DeviceAddr= 0x9c;IIC_Channel=2;Command=0x10;}
//地址编码为1,写命令,写入输入寄存器
if((n==1)&&(j==2)){DeviceAddr= 0x9c;IIC_Channel=2;Command=0x20;}
//地址编码为1,写命令,更新 DAC寄存器
if((n==1)&&(j==3)){DeviceAddr= 0x9c;IIC_Channel=2;Command=0x30;}
//地址编码为1,写命令,写入寄存器和更新DAC寄存器
if((n==1)&&(j==4)){DeviceAddr= 0x98;IIC_Channel=2;Command=0x40;}
//地址编码为1,写命令,写入控制寄存器
//sprintfU4( " 1 \r\n");
do
{
IIC_Start();
IIC_WRITE_BYTE(DeviceAddr);//写从属地址
if(IIC_Recelve_Ack()==0)
{
IIC_WRITE_BYTE(Command);//命令字节
if(IIC_Recelve_Ack()==0)
{
temp = date/256;
IIC_WRITE_BYTE(temp);
if(IIC_Recelve_Ack()==0)
{
temp = date%256;
IIC_WRITE_BYTE(temp);
if(IIC_Recelve_Ack()==0)
{
AD5693_Write_flag = 0;
Write_Flag = 0;
}
else
{Write_Flag = 1;t++;}
}
else {Write_Flag = 1;t++;}
}
else {Write_Flag = 1;t++;}
}
else {Write_Flag = 1;t++;}
}
while((Write_Flag == 1)&&(t<800));
if(t>=800){AD5693_Write_flag = 1;}
IIC_Stop();
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
delay_Nus(250);
}
#include <stm32f10x_rcc.h>
// 时钟线定义
#define DAC8411_SCLK_GPIO_CLK RCC_APB2Periph_GPIOD
#define DAC8411_DIN_GPIO_CLK RCC_APB2Periph_GPIOD
#define DAC8411_SYNC_GPIO_CLK RCC_APB2Periph_GPIOB
#define DAC8411_SPI_CLK RCC_APB2Periph_SPI1
// IO 定义
#define DAC8411_SCLK_GPIO_PORT GPIOD
#define DAC8411_DIN_GPIO_PORT GPIOD
#define DAC8411_SYNC_GPIO_PORT GPIOB
#define DAC8411_SPI_PERIPH SPI1
#define DAC8411_SCLK_GPIO_PIN GPIO_Pin_5
#define DAC8411_DIN_GPIO_PIN GPIO_Pin_6
#define DAC8411_SYNC_GPIO_PIN GPIO_Pin_8
#define DAC8411_SPI_CS_LINE GPIO_Pin_8
void DAC8411_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(DAC8411_SCLK_GPIO_CLK | DAC8411_DIN_GPIO_CLK | DAC8411_SYNC_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = DAC8411_SCLK_GPIO_PIN | DAC8411_DIN_GPIO_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(DAC8411_SCLK_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = DAC8411_SYNC_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(DAC8411_SYNC_GPIO_PORT, &GPIO_InitStructure);
}
void SPI1_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
RCC_APB1PeriphClockCmd(DAC8411_SPI_CLK, ENABLE);
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(DAC8411_SPI_PERIPH, &SPI_InitStructure);
SPI_Cmd(DAC8411_SPI_PERIPH, ENABLE);
}
void DAC8411_write(uint16_t data)
{
u8 i;
GPIO_ResetBits(DAC8411_SYNC_GPIO_PORT, DAC8411_SYNC_GPIO_PIN); // 拉低SYNC引脚以启动写操作
delay_us(40);
for(i=0;i<2;i++) // 前2位写0
{
GPIO_SetBits(GPIOD, GPIO_Pin_5); // 设置SPI SCK 为高
GPIO_ResetBits(GPIOD, GPIO_Pin_6); // 设置SPI MOSI 为低
delay_us(10);
GPIO_ResetBits(GPIOD, GPIO_Pin_5); // 设置SPI SCK 为低
delay_us(40);
}
SPI_I2S_SendData(DAC8411_SPI_PERIPH, data); // 通过SPI发送16位数据
while(SPI_I2S_GetFlagStatus(DAC8411_SPI_PERIPH, SPI_I2S_FLAG_TXE) == RESET); // 等待发送完成
GPIO_SetBits(DAC8411_SYNC_GPIO_PORT, DAC8411_SYNC_GPIO_PIN); // 释放SYNC引脚以结束写操作
}
⚠下面以STM32F4xx 系列芯片为例,不同芯片DMA 资源会有所不同;
DMA(Direct Memory Access,直接存储器访问):DMA 的出现就是为了解决大量数据的传输问题。DMA 是指外部设备不通过CPU而直接与内部存储器(FLASH/SRAM)交换数据的接口技术。这样数据的传送速度就完全取决于存储器和外设的工作速度。大量节省CPU 资源;
如下图,数据通过CPU传输的路径为黑线所示,通过DMA传输的路径为红线所示;可见红线上的数据传输并没有经过CPU;
如下图以STM32F4 大容量MCU 为例,其内部集成2个DMA,每个DMA有8个数据流,每个数据流有8个硬件通道(又叫请求),每个通道有独立的仲裁器,每个通道可以配置外设地址以连接到外设;
仲裁器:用于管理多个DMA 请求的优先级,优先级高的先处理,反之优先级低的等待被处理;
数据流级优先级:同一个DMA 的8个数据流,可通过软件编程配置成4种优先级(非常高、高、中、低);
已知系统总线是由CPU 管理的,当DMA 工作时,希望CPU把总线(AHB)让出来给DMA 控制器管理,因此DMA 控制器必须有以下功能:
由上图可知,DMA 控制器提供两个 AHB 主端口:存储器端口(用于连接存储器)和 外设端口(用于连接外设)。但是,要执行存储器到存储器的传输,外设端口必须也能访问存储器;
在上一节中是单个DMA 控制器的框图,以下是两个DMA 控制器(一般STM32有两个DMA)与总线矩阵、外设、存储器组成的DMA 系统框图;
注:下图为STM32F405xx/07xx 和STM32F415xx/17xx 系列芯片DMA 控制器系统框图,具体芯片内部资源可能不一样,以具体芯片规格书为准;
值得注意的是,如下图红框标注地方,相比于DMA2,DMA1的SHB 外设端口并没有连接到总线矩阵,因此它并不能实现外部存储器到内部存储器的数据传输;
三种传输方向:
闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标;
在用户配置好上述3个条件后,若有DMA 事件触发,DMA 控制器就会启动数据传输,当剩余传输数据量为0时,即认为达到传输终点,结束DMA 传输;另外,DMA 还有循环传输模式 ,即当到达传输终点时,DMA 控制器会重新启动DMA传输,一直循环;
当发生一个事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道的优先级处理请求。当DMA控制器开始访问发出请求的外设时,DMA控制器立即发送给它一个应答信号。当从DMA控制器得到应答信号时,外设立即释放它的请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。总之,每次DMA 数据传输由以下3个操作组成:
DMA通道的操作可以在没有外设请求的情况下进行,这种操作就是存储器到存储器模式。当设置了DMA_CCRx寄存器中的MEM2MEM位之后,在软件设置了DMA_CCRx寄存器中的EN位启动DMA通道时,DMA传输将马上开始。当DMA_CNDTRx寄存器变为0时,DMA传输结束。存储器到存储器模式不能与循环模式同时使用。
以2.1.3 节视角看,其处理过程如下图红色箭头所示:
以内部寄存器看,每次DMA 传输包含三项操作:
数据传输序列就是由多个数据组成的序列,如下图,DMA 事件由给定数目的数据传输序列组成;数据传输序列的数目及其宽度(8 位、16 位 或 32 位)可用软件编程;
如2.1.2节所述,每个DMA 有8个数据流共64个通道(请求)(8*8=64),对于同一DMA,假设64个通道同时发生数据传输请求,则需要进行通道选择,该选择通过每个数据流中的DMA_SxCR 寄存器中的 CHSEL[2:0] 位控制其内8个通道的优先级;
下表是DMA 各数据流的8个通道对应的硬件外设连接;
如:要实现串口5接收DMA 传输,根据上表,其将通过数据流0的通道4向DMA1控制器发送数据传输请求,请求被确认后,数据将通过数据流0进行数据传输;
对于每个DMA 控制器内的8个数据流,它们都能提供源和目标之间的单向传输链路;每个数据流都可进行2种传输:
如2.1.4 图所示,每个DMA 控制器中都有一个仲裁器,其作用是管理8个数据流的请求优先级;
优先权管理分2个阶段:
软件仲裁:每个通道的优先权可以在DMA_CCRx
寄存器中设置,有4个等级:
硬件仲裁:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。如:通道2优先于通道4。
注意: 在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级
下面以STM32F1 DMA应用于串口3数据接收功能为例:
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); // 开启串口3空闲中断
DMA_DeInit(DMA1_Channel3); // 复位DMA1_Channel3
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 使能DMA1时钟
// RX DMA1
DMA_InitStruct.DMA_BufferSize = sizeof(Usart3data); // 传输的数据大小
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据的来源
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 不使能M TO M传输
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Usart3data; // 设置DMA源地址:串口数据寄存器
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据单元
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 外设地址不增
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // DMA模式一次或者循环模式
//DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // DMA模式一次或者循环模式
DMA_InitStruct.DMA_PeripheralBaseAddr = USART3_BASE + 0x04; // 设置DMA源地址:串口数据寄存器地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单元
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增加
DMA_InitStruct.DMA_Priority = DMA_Priority_High; // 优先级为中
// 初始化DMA通道
DMA_Init(DMA1_Channel3, &DMA_InitStruct);
// 清除DMA所有标志
DMA_ClearFlag(DMA1_FLAG_TC3);
DMA_ITConfig(DMA1_Channel3, DMA_IT_TE,ENABLE);
USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);// 使能串口DMA接收
DMA_Cmd(DMA1_Channel3, ENABLE); // 使能DMA通道
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
比如要查询DMA通道4传输是否完成:
DMA_GetFlagStatus(DMA2_FLAG_TC4);
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
比如要获取 DMA 通道 4 还有多少个数据没有传输
DMA_GetCurrDataCounter(DMA1_Channel4);
typedef struct
{
// DMA 通道
uint32_t DMA_Channel; /*!< Specifies the channel used for the specified stream.
This parameter can be a value of @ref DMA_channel */
// DMA 外部接口基地址
uint32_t DMA_PeripheralBaseAddr; /*!< Specifies the peripheral base address for DMAy Streamx. */
// DMA 内存基地址
uint32_t DMA_Memory0BaseAddr; /*!< Specifies the memory 0 base address for DMAy Streamx.
This memory is the default memory used when double buffer mode is
not enabled. */
// 传输方向
uint32_t DMA_DIR; /*!< Specifies if the data will be transferred from memory to peripheral,
from memory to memory or from peripheral to memory.
This parameter can be a value of @ref DMA_data_transfer_direction */
// DMA 缓冲区大小
uint32_t DMA_BufferSize; /*!< Specifies the buffer size, in data unit, of the specified Stream.
The data unit is equal to the configuration set in DMA_PeripheralDataSize
or DMA_MemoryDataSize members depending in the transfer direction. */
// 指定外围地址寄存器是否应递增
uint32_t DMA_PeripheralInc; /*!< Specifies whether the Peripheral address register should be incremented or not.
This parameter can be a value of @ref DMA_peripheral_incremented_mode */
// 指定内存地址寄存器是否应递增
uint32_t DMA_MemoryInc; /*!< Specifies whether the memory address register should be incremented or not.
This parameter can be a value of @ref DMA_memory_incremented_mode */
// 外围数据的宽度
uint32_t DMA_PeripheralDataSize; /*!< Specifies the Peripheral data width.
This parameter can be a value of @ref DMA_peripheral_data_size */
// 内存数据的宽度
uint32_t DMA_MemoryDataSize; /*!< Specifies the Memory data width.
This parameter can be a value of @ref DMA_memory_data_size */
// DMA 工作模式
uint32_t DMA_Mode; /*!< Specifies the operation mode of the DMAy Streamx.
This parameter can be a value of @ref DMA_circular_normal_mode
@note The circular buffer mode cannot be used if the memory-to-memory
data transfer is configured on the selected Stream */
// DMA 软件优先级
uint32_t DMA_Priority; /*!< Specifies the software priority for the DMAy Streamx.
This parameter can be a value of @ref DMA_priority_level */
// DMA FIFO 模式
uint32_t DMA_FIFOMode; /*!< Specifies if the FIFO mode or Direct mode will be used for the specified Stream.
This parameter can be a value of @ref DMA_fifo_direct_mode
@note The Direct mode (FIFO mode disabled) cannot be used if the
memory-to-memory data transfer is configured on the selected Stream */
// DMA FIFO 模式的阈值水平
uint32_t DMA_FIFOThreshold; /*!< Specifies the FIFO threshold level.
This parameter can be a value of @ref DMA_fifo_threshold_level */
// 内存传输的突发传输配置
uint32_t DMA_MemoryBurst; /*!< Specifies the Burst transfer configuration for the memory transfers.
It specifies the amount of data to be transferred in a single non interruptable
transaction. This parameter can be a value of @ref DMA_memory_burst
@note The burst mode is possible only if the address Increment mode is enabled. */
// 外围传输的突发传输配置
uint32_t DMA_PeripheralBurst; /*!< Specifies the Burst transfer configuration for the peripheral transfers.
It specifies the amount of data to be transferred in a single non interruptable
transaction. This parameter can be a value of @ref DMA_peripheral_burst
@note The burst mode is possible only if the address Increment mode is enabled. */
}DMA_InitTypeDef;
// 注:以上配置参数可选项,可在程序文件中搜索`@ref` 之后的的关键字获取
通过设置DMA_CCRx
寄存器中的PINC
和MINC
标志位,外设和存储器的指针在每次传输后可以有选择地完成自动增量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值,增量值取决与所选的数据宽度为1、2或4 .
以存储器地址增量模式(MINC标志位设置为1)为例:
- 定义存储器数据缓冲区数据位为8位
- 定义外设的数据位为8位
- 数据从M数据缓冲区数组从低位到高位依次传输到P数据寄存器,指针根据增量值依次递增(上面设递增量为1),每传输一个数据,DMA_CNDTRx依次递减,一直等到DMA_CNDTRx为0为止
- DMA_CNDTRx为0后,根据是否设置为循环模式,是则在DMA_CNDTRx寄存器中重新写入传输数目,否则结束
循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式,数据传输的数目变为0时,将会自动地被恢复成配置通道时设置的初值,DMA操作将会继续进行。
如下图,定义通道传输数据量的寄存器为DMA_CNDTRx,它是一个32位寄存器,但只使用了前16位,故,数据传输数量最大为65535
每个DMA 的数据流都有5个事件标志来触发中断请求,他们分别是DMA 半传输、DMA 传输完成、DMA 传输错误、DMA FIFO 错误、直接模式错误;为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断;
CPU与外设之间的数据传送方式即与DMA对立的另一种外设数据传输方式,一般分为程序传送方式和中断传送方式。其特点是CPU通过控制系统总线与其他部件连接并进行数据传输。
它们均由CPU控制数据传输,不同的是程序传送方式由CPU来查询外设状态,CPU处于主动地位,而外设处于被动地位。这就是常说的对外设的轮询,效率低。而中断传送方式则是外设主动向CPU发生请求,等候CPU处理,在没有发出请求时,CPU和外设都可以独立进行各自的工作。 需要进行断点和现场的保护和恢复,浪费了很多CPU的时间,所以这种方式只适合少量数据的传送。
程序传送方式是指直接在程序控制下进行数据的输入/输出操作。分为无条件传送方式和查询(条件传送方式)两种:
中断传送方式是指当外设需要与CPU进行信息交换时,由外设向CPU发出请求信号,使CPU暂停正在执行的程序,转而去执行数据输入/输出操作,待数据传送结束后,CPU再继续执行被暂停的程序。
参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。