08:STM32----DMA数据转运
目录
1:简历
2:存储器映像
3:DMA基本结构
4: DMA转运的条件
5:DMA请求
A:DMA数据转运
1:连接图
2:数据转运+DMA
3:函数介绍
4:步骤
5:代码
B:DMA+AD多通道
1:连接图
2:结构图
3:函数介绍
4:代码
1:简历
DMA(Direct Memory Access)直接存储器存取
DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)
每个通道都支持软件触发和特定的硬件触发 (每隔DMA通道的触发源不一样)
STM32F103C8T6 DMA资源:DMA1(7个通道)
外设(外设存储器) : 一般是外设的数据寄存器DR , 比如ADC的数据寄存器、串口的数据寄存器等等
存储器 : 这里存储器,指的就是是运行内存SRAM和程序存储器Flash , 是我们存储变量数组和程序代码的地方
外设和存储器的数据传输---------使用特定的硬件触发(每隔DMA通道的触发源不一样)
存储器和存储器之间的数据传输------软件触发
2:存储器映像
ROM : 只读存储器,是一种非易失性、掉电不丢失的存储器
RAM : 随机存储器,是一种易失性、掉电丢失的存储器
因为CPU是32位的。所以导址范围就是32位的范围
3:DMA基本结构
下面的参数在配置DMA时使用

方向 : 决定了数据的方向 外设--->储存器 或者 储存器---->外设 或者 储存器---->储存器
Flash是只读储存器, 所以在储存器--->储存器的传输方向的时候 :只能选择 Flash-------------->SRAM方向的传输
X.DMA_DIR=指定外设站点(外设寄存器)为数据源还是目的地
传输计数器: 用来指定,我总共需要转运几次的, 这个传输计数器是一个自减计数器
比如给它写一个5, 那DMA就只能进行5次数据转运 , 转运过程中,每转运一次,计数器的数就会减1, 当传输计数器减到0之后,DMA就不会再进行数据转运了 , 它减到0之后,之前自增的地址,也会恢复到起始地址的位置
X.DMA_BufferSize=传输计数器
自动重装器 : 传输计数器减到0之后 , 是否要恢复到最初的值
比如最初传输计数器给5, 如果不使用自动重装器,那转运5次后,DMA就结束了. 如果使用自动重装器,那转运5次, 计数器减到0后,就会立即重装到初始值5, 决定了转运的模式
不重装,就是正常的单次模式
重装,就是循环模式
X.DMA_Mode=自动重装器
起始地址 : 决定了数据从那里来到那里去的
因为stm32是32位的单片机,他的一个内存单元是32位的, 所以起始地址填入的都为32位
数据宽度 : 指定一次转运要按多大的数据宽度来进行
字节Byte(uint8_t)、半字HalfWord(uint16_t)和字Word(uint32_t)
软件触发和循环模式,不能同时用
软件触发 : 软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA, 早日把传输计数器清零,完成这一轮的转换
软件触发就是尽快把传输计数器清零 . 循环模式是清零后自动重装 , 如果同时用的话,那DMA就停不下来了
4: DMA转运的条件
1: 开关控制,DMA _Cmd函数必须使能
2: 传输计数器必须大于0
3: 触发源,必须有触发信号
触发一次,转运一次,传输计数器自减一次 , 当传输计数器等于0,且没有自动重装时 , 无论是否触发,DMA都不会再进行转运了, 此时就需要DMA_Cmd函数,给DISABLE,关闭DMA. 再为传输计数器写入一个大于0的数, 再DMA_Cmd,给ENABLE,开启DMA才可以正常工作.
注意,写传输计数器时,必须要先关闭DMA,再进行, 不能在DMA开启时,写传输计数器
5:DMA请求

特定的硬件触发 : 每个通道的硬件触发源都是不同的 , 需要用ADC1来触发的话一那就必须选择通道1; 定时器2的更新事件来触发的话,那就必须选择通道2
因为每个通道的硬件触发源都不同 , 如果你想使用某个硬件触发源的话, 就必须使用它所在的通道
软件触发 : 使用软件触发的话,那通道就可以任意选择了. 每个通道的软件触发都是一样的
A:DMA数据转运
1:连接图

2:数据转运+DMA

3:函数介绍
在stm32f10x dmah文件中配置----MDA初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
在stm32f10x dmah文件中配置----开启MDA
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
在stm32f10x dmah文件中配置----数据寄存器设置
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
DMA_SetCurrDataCounter : 设置当前数据寄存器 , 就是给这个传输计数器写数据的, 和配置DMA中X.DMA_Buffersize参数相似
DMA_GetCurrDataCounter : 获取当前数据寄存器, 这个函数就是返回传输计数器的值 , 在转运完成后把标志位值1
在stm32f10x dmah文件中配置----清除中断标志位
void DMA_ClearFlag(uint32_t DMAy_FLAG)
DMA_ClearFlag : 清除中断标志位
4:步骤
1:开启RCC时钟-----DMA的时钟在AHB总线上
2:配置DMA
5:代码
使用的是存储器和存储器之间的数据传输------软件触发
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MyDMA.h"
uint16_t MyDMA_Size;
void MyMDA_init(uint32_t AddrA, uint32_t AddrB, uint16_t Size){MyDMA_Size=Size;//开启RCC--DMA是AHB总线的设备 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);/*我们使用的STM32型号为: STMF103参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号参数1的第一个选择 : 其他设备---103选择这个*///配置DMADMA_InitTypeDef DMA_initstruct;//外设备站点的3个参数//因为stm32是32位的单片机,他的一个内存单元是32位的DMA_initstruct.DMA_PeripheralBaseAddr=AddrA; //外设的起始地址--要求:32位DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;外设的数据宽度---我们选择以字节的方式传输(uint8_t)DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设的地址是否自增--自增//储存器的3个参数DMA_initstruct.DMA_MemoryBaseAddr=AddrB;//储存器的起始地址--要求:32位DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//储存器的数据宽度--我们选择以字节的方式传输(uint8_t)DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增//传输方向DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源//缓冲区大小---传输计数器DMA_initstruct.DMA_BufferSize=Size;//传输几次//传输模式----是否使用自动重装DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//正常模式 //传输计数器到0直接停止//选择触发模式---硬件触发或者软件触发DMA_initstruct.DMA_M2M=DMA_M2M_Enable; //使用软件触发//优先级DMA_initstruct.DMA_Priority=DMA_Priority_Medium;DMA_Init(DMA1_Channel1,&DMA_initstruct);//开启MDADMA_Cmd(DMA1_Channel1,DISABLE);}void MyDMA_Transfer(void)
{//需要给我传输寄存器重新赋值,首先要使CMD失能DMA_Cmd(DMA1_Channel1, DISABLE);/*设置当前数据寄存器 , 就是给这个传输计数器写数据的和配置DMA中X.DMA_BufferSize参数相似*/DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);DMA_Cmd(DMA1_Channel1, ENABLE);//DMA1_FLAG_TC1---转运完成标志位 转运完成后置1while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
}uint8_t dataA[]={0x01,0x02,0x03,0x04};
uint8_t dataB[]={0,0,0,0};
int main(void)
{OLED_Init();MyMDA_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);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);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);MyDMA_Transfer();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); Delay_ms(1000); }
}
开启RRC时钟
开启RCC--DMA是AHB总线的设备
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
/*
我们使用的STM32型号为: STMF103
参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号
参数1的第一个选择 : 其他设备---103选择这个
*/
字节问题
因为要参数的数据是dataA和dataB都是uint8_t的大小, 所以在配置DMA时宽度的参数(X.DMA_PeripheralDataSize)选择DMA_PeripheralDataSize_Byte(uint8_t)
在配置DMA起始地址时选uint32_t: 因为stm32是32位的单片机,他的一个内存单元是32位的
B:DMA+AD多通道
1:连接图
和07:STM32----ADC模数转化器 --- B:AD多通道一致
2:结构图

3:函数介绍
在文件stm32f10x_adc.c中-----开启ADC到DMA的输出
void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)
4:代码
在07:STM32----ADC模数转化器中 我们使用了没有使用扫描模式, 是由于ADC的数据覆盖问题,在下面的代码中我们使用DMA进行数据转运, 所以在ADC使用扫描模式的情况下必须使用DMA进行数据转运
ADC单次扫描+DMA单次转运的模式
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
//ADC单次扫描+DMA单次转运的模式
uint16_t AD_Value[4];void AD_init(void){//RCC开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);/*我们使用的STM32型号为: STMF103参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号参数1的第一个选择 : 其他设备---103选择这个*///配置ADCCLK//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz//配置GPIOGPIO_InitTypeDef GPIO_initstruct;GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入,可以理解为ADC的专属模式GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_initstruct);/*ADC_Channel_0 --通道o1----1~16的范围规则组第几个序列ADC_SampleTime_55Cycles5-----指定通道的采样时间*///选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5); //初始化ADCADC_InitTypeDef ADC_initstruct;ADC_initstruct.ADC_ContinuousConvMode=DISABLE;//选择是连续转换还是单次转换---单次ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式ADC_Init(ADC1,&ADC_initstruct);//ADC在完成后面数据写在DR寄存器中//配置DMA//外设备站点的3个参数//因为stm32是32位的单片机,他的一个内存单元是32位的DMA_InitTypeDef DMA_initstruct;DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
// //储存器的3个参数DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增//传输方向DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源//缓冲区大小---传输计数器DMA_initstruct.DMA_BufferSize=4;//传输几次//传输模式----是否使用自动重装DMA_initstruct.DMA_Mode=DMA_Mode_Normal;//普通模式 //传输计数器到0直接停止 //DMA_Mode_Circular--循环模式 DMA_Mode_Normal//选择触发模式---硬件触发或者软件触发DMA_initstruct.DMA_M2M=DMA_M2M_Disable; //使用硬件触发//优先级DMA_initstruct.DMA_Priority=DMA_Priority_Medium;DMA_Init(DMA1_Channel1,&DMA_initstruct);//开启MDADMA_Cmd(DMA1_Channel1,ENABLE);//开启ADC到DMA的输出ADC_DMACmd(ADC1,ENABLE);//开启ADCADC_Cmd(ADC1,ENABLE);//校准ADC//复位校准ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态while (ADC_GetResetCalibrationStatus(ADC1)==SET); //SET=1/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除在校准寄存器被初始化后该位将被清除,所以该位的用法就是:你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0*///开始校准ADC_StartCalibration(ADC1);//获取校准状态while(ADC_GetCalibrationStatus(ADC1)==SET);}void ad_getvalue(void){//重新写一下传输寄存器DMA_Cmd(DMA1_Channel1, DISABLE);/*设置当前数据寄存器 , 就是给这个传输计数器写数据的和配置DMA中X.DMA_BufferSize参数相似*/DMA_SetCurrDataCounter(DMA1_Channel1, 4);DMA_Cmd(DMA1_Channel1, ENABLE);//单次模式-----软件触发转换ADC_SoftwareStartConvCmd(ADC1,ENABLE);//这里因为转运(DMA)总是在转换之后的//等待MDA完成的代码//DMA1_FLAG_TC1---转运完成标志位 转运完成后置1while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位}int main(void)
{OLED_Init();AD_init();OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:"); while (1){ad_getvalue();OLED_ShowNum(1, 5, AD_Value[0], 4);OLED_ShowNum(2, 5, AD_Value[1], 4);OLED_ShowNum(3, 5, AD_Value[2], 4);OLED_ShowNum(4, 5, AD_Value[3], 4);Delay_ms(100);}
}
ad_getvalue函数
因为在这里面的ADC是单次转化, 所以每次转化时都需要软件触发一次, 每转化一次都需要调用一次这个的函数----ADC_SoftwareStartConvCmd(ADC1,ENABLE);
因为DMA也是正常的单次模式: 所以在触发ADC之前,需要再重新写入一下传输计数器
//重新写一下传输寄存器DMA_Cmd(DMA1_Channel1, DISABLE);/*设置当前数据寄存器 , 就是给这个传输计数器写数据的和配置DMA中X.DMA_BufferSize参数相似*/DMA_SetCurrDataCounter(DMA1_Channel1, 4);DMA_Cmd(DMA1_Channel1, ENABLE)最后,等待ADC转换和DMA转运完成, 这里因为转运总是在转换之后的 , 所以我们只需要写等待DMA完成的代码, 不用写等待ADC完成的代码
//这里因为转运(DMA)总是在转换之后的//等待MDA完成的代码//DMA1_FLAG_TC1---转运完成标志位 转运完成后置1while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET); DMA_ClearFlag(DMA1_FLAG_TC1);//清除转运完成标志位
启动扫描模式
就是在选择组的时候多选几个,然后在配置ADC的时候选择扫描模式, 通道要和一引脚对应. ADC配置的通道数目填入相应的通道数目
/*ADC_Channel_0 --通道o1----1~16的范围规则组第几个序列ADC_SampleTime_55Cycles5-----指定通道的采样时间*///选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5); ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道
DR寄存器
在ADC会把数据写入到ADC的DR寄存器中, 使用在配置DMA的外设起始地址应该是
DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位
数据源外设地址不自增, 寄存器地址自增
在这里外面使用的ADC模式为扫描模式, 就是启动了一个组在组中写通道, 如果地址自增就会跑到其他的组中去. 寄存器地址自增 : 外设中组中通道的每个数据对应的是寄存器中不同的地方, 这样才可以实现数据的转移
其他注意
在开启ADC前: 要打开ADC到DMA的输出
DMA_Cmd(DMA1_Channel1,ENABLE);在配置DMA的传输计数器是也要改变为相应的传输次数
DMA_initstruct.DMA_BufferSize=4;//传输几次配置DMA的触发方式改为硬件触发
DMA_initstruct.DMA_M2M=DMA_M2M_Disable; //使用硬件触发
ADC连续扫描+DMA循环转运模式
因为使用的是连续---所以软件触发只需在初始化的时候触发一次即可.
当ADC触发之后,ADC连续转换,DMA循环转运 , 两者一直在工作,始终把最新的转换结果,刷新到SRAM数组里, 我们想要数据的时候,随时去数组里取就行了
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "AD.h"
uint16_t AD_Value[4];void AD_init(void){//RCC开启时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);/*我们使用的STM32型号为: STMF103参数1的第一个选择 : 互连型设备--互联型是STM23F 105/107的型号参数1的第一个选择 : 其他设备---103选择这个*///配置ADCCLK//APB2时钟72MHz时钟信号然后通过ADC预分频器进行分频,得到ADCCLK钟信号RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72Mhz/6=12Mhz//配置GPIOGPIO_InitTypeDef GPIO_initstruct;GPIO_initstruct.GPIO_Mode=GPIO_Mode_AIN; //模拟输入,可以理解为ADC的专属模式GPIO_initstruct.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_initstruct);/*ADC_Channel_0 --通道o1----1~16的范围规则组第几个序列ADC_SampleTime_55Cycles5-----指定通道的采样时间*///选择AD转化器----我们选择规则组的输入通道 因为启动了应该组,所以是下面改为扫描模式ADC_RegularChannelConfig(ADC1,ADC_Channel_0,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_1,2,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_2,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC1,ADC_Channel_3,4,ADC_SampleTime_55Cycles5); //初始化ADCADC_InitTypeDef ADC_initstruct;ADC_initstruct.ADC_ContinuousConvMode=ENABLE;//选择是连续转换还是单次转换--连续ADC_initstruct.ADC_DataAlign=ADC_DataAlign_Right; //数据对齐---右对齐ADC_initstruct.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;//触发控制的触发源---不使用外部触发,使用内部软件触发ADC_initstruct.ADC_Mode=ADC_Mode_Independent;//ADC的工作模式---独立模式ADC_initstruct.ADC_NbrOfChannel=4; //通道数目--指定在扫描模式下,总共会用到几个通道ADC_initstruct.ADC_ScanConvMode=ENABLE;//可以选择是扫描模式还是非扫描模式---扫描模式ADC_Init(ADC1,&ADC_initstruct);//ADC在完成后面数据写在DR寄存器中//配置DMA//外设备站点的3个参数//因为stm32是32位的单片机,他的一个内存单元是32位的DMA_InitTypeDef DMA_initstruct;DMA_initstruct.DMA_PeripheralBaseAddr=(uint32_t)&ADC1->DR; //外设的起始地址--要求:32位DMA_initstruct.DMA_PeripheralDataSize=DMA_PeripheralDataSize_HalfWord;外设的数据宽度---我们选择以字节的方式传输(uint16_t)DMA_initstruct.DMA_PeripheralInc=DMA_PeripheralInc_Disable;//外设的地址是否自增--不自增
// //储存器的3个参数DMA_initstruct.DMA_MemoryBaseAddr=(uint32_t)AD_Value;//储存器的起始地址--要求:32位DMA_initstruct.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;//储存器的数据宽度--我们选择以字节的方式传输(uint16_t)DMA_initstruct.DMA_MemoryInc=DMA_MemoryInc_Enable;//储存器的地址是否自增--自增//传输方向DMA_initstruct.DMA_DIR=DMA_DIR_PeripheralSRC;//指定外设站点(外设寄存器)为数据源还是目的地---数据源//缓冲区大小---传输计数器DMA_initstruct.DMA_BufferSize=4;//传输几次//传输模式----是否使用自动重装DMA_initstruct.DMA_Mode=DMA_Mode_Circular;//循环模式 //传输计数器到0直接停止 //DMA_Mode_Circular--循环模式 DMA_Mode_Normal//选择触发模式---硬件触发或者软件触发DMA_initstruct.DMA_M2M=DMA_M2M_Disable; //使用硬件触发//优先级DMA_initstruct.DMA_Priority=DMA_Priority_Medium;DMA_Init(DMA1_Channel1,&DMA_initstruct);//开启MDADMA_Cmd(DMA1_Channel1,ENABLE);//开启ADC到DMA的输出ADC_DMACmd(ADC1,ENABLE);//开启ADCADC_Cmd(ADC1,ENABLE);//校准ADC//复位校准ADC_ResetCalibration(ADC1);//---把CR2_RSTCAL_Set这一位置一//等待复位校准完成--ADC_GetResetCalibrationStatus作用:返回复位校准的状态while (ADC_GetResetCalibrationStatus(ADC1)==SET); //SET=1/*获取的是这个CR2_RSTCAL_Set的标志位 ,该位由软件设置并由硬件清除在校准寄存器被初始化后该位将被清除,所以该位的用法就是:你软件置该位为1,那硬件就会开始复位校准 , 当复位校准完成后,该位就会由硬件自动清0*///开始校准ADC_StartCalibration(ADC1);//获取校准状态while(ADC_GetCalibrationStatus(ADC1)==SET);ADC_SoftwareStartConvCmd(ADC1,ENABLE);}int main(void)
{OLED_Init();AD_init();OLED_ShowString(1, 1, "AD0:");OLED_ShowString(2, 1, "AD1:");OLED_ShowString(3, 1, "AD2:");OLED_ShowString(4, 1, "AD3:"); while (1){OLED_ShowNum(1, 5, AD_Value[0], 4);OLED_ShowNum(2, 5, AD_Value[1], 4);OLED_ShowNum(3, 5, AD_Value[2], 4);OLED_ShowNum(4, 5, AD_Value[3], 4);Delay_ms(100);}
}
相关文章:
08:STM32----DMA数据转运
目录 1:简历 2:存储器映像 3:DMA基本结构 4: DMA转运的条件 5:DMA请求 A:DMA数据转运 1:连接图 2:数据转运DMA 3:函数介绍 4:步骤 5:代码 B:DMAAD多通道 1:连接图 2:结构图 3:函数介绍 4:代码 1:简历 DMA(Direct Memory Access)直接存储…...
Golang 程序漏洞检测利器 govulncheck(二):漏洞数据库详解
上一篇文章详细介绍了 Golang 程序漏洞扫描工具 govulncheck 的使用方法,govulncheck 强大功能的背后,离不开 Go 漏洞数据库(Go vulnerability database)的支持,接下来详细讲解下 Go 漏洞数据库相关的知识。 Go 漏洞数…...
[JDK8下的HashMap类应用及源码分析] 数据结构、哈希碰撞、链表变红黑树
系列文章目录 [Java基础] StringBuffer 和 StringBuilder 类应用及源码分析 [Java基础] 数组应用及源码分析 [Java基础] String,分析内存地址,源码 [JDK8环境下的HashMap类应用及源码分析] 第一篇 空构造函数初始化 [JDK8环境下的HashMap类应用及源码分…...
高等数学刷题
两个公式本质都是相同的 Π/2 1^∞类型...
lintcode 1840 · 矩阵还原【中等 vip 二维前缀和数组】
题目 https://www.lintcode.com/problem/1840 现有一个n行m列的矩阵 before,对于before里的每一个元素 before[i][j],我们会使用以下算法将其转化为 after[i][j]。现给定after矩阵,请还原出原有的矩阵before。s 0 for i1: 0 -> ifor j1…...
VMware虚拟机+Centos7 配置静态,动态IP
本章目录 一、查看网关: 编辑–>虚拟网络编辑器二、点击NAT设置三、记住网关IP待会要用四、配置静态ip地址1、进入存放修改IP地址的目录2、修改ip地址的文件3、编辑文件4、文件(编辑好后退出) 五、重启网络六、测试1、linux上查看IP地址的…...
【C++精华铺】10.STL string模拟实现
1. 序言 STL(标准模板库)是一个C标准库,其中包括一些通用的算法、容器和函数对象。STL的容器是C STL库的重要组成部分,它们提供了一种方便的方式来管理同类型的对象。其中,STLstring是一种常用的字符串类型。 STLstrin…...
微信小程序开发---事件的绑定
目录 一、事件的概念 二、小程序中常用的事件 三、事件对象的属性列表 四、bindtap的语法格式 (1)绑定tap触摸事件 (2)编写处理函数 五、在事件处理函数中为data中的数据赋值 六、事件传参 七、bindinput的语法格式 八、…...
基于Hata模型的BPSK调制信号小区覆盖模拟matlab完整程序分享
基于Hata信道模型的BPSK调制信号小区覆盖模拟matlab仿真,对比VoIP, Live Video,FTP/Email 完整程序: clc; clear; close all; warning off; addpath(genpath(pwd)); % Random bits are generated here. bits randi([0, 1], [50,1]); M 2; t 1:1:50; …...
音视频 ffmpeg视频裁剪
将输入视频帧的宽度和高度从x和y值表示的位置裁剪到指定的宽度和高度;x和y是输出的左上角坐标,协调系统的中心是输入视频帧的左上角。 如果使用了可选的keep_aspect参数,将会改变输出SAR(样本宽比)以补偿新的DAR(显示长宽比) cropow[:oh[:x[:y[:keep_as…...
Web3数据云OORT推出商用版智能代理构建平台:OORT TDS
随着技术进步和数据隐私问题的日益凸显,生成式AI和去中心化技术联手为企业和个人开辟了全新的互动视野。站在这一趋势的前沿,OORT展现了其在去中心化数据云领域的技术实力,作为行业的领先者,今日Oort正式宣布OORT TDS (Talk-to-Da…...
ChatGPT:革命性的自然语言处理技术
自然语言处理(NLP)技术的快速发展已经为我们的日常生活带来了巨大的变革。在这个领域,ChatGPT作为一个突出的代表,正在为我们带来更多的便利和机会。本文将介绍ChatGPT的基本概念、应用领域以及它在未来可能带来的影响。 ChatGPT…...
利用frps搭建本地自签名https服务的透传
nginx的搭建就不介绍了,教程很多,基本上油手就会。 在本例中,frp服务器的域名是 www.yourfrp.com,同时也是反向代理nginx服务器; 本地网站要用的域名: test.abcd.com 请事先将 test.abcd.com 解析到 frp所在服务器…...
安卓手机安装Linux然后在其中安装(jdk,MySQL,git)
安卓手机安装Linux然后在其中安装(jdk,MySQL,git) 一.安卓手机安装Linux 安装termux最新教程_哔哩哔哩_bilibili Linux入门教程__阿伟_的博客-CSDN博客 二.安装jdk Termux手机终端运行java。jdk环境的搭建_哔哩哔哩_bilibili java后端__阿伟_的博客-CSD…...
javaee之黑马乐优商城2
简单分析一下商品分类表的结构 先来说一下分类表与品牌表之间的关系 再来说一下分类表和品牌表与商品表之间的关系 面我们要开始就要创建sql语句了嘛,这里我们分析一下字段 用到的数据库是heima->tb_category这个表 现在去数据库里面创建好这张表 下面我们再去编…...
Qt打开及创建项目,运行程序(1)
安装之后, 1.文件->新建文件或项目 2.Application->Qt Widgets Application 3.自己设置名称和路径 4.这一步非常非常重要,要选择编译器,(MinGW是可以在Qt里用,如果想与VS交互,要选择MSVC)…...
八种十倍提升API性能的方式
提起API,作为程序员来说并不陌生,很多程序员的大部分工作都是围绕着它, 然而,有些内容被大家忽略,API的性能会直接影响产品的用户体验,比如,一个视频软件,播放1s后需要加载5s&#x…...
pg_database中的datlastsysoid
一,关于 pg_database 在 PostgreSQL 中,对于在数据库集群内创建的每个数据库,其关键信息都会被保存到 pg_database 系统表中。 PostgreSQL 确保通过 pg_database 系统表持久化存储每个数据库的属性信息,以方便后续管理和使用。这也让 pg_da…...
【已解决】ognl.PropertyAccessor
在Spring boot2.x用TemplateEngine处理数据得时候,出现以下错误: 定位到代码行: 解决办法:修改thymeleaf的依赖: <!-- thymeleaf --><dependency><groupId>org.thymeleaf</groupId><…...
Pytest系列-快速入门和基础讲解(1)
前言 目前有两种纯测试的测试框架,pytest和unittestunittest应该是广为人知,而且也是老框架了,很多人都用来做自动化,无论是UI还是接口pytest是基于unittest开发的另一款更高级更好用的单元测试框架 单元测试框架介绍 单元测试…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...


