1、DMA简介
2、存储器映像(STM32F103C8T6)
类型 | 起始地址 | 存储器 | 用途 |
ROM | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码 |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数 | |
RAM | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量 |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数 |
计算机系统的五大组成系统:运算器、控制器、存储器、输入与输出设备。
核心CPU(运和控)。存储器关键一个内容,一个地址。
ROM(只读存储器):非易失性、掉电不丢失。
RAM(随机):易失性、掉电丢失。
选项字节:主要存储Flash的读保护、写保护、看门狗等配置。
外设寄存器:初始化各个外设,最终读写的东西。
内核外设:NVIC和SysTick
3、DMA框图
Flash:主闪存
SRAM:运行内存
各个外设都可以看成寄存器,和SRAM存储器。寄存器:一种特殊的存储器,一方面可以由CPU进行读写,另一方面,寄存器的每一位背后,连接一根导线,用于控制外设电路状态。
仲裁器是在DMA通道产生冲突时,由它根据通道优先级决定谁先用。总线矩阵也有,如果DMA和CPU同时访问一个目标,DMA暂停CPU访问,防止冲突 。但任然保证CPU得到一半总线带宽使CPU正常工作。
DMA作为一个外设,它自己也会有相应配置寄存器连接于总线右边的AHB总线上,所以,DMA即是总线矩阵的主动单元,可以读写各种存储器,也是AHB上的被动单元,CPU通过线路,对DMA进行配置。
DMA请求就是DMA的硬件触发源。
4、DMA基本结构
传输计数器:自减计数器。转运完成后,自增的地址,也会恢复起始地址位置方便新的一轮转换。计数器右边,有个自动重装器,计数器到0,是否自动恢复到最初的值,如果不使用,DMA就直接结束,使用,陷入循环。
M2M:Memory to Memory存储器到存储器的意思。置1为软件,以最快的速度,连续不断的触发DMA,将其请0。软件触发一般是存储器到存储器触发。硬件触发,一般是与外设有关的转运。
5、DMA转运条件:
1)开关控制,DMA_Cmd必须使能
2)传输寄存器必须大于0
3)触发源必须有触发信号
注意事项,不能在DMA开启时,写传输计数器。
6、DMA请求
7、 数据宽度与对齐
当源端与目标宽度小时,在源端读B0,在目标写00B0,在前端补0
源端与目标宽度大时,在源端读B1B0,在目标写B1,多出来高位舍掉
8、 数据转运+DMA
当位置一一对应,所以地址左右都应该自加,移动到下一个数据位置,继续转运。
9、 ADC扫描模式+DMA
10、 DMA数据转运
1)main
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0};
int main(void)
{
OLED_Init();
OLED_ShowHexNum(1,1,DataA[0],2);
OLED_ShowHexNum(1,4,DataA[1],2);
OLED_ShowHexNum(1,7,DataA[2],2);
OLED_ShowHexNum(1,10,DataA[3],2);
OLED_ShowHexNum(2,1,DataB[0],2);
OLED_ShowHexNum(2,4,DataB[1],2);
OLED_ShowHexNum(2,7,DataB[2],2);
OLED_ShowHexNum(2,10,DataB[3],2);
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
//OLED_ShowHexNum(2,1,(uint32_t)&ADC1->DR,8);//不加强制类型转换,即指针跨级赋值
OLED_ShowHexNum(3,1,DataA[0],2);
OLED_ShowHexNum(3,4,DataA[1],2);
OLED_ShowHexNum(3,7,DataA[2],2);
OLED_ShowHexNum(3,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
while (1)
{
}
}
2)MyDMA
#include "stm32f10x.h" // Device header
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_Initstructure;
DMA_Initstructure.DMA_PeripheralBaseAddr = AddrA;//起始地址
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;//是否自增(外设)
DMA_Initstructure.DMA_MemoryBaseAddr = AddrB;//起始地址
DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte ;//数据宽度
DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC ;//传输方向, 外设到存储器
DMA_Initstructure.DMA_BufferSize = size;//缓存区大小(传输计数器)
DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;//传输模式(是否自动重装),正常模式
DMA_Initstructure.DMA_M2M = DMA_M2M_Enable;//是否存储器到存储器(硬软触发),软件触发
DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
DMA_Init(DMA1_Channel1,&DMA_Initstructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
}
3)OLED
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_D0(x) GPIO_WriteBit(GPIOB, GPIO_Pin_12, (BitAction)(x))
#define OLED_W_D1(x) GPIO_WriteBit(GPIOB, GPIO_Pin_13, (BitAction)(x))
#define OLED_W_RES(x) GPIO_WriteBit(GPIOB, GPIO_Pin_14, (BitAction)(x))
#define OLED_W_DC(x) GPIO_WriteBit(GPIOB, GPIO_Pin_15, (BitAction)(x))
#define OLED_W_CS(x) GPIO_WriteBit(GPIOA, GPIO_Pin_8, (BitAction)(x))
/*引脚初始化*/
void OLED_SPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);
OLED_W_D0(1);
OLED_W_D1(1);
OLED_W_RES(1);
OLED_W_DC(1);
OLED_W_CS(1);
}
/**
* @brief SPI发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_SPI_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_D0(0);
OLED_W_D1(Byte & (0x80 >> i));
OLED_W_D0(1);
}
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_W_CS(0);
OLED_W_DC(0);
OLED_SPI_SendByte(Command);
OLED_W_CS(1);
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_W_CS(0);
OLED_W_DC(1);
OLED_SPI_SendByte(Data);
OLED_W_CS(1);
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_SPI_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
4)实物
11、源码改变,持续存储,需要给传输器重新赋值
1) main
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
int main(void)
{
OLED_Init();
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowString(1,1,"DataA");
OLED_ShowString(3,1,"DataB");
OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
OLED_ShowHexNum(3,8,(uint32_t)DataB,8);
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
while (1)
{
DataA[0]++;
DataA[1]++;
DataA[2]++;
DataA[3]++;
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
Delay_ms(1000);
MyDMA_Transfer();
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
}
}
2)MyMDA
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t size)
{
MyDMA_Size=size;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_Initstructure;
DMA_Initstructure.DMA_PeripheralBaseAddr = AddrA;//起始地址
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//数据宽度
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable ;//是否自增(外设)
DMA_Initstructure.DMA_MemoryBaseAddr = AddrB;//起始地址
DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte ;//数据宽度
DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC ;//传输方向, 外设到存储器
DMA_Initstructure.DMA_BufferSize = size;//缓存区大小(传输计数器)
DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;//传输模式(是否自动重装),正常模式
DMA_Initstructure.DMA_M2M = DMA_M2M_Enable;//是否存储器到存储器(硬软触发),软件触发
DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
DMA_Init(DMA1_Channel1,&DMA_Initstructure);
DMA_Cmd(DMA1_Channel1,DISABLE);
}
//外设数据改变,将其重新存入存储器
void MyDMA_Transfer(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size);//重新给传输计数器赋值
DMA_Cmd(DMA1_Channel1,ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
DMA_ClearFlag(DMA1_FLAG_TC1);//清楚
}
3)实物
12、ADC单次扫描+DMA单次转运
1) main
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
while (1)
{
AD_GetValue();
OLED_ShowNum(1,5,AD_Value[0],5);
OLED_ShowNum(2,5,AD_Value[1],5);
Delay_ms(100);
}
}
2) AD
#include "stm32f10x.h" // Device header
//ADC单次扫描+DMA单次循环
uint16_t AD_Value[2];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK六分频在RCC.H里面找680
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AIN;//ADC专属模式,在AIN模式下GPIO口无效,防止干扰
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
/************选择规则组输入通道***********/
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2, ADC_SampleTime_55Cycles5);
/************用结构体初始化ADC************/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;//连续转换模式:
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ;//数据对齐:右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发控制的触发源:内部触发,软件触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ;//工作模式:独立模式
ADC_InitStructure.ADC_NbrOfChannel = 2;//通道数目
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描转换模式:
ADC_Init(ADC1,&ADC_InitStructure);
/*****************DMA结构体**************/
DMA_InitTypeDef DMA_Initstructure;
DMA_Initstructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始地址
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//是否自增(外设)
DMA_Initstructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//起始地址
DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//数据宽度
DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC ;//传输方向, 外设到存储器
DMA_Initstructure.DMA_BufferSize = 2;//缓存区大小(传输计数器)
DMA_Initstructure.DMA_Mode = DMA_Mode_Normal;//传输模式(是否自动重装),正常模式
DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;//是否存储器到存储器(硬软触发),软件触发
DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
DMA_Init(DMA1_Channel1,&DMA_Initstructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);//开启DMA触发信号
/************开启ADC电源************/
ADC_Cmd(ADC1,ENABLE);
/************ADC进行校准************/
ADC_ResetCalibration(ADC1);//四步骤:复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
ADC_StartCalibration(ADC1);//开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
}
void AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
DMA_SetCurrDataCounter(DMA1_Channel1,2);//重新给传输计数器赋值
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换函数
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
DMA_ClearFlag(DMA1_FLAG_TC1);//清除
}
13、ADC连续扫描+DMA循环转运
1)main
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
int main(void)
{
OLED_Init();
AD_Init();
OLED_ShowString(1,1,"AD0:");
OLED_ShowString(2,1,"AD1:");
while (1)
{
OLED_ShowNum(1,5,AD_Value[0],5);
OLED_ShowNum(2,5,AD_Value[1],5);
Delay_ms(100);
}
}
2) AD
#include "stm32f10x.h" // Device header
//ADC单次扫描+DMA单次循环
uint16_t AD_Value[2];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//ADC是APB2上的设备
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);//ADCCLK六分频在RCC.H里面找680
GPIO_InitTypeDef GPIO_Initstructure;
GPIO_Initstructure.GPIO_Mode=GPIO_Mode_AIN;//ADC专属模式,在AIN模式下GPIO口无效,防止干扰
GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_Initstructure);
/************选择规则组输入通道***********/
ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2, ADC_SampleTime_55Cycles5);
/************用结构体初始化ADC************/
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_ContinuousConvMode=ENABLE;//连续转换模式:
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right ;//数据对齐:右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//外部触发控制的触发源:内部触发,软件触发
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent ;//工作模式:独立模式
ADC_InitStructure.ADC_NbrOfChannel = 2;//通道数目
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//扫描转换模式:
ADC_Init(ADC1,&ADC_InitStructure);
/*****************DMA结构体**************/
DMA_InitTypeDef DMA_Initstructure;
DMA_Initstructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//起始地址
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//是否自增(外设)
DMA_Initstructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;//起始地址
DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//数据宽度
DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//是否自增(存储器)
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC ;//传输方向, 外设到存储器
DMA_Initstructure.DMA_BufferSize = 2;//缓存区大小(传输计数器)
DMA_Initstructure.DMA_Mode = DMA_Mode_Circular;//传输模式(是否自动重装),循环模式
DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;//是否存储器到存储器(硬软触发),软件触发
DMA_Initstructure.DMA_Priority = DMA_Priority_Medium;//优先级,中等优先级
DMA_Init(DMA1_Channel1,&DMA_Initstructure);
DMA_Cmd(DMA1_Channel1,ENABLE);
ADC_DMACmd(ADC1,ENABLE);//开启DMA触发信号
/************开启ADC电源************/
ADC_Cmd(ADC1,ENABLE);
/************ADC进行校准************/
ADC_ResetCalibration(ADC1);//四步骤:复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);//等待复位校准完成
ADC_StartCalibration(ADC1);//开始校准
while (ADC_GetCalibrationStatus(ADC1) == SET);//等待校准完成
ADC_SoftwareStartConvCmd(ADC1,ENABLE);//软件触发转换函数
}
void AD_GetValue(void)
{
DMA_Cmd(DMA1_Channel1,DISABLE);//先失能
DMA_SetCurrDataCounter(DMA1_Channel1,2);//重新给传输计数器赋值
DMA_Cmd(DMA1_Channel1,ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC1)==RESET);//获取DMA转运完成标志
DMA_ClearFlag(DMA1_FLAG_TC1);//清除
}
3)实物
节省软件资源,全由内部软件触发。