淘宝做海淘产品 网站折扣变化快贵州凤冈新闻今天
news/
2025/10/9 13:05:47/
文章来源:
淘宝做海淘产品 网站折扣变化快,贵州凤冈新闻今天,关键词检索怎么弄,华为云服务器购买每个DMA通道硬件触发源是不一样的#xff0c;要使用某个外设的硬件触发源#xff0c;就必须使用它连接的那个通道 12个独立可配置的通道#xff1a;DMA1(7个通道)#xff0c;DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发 C8T6 DMA资源#xff1a;DMA1 #xff… 每个DMA通道硬件触发源是不一样的要使用某个外设的硬件触发源就必须使用它连接的那个通道 12个独立可配置的通道DMA1(7个通道)DMA2(5个通道) 每个通道都支持软件触发和特定的硬件触发 C8T6 DMA资源DMA1 比如想把Flash里的一批数据转运到SRAM里需要软件触发使用软件触发之后DMA会以最快都速度把这批数据转运如果进行外设到存储器的转运就不能一股脑地转运了因为外设的数据是有一定的时机的这时就需要用硬件触发 比如转运ADC的数据就需要每个ADC通道转换完成后硬件触发一次DMA再转运触发一次转运一次这样数据才是正确的才是我们想要的效果所以存储器到存储器的数据转运一般使用软件触发外设到存储器要硬件触发 运算器和控制器一般会合在一起叫做CPU所以计算机的核心关键部分就是CPU和存储器 存储器的重要知识点存储的内容和存储器的地址。外设也是存储器 ROM: 只读存储器是一种非易失性掉电不丢失的存储器 RAM:随机存储器易失掉电丢失 ROM 分为3块分别为程序存储器Flash主闪存系统存储器和选项字节 RAM运行内存SRAM外设寄存器内核外设寄存器。 1.寄存器是一种特殊的存储器一方面CPU可以对寄存器进行读写就像读写运行内存一样。另一方面寄存器的每一位背后都连接了一根导线可以用于控制外设电路的状态 2.比如置引脚的高低电平导通和断开开关切换数据选择器或者多位结合起来当作计数器数据寄存器等。所以寄存器是连接软件和硬件的桥梁软件读写寄存器就相当于再控制硬件的执行。 3.所以使用DMA进行数据转运就相当于从某个地址取内容再放到另一个地址里。为了高效有条理地访问存储器STM32设计了总线矩阵左端是主动单元也就是存储器的访问权。右边是被动单元只能被左边的主动单元读取。主动单元包括内核的DCode和系统总线可以访问右边的存储器DCode专门访问Flash系统总线访问其他东西。 4.由于DMA要访问数据所以DMA也必须要有访问的主动权主动单元除了内核CPU,剩下的就是DMA了每个DMA通道可以分别设置他们转运数据的源地址和目的地址这样他们就可以各自独立的进行工作了。 5.仲裁器的作用虽然每个通道可以独立进行设置但是DMA总线只有一条所以所有的通道都只能分时复用这一条DMA总线。如果产生了冲突就会由仲裁器根据通道的优先级决定谁先用谁后用。 6.在总线矩阵里也会有仲裁器如果DMA和CPU都要访问同一个目标那么DMA就会暂停CPU的访问防止冲突。不过。此时总线仲裁器仍然会保证CPU得到一般的总线带宽使CPU能够正常的工作。 7.AHB从设备DMA自身的寄存器因为DMA作为一个外设它自己也会有相应的配置寄存器上面连接在总线右边的AHB总线上所以DMA既是总线矩阵的主动单元可以读写各种存储器也是AHB总线上的被动单元 DMA请求就是触发的意思右边的触发源就是各个外设DMA请求就是DMA的硬件触发源比如ADC转换完成串口就受到数据等。需要触发DMA转运数据的时候就通过DMA请求线路向DMA发出硬件触发信号之后DMA就可以执行数据转运的动作了。 DMA的各个部分和作用 DMA总线用于访问各个存储器内部的多个通道可以进行独立的数据转运。 仲裁器用于调度各个通道防止产生冲突 AHB从设备用于配置DMA参数 DMA请求用于硬件触发DMA的数据转运 DMA基本结构 画圈的是数据转换的站点左边是外设寄存器站点右边是存储器站点包括Flash和SRAM。
STM32存储器一般特指Flash和SRAM不包含外设寄存器外设寄存器一般直接称为外设所以就是外设到存储器存储器到存储器这样来描述。 外设和存储器参数的作用 外设和存储器起始地址作用决定了数据从哪里来到哪里去。 数据宽度作用是指定一次转运要按多大的数据宽度来进行可以选择字节Byte半字HalhWord和字Word。字节是8位半字是16位 地址是否自增作用指定一次转运完成后下一次转运要把地址移动到下一个位置去相当于指针P 。比如ADC扫描模式用DMA进行数据转运外设地址是ADC_DR寄存器寄存器的地址不用自增不然就会移位到其他寄存器里面了。存储器地址需要自增每转运一次就往后移动一位不然会覆盖上一个数据。 进行存储器到存储器的转运就需要把其中一个存储器的地址放在外设的这个站点。只要在存储器里写Flash或SRAM的地址他就会去这两个地方找数据。
站点虽然叫外设存储器并不是说只能写寄存器的地址只是一个名字而已。 传输计数器用来指定总共需要转运几次它是自减计数器比如你给他写个5那DMA就只能进行5次数据转运转运过程中每转运一次计数器的数就会减1。当传输计数器减到0后DMA不再进行数据转运。 另外它减到0之后之前自增的地址也会恢复到起始地址的位置以方便之后DMA开始新一轮的转运。 自动重装器 作用传输计数器减到0之后是否要恢复到最初的值比如最初的值是5如果不使用自动重装器那么转运5次之后就DMA结束。使用自动重装器转运5次计数器减到0之后计数器就会立即重装到初始值5. 不重装就是单次模式重装就是循环模式 比如说要转运一个数组一般是单次模式转运一轮就结束了。如果是ADC扫描模式连续转换为了配合ADC。DMA也需要使用循环模式。和ADC差不多都是指定一轮工作完成后是不是立即开始下一轮工作 DMA触发控制 决定DMA在什么时机进行转运触发源有硬件触发和软件触发具体选择哪个有M2M参数决定。 M2M就是Memory to Memory即存储器到存储器选择1DMA就会选择软件触发 软件触发执行逻辑以最快的速度连续不断地触发DMA争取快速完成传输计数器清零完成这一轮的转换工作给0就是硬件触发。 触发源可以选择ADC串口定时器等一般都是与外设有关的转运需要一定的时机比如ADC转换完成串口收到数据定时时间到等。所以需要使用硬件触发在硬件达到这些时机时传一个信号过来触发DMA进行转运。 开关控制
使用 DMA_Cmd函数给DMA使能后DMA准备就绪可以进行转运 DMA转运的条件 1.开关控制DMA_Cmd必须使能 2.传输计数器必须大于0 3.触发源必须有触发信号触发一次转运一次传输计数器自减一次。当传输计数器等于0且没有自动重装时无论是否触发DMA都不会进行转运此时需要DMA_Cmd给DISABLE关闭DMA再为传输计数器写入一个大于0的数再DMA_Cmd给ENABLE开启DMA才能继续工作。在写传输计数器时必须要先关闭DMA在进行写入不能开启时写入EN0时不工作 硬件触发注意事项使用相应的硬件触发需要选择对应的通道不然就触发不了选择哪个触发源由对应的外设是否开启DMA输出决定的。 比如要使用ADC1就需要使用ADC_DMACmd函数进行使能必须使用这个库函数开启ADC1这一路输出才有效。使用定时器3会有TIM_DMACmd函数用来进行DMA输出控制使用哪一个外设触发源取决于把哪个通道开启了全开启理论上都可以使用 开关控制开启之后这七个触发源进入到仲裁器进行优先级判断最终产生内部DMA1请求优先级判断与中断类似序号越小优先级越高也可以在程序中配置优先级。 DMA进行转运的三个条件
DMA进行转运的三个条件
1、开关控制DMA_Cmd必须使能
2、传输计数器必须大于0
3、触发源必须有触发信号。 注意写传输计数器时必须要先关闭DMA在进行不能再DMA开启时写传输计数器。
//在里面需要重新给传输计数器赋值传输计数器赋值必须要先给DMA失能DMA_Cmd(DMA1_Channel1, DISABLE);//给传输计数器赋值DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);//使能此时DMA再次进行转运DMA_Cmd(DMA1_Channel1, ENABLE);每个通道都有一个数据选择器可以选择硬件触发和软件触发。EN并不是数据选择器的控制位而是决定这个选择器要不要工作。EN0数据选择器不工作EN1数据选择器工作。软件触发后面跟M2M的意思是当M2M位1时选择软件触发。每个通道的硬件触发源都不同若想使用某个硬件触发源的话必须使用它所在的通道这就是硬件触发的注意事项。如你要使用ADC1来触发就必须选择通道1.如果使用软件触发那么通道就可以任意选择。
数据宽度与对齐
如果转运的数据宽度一样就是正常的一个个转运如果数据宽度不一样
比如源宽度是8位目标宽度是16位传输B0时需要在前面高位补上0传输结果就是00B0.
比如源宽度是16位目标宽度是8位传输B1B0时需要把高位舍去传输结果就是B0. 具体工作流程左边是ADC扫描模式的执行流程在这里有7个通道触发一次后7个通道依次进行AD转换然后将转换结果都放到ADC_DR数据寄存器里面ADC是连续扫描那DMA就可以使用自动重装在ADC启动下一轮转换的时候DMA也启动下一轮的转换。ADC和DMA同步工作。ADC扫描在每隔单独的通道转换完成后没有任何标志位也不会触发中断。所以我们程序不太好判断某一个通道转换完成的时机是什么时候但他会产生DMA请求去触发DMA转运。
DMA库函数
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx); //恢复缺省配置
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct); //初始化
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct); //结构体初始化
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState); //使能
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState); //中断输出使能
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); //设置当前数据寄存器给这个传输计数器写数据
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);//获取当前数据寄存器返回传输计数器的值查看还剩多少数据没有转运
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);//获取标志位
void DMA_ClearFlag(uint32_t DMAy_FLAG); //清除标志位
ITStatus DMA_GetITStatus(uint32_t DMAy_IT); //获取中断状态
void DMA_ClearITPendingBit(uint32_t DMAy_IT);//清除中断挂起位初始化后立刻转运
MyDMA.c
#include stm32f10x.h // Device headeruint16_t MyDMA_Size;void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{MyDMA_Size Size;//RCC开启DMA的时钟DMA是AHB总线的设备所以用AHB开启时钟的函数RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//初始化DMADMA_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就会连续触发DMA_InitStructure.DMA_M2M DMA_M2M_Enable; //选择是否是存储器到存储器其实就是选择硬件触发还是软件触发这里选择软件触发DMA_InitStructure.DMA_Priority DMA_Priority_Medium; //优先级DMA_Init(DMA1_Channel1, DMA_InitStructure); //DMA1_Channel1选择是哪个DMA也选择了DMA的哪个通道软件触发所以任意通道都可以//因为这里是存储器到存储器所以通道可任意选择//此处选择DMA1,通道1DMA_Cmd(DMA1_Channel1, DISENABLE);/*如果选择的是硬件出发记得在对应的外设调用xxx_DMACmd,开启触发信号的输出*//*如果你需要DMA的中断就调用DMA_ITConfig,开启中断输出再在NVIC配置相应的中断通道然后写中断服务函数*//*在运行的过程中如果转运完成传输计数器清零这是想再给传输计数器赋值则需要DMA失能写传输计数器DMA使能*/
}
/*DMA转运的三个条件
1、开关控制DMA_Cmd必须使能2、传输计数器必须大于03、触发源必须有触发信号。
*/
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_ClearFlag(DMA1_FLAG_TC1);
}
MyDMA.h
#ifndef __MYDMA_H
#define __MYDMA_Hvoid MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);#endif
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include MyDMA.h//DataA作为待转运的原数据
uint8_t DataA[] {0x01, 0x02, 0x03, 0x04};
//DataB作为转运数据的目的地(全局变量默认初始值为0)
uint8_t DataB[] {0, 0, 0, 0};int main(void)
{OLED_Init();//显示dataAOLED_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);//显示dataBOLED_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); //因为需要传输4个数据所以为4//转运后//显示dataAOLED_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);//显示dataBOLED_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){}
}
程序现象 转运多次 初始化后立刻转运的代码并且转运一次之后DMA停止工作如果DataA的数据变化需要再转运一次怎么做呢 此时需要给传输计数器赋值调用一次MyDMA_Transfer(void)就再次启动一次DMA转运在函数里面需要重新给传输计数器赋值传输计数器赋值必须要先给DMA失能然后给传输计数器赋值DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);然后给DMA使能。开始转运但需要等待转运完成 while(DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET);然后清除标志位。
MyDMA.c
#include stm32f10x.h // Device header//全局变量
uint16_t MyDMA_Size;void MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size)
{//将Size赋值给全局变量MyDMA_Size Size;//RCC开启DMA的时钟DMA是AHB总线的设备所以用AHB开启时钟的函数RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//初始化DMADMA_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); //DMA1_Channel1选择是哪个DMA也选择了DMA的哪个通道软件触发所以任意通道都可以//不让DMA初始化后就立刻进行转运而是等调用Transfer函数后才进行转运DMA_Cmd(DMA1_Channel1, DISENABLE);/*如果选择的是硬件出发记得在对应的外设调用xxx_DMACmd,开启触发信号的输出*//*如果你需要DMA的中断就调用DMA_ITConfig,开启中断输出再在NVIC配置相应的中断通道然后写中断服务函数*//*在运行的过程中如果转运完成传输计数器清零这是想再给传输计数器赋值则需要DMA失能写传输计数器DMA使能*/
}
/*DMA转运的三个条件
1、开关控制DMA_Cmd必须使能2、传输计数器必须大于03、触发源必须有触发信号。
*///转运多次需要给传输计数器重新赋值
//调用该函数就再次启动一次DMA转运
//调用一次再转运一次
void MyDMA_Transfer(void)
{//在里面需要重新给传输计数器赋值传输计数器赋值必须要先给DMA失能DMA_Cmd(DMA1_Channel1, DISABLE);//给传输计数器赋值DMA_SetCurrDataCounter(DMA1_Channel1, MyDMA_Size);//使能此时DMA再次进行转运DMA_Cmd(DMA1_Channel1, ENABLE);//等待转运完成TC1为转运完成标志位while (DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET);//标志位置1后需要手动清除DMA_ClearFlag(DMA1_FLAG_TC1);
}
MyDMA.h
#ifndef __MYDMA_H
#define __MYDMA_Hvoid MyDMA_Init(uint32_t AddrA, uint32_t AddrB, uint16_t Size);
void MyDMA_Transfer(void);#endif
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include MyDMA.h//DataA作为待转运的原数据
uint8_t DataA[] {0x01, 0x02, 0x03, 0x04};
//DataB作为转运数据的目的地(全局变量默认初始值为0)
uint8_t DataB[] {0, 0, 0, 0};int main(void)
{OLED_Init();//将原数组和目的数组的地址传递进函数转运数据长度为4MyDMA_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);//之所以要强转类型是因为DataA是指针类型OLED_ShowHexNum(3, 8, (uint32_t)DataB, 8);while (1){//第一步自增变化一下原数组DataA的测试数据/*在给 DataA 数组的元素递增的操作中每次循环都对 DataA 数组的不同元素进行加一操作即 DataA[0]、DataA[1]、DataA[2]、DataA[3]。这样做的目的是为了改变 DataA 数组中的值以便在后续的代码中能够观察到数据传输的效果。通过递增操作每次循环 DataA 数组的不同元素的值都会增加这样在每次数据传输之前和之后都可以通过 OLED 显示来观察到数据的变化。*/DataA[0] ;DataA[1] ;DataA[2] ;DataA[3] ;//显示DataA和DataB转运前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);//延时1秒方便观看Delay_ms(1000);//第三步 调用MyDMA_Transfer()函数使用DMA进行数据转运MyDMA_Transfer();//显示DataA和DataB检测数据是不是从DataA转运到DataB转运后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);//延时1秒方便观看Delay_ms(1000);}
}
程序现象 ADCDMA应用两个不同的方法最后实现同一功能
ADC扫描模式 DMA数据转运 (使用ADC的扫描模式来实现多通道采集然后使用DMA进行数据转运) 开启ADC到DMA的输出 ADC单次扫描DMA单次转运模式
AD.c
#include stm32f10x.h // Device headeruint16_t AD_Value[4];/* ADC扫描模式 DMA数据转运 */
void AD_Init(void)
{//开启ADC1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//RCC开启DMA的时钟DMA是AHB总线的设备所以用AHB开启时钟的函数RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//该函数用来配置ADCCLK分频器的他可以对APB2的72Mhz时钟选择2、4、6、8分频输入到ADCCLKRCC_ADCCLKConfig(RCC_PCLK2_Div6);//PA0被初始化成模拟输入的引脚GPIO_InitTypeDef GPIO_InitStructure;//模拟输入模式在AIN模式下GPIO口是无效的断开GPIO防止GPIO口的输入输出对模拟电压造成干扰GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);//菜单上的1~4号空位我填上了0~3这四个通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //通道0放到序列1ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //通道1放到序列2ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); //通道2放到序列3ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //通道3放到序列4//用结构体初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; //右对齐ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 不使用外部触发也就是使用内部软件触发ADC_InitStructure.ADC_ContinuousConvMode DISABLE; //单次转换还是连续转换ADC_InitStructure.ADC_ScanConvMode ENABLE; //扫描ADC_InitStructure.ADC_NbrOfChannel 4; //通道数目,我点了4个菜你看前4个位置就可以了ADC_Init(ADC1, ADC_InitStructure);/*配置DMA*//* DMA 可以想象为服务员 ADC厨师把菜做好后DMA这个服务员要尽快把菜端出来防止覆盖 *///初始化DMADMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; //端菜的源头地址DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; //数据宽度(我们想要DR寄存器低16位的数据)//外设寄存器只有一个地址不用递增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 4; //缓存区大小传输计数器DMA_InitStructure.DMA_Mode DMA_Mode_Normal; //传输模式是否使用自动重装//触发源为ADC1厨师每个菜做好了教我一下我再去端菜这样才是合适的时机DMA_InitStructure.DMA_M2M DMA_M2M_Disable; //选择是否是存储器到存储器其实就是选择硬件触发还是软件触发,此处选择硬件触发DMA_InitStructure.DMA_Priority DMA_Priority_Medium; //优先级//必须使用DMA1的通道1DMA_Init(DMA1_Channel1, DMA_InitStructure); //DMA1_Channel1选择是哪个DMA也选择了DMA的哪个通道//开启ADC到DMA的输出ADC_DMACmd(ADC1, ENABLE);//不让DMA初始化后就立刻进行转运而是等调用Transfer函数后才进行转运DMA_Cmd(DMA1_Channel1, ENABLE);//传输计数器不为零//DMA使能//但是触发源有信号目前不满足因为这里是硬件触发ADC还没启动不会有触发信号所以dma是能后不会立刻工作//开启ADC电源ADC_Cmd(ADC1, ENABLE);//对ADC进行校准ADC_ResetCalibration(ADC1); //复位校准while (ADC_GetResetCalibrationStatus(ADC1) SET); //等待复位校准完成0位复位校准完成1为初始化复位校准ADC_StartCalibration(ADC1); //开始校准while (ADC_GetCalibrationStatus(ADC1) SET); //等待校准完成/*至此ADC初始化已经完成,ADC处于准备就绪的状态*/
}//因为ADC到DMA通道设定好了而且自动运行不需要判断标志位使用DMA后不需要其他方式转运
//此函数调用该函数ADC开始转换连续扫描4个通道DMA也同步进行转运AD转换结果依次放在上面的AD_Value数组里
void AD_GetValue(void)
{//在里面需要重新给传输计数器赋值传输计数器赋值必须要先给DMA失能DMA_Cmd(DMA1_Channel1, DISABLE);//给传输计数器赋值DMA_SetCurrDataCounter(DMA1_Channel1, 4);//使能此时DMA再次进行转运DMA_Cmd(DMA1_Channel1, ENABLE);//ADC还是单次模式还需要软件触发ADC转换ADC_SoftwareStartConvCmd(ADC1, ENABLE);//最后等待ADC转换和DMA转运完成转运总是在转换之后等待ADC转换完成的代码就不需要了//等待转运完成TC1为转运完成标志位while (DMA_GetFlagStatus(DMA1_FLAG_TC1) RESET);//标志位置1后需要手动清除DMA_ClearFlag(DMA1_FLAG_TC1);}
AD.h
#ifndef __AD_H
#define __AD_H//把数据存到SRAM数组里,外部可调用
extern uint16_t AD_Value[4];void AD_Init(void);
void AD_GetValue(void);#endif
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include AD.hint 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){//调用getvalue之后数据就直接跑搭配AD_Value数组里AD_GetValue();//显示结果OLED_ShowNum(1,5,AD_Value[0],4); //通道1OLED_ShowNum(2,5,AD_Value[1],4); //通道2OLED_ShowNum(3,5,AD_Value[2],4); //通道3OLED_ShowNum(4,5,AD_Value[3],4); //通道4Delay_ms(100); //让他刷新慢些}
}
ADC连续扫描DMA循环转运
总结ADC连续扫描DMA循环转运此时硬件外设已经实现了相互配合和高度自动化各种操作都是硬件自己完成,极大地减轻了软件负担软件什么都不需要做也不需要进行任何中断。硬件自动就把活干完。
AD.c
#include stm32f10x.h // Device headeruint16_t AD_Value[4];/* ADC扫描模式 DMA数据转运 */
void AD_Init(void)
{//开启ADC1时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//开启GPIOA时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//RCC开启DMA的时钟DMA是AHB总线的设备所以用AHB开启时钟的函数RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//该函数用来配置ADCCLK分频器的他可以对APB2的72Mhz时钟选择2、4、6、8分频输入到ADCCLKRCC_ADCCLKConfig(RCC_PCLK2_Div6);//PA0被初始化成模拟输入的引脚GPIO_InitTypeDef GPIO_InitStructure;//模拟输入模式在AIN模式下GPIO口是无效的断开GPIO防止GPIO口的输入输出对模拟电压造成干扰GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOA, GPIO_InitStructure);//菜单上的1~4号空位我填上了0~3这四个通道ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //通道0放到序列1ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //通道1放到序列2ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5); //通道2放到序列3ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5); //通道3放到序列4//用结构体初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode ADC_Mode_Independent; // 独立模式ADC_InitStructure.ADC_DataAlign ADC_DataAlign_Right; //右对齐ADC_InitStructure.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; // 不使用外部触发也就是使用内部软件触发ADC_InitStructure.ADC_ContinuousConvMode ENABLE; //单次转换还是连续转换ADC_InitStructure.ADC_ScanConvMode ENABLE; //扫描ADC_InitStructure.ADC_NbrOfChannel 4; //通道数目,我点了4个菜你看前4个位置就可以了ADC_Init(ADC1, ADC_InitStructure);/*配置DMA*//* DMA 可以想象为服务员 ADC厨师把菜做好后DMA这个服务员要尽快把菜端出来防止覆盖 *///初始化DMADMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)ADC1-DR; //端菜的源头地址DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_HalfWord; //数据宽度(我们想要DR寄存器低16位的数据)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 4; //缓存区大小传输计数器DMA_InitStructure.DMA_Mode DMA_Mode_Circular; //传输模式是否使用自动重装//触发源为ADC1厨师每个菜做好了教我一下我再去端菜这样才是合适的时机DMA_InitStructure.DMA_M2M DMA_M2M_Disable; //选择是否是存储器到存储器其实就是选择硬件触发还是软件触发,此处选择硬件触发DMA_InitStructure.DMA_Priority DMA_Priority_Medium; //优先级//必须使用DMA1的通道1DMA_Init(DMA1_Channel1, DMA_InitStructure); //DMA1_Channel1选择是哪个DMA也选择了DMA的哪个通道//开启ADC到DMA的输出ADC_DMACmd(ADC1, ENABLE);//不让DMA初始化后就立刻进行转运而是等调用Transfer函数后才进行转运DMA_Cmd(DMA1_Channel1, ENABLE);//开启ADC电源ADC_Cmd(ADC1, ENABLE);//对ADC进行校准ADC_ResetCalibration(ADC1); //复位校准while (ADC_GetResetCalibrationStatus(ADC1) SET); //等待复位校准完成0位复位校准完成1为初始化复位校准ADC_StartCalibration(ADC1); //开始校准while (ADC_GetCalibrationStatus(ADC1) SET); //等待校准完成/*至此ADC初始化已经完成,ADC处于准备就绪的状态*///ADC触发后ADC连续转换DMA循环转运两者一直在工作始终把最新的转换结果刷新到SRAM数组里ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
AD.h
#ifndef __AD_H
#define __AD_H//把数据存到SRAM数组里,外部可调用
extern uint16_t AD_Value[4];void AD_Init(void);#endif
main.c
#include stm32f10x.h // Device header
#include Delay.h
#include OLED.h
#include AD.hint 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); //通道1OLED_ShowNum(2,5,AD_Value[1],4); //通道2OLED_ShowNum(3,5,AD_Value[2],4); //通道3OLED_ShowNum(4,5,AD_Value[3],4); //通道4Delay_ms(100); //让他刷新慢些}
} 程序现象
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/932569.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!