突然想测试一下STM32单片机ADC采样速率问题,按照常规方法,可以通过ADC采样,然后将采样值打印出来。但是这种方法在处理和打印数据的时候会占用很多时间,导致处理数据的时间超过了ADC的采样时间。于是想到了ADC采样的数据用DMA功能存储,并通过串口打印。但是串口打印依然要占用单片机时间,那能不能串口数据的输出也采用 DMA功能呢?这样ADC采样的数据通过DMA直接存储,然后串口通过DMA功能直接输出采样到的数据。这样速度程序执行速度不就极大的提升了吗?说干就干,使用STM32F103C8T6单片机,标准库函数,keil5软件,编写一个测试程序。
首先实现ADC采样并通过DMA存储
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h"
// 注意:用作ADC采集的IO必须没有复用,否则采集电压会有影响
/********************ADC1输入通道(引脚)配置**************************/
#define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_CLK RCC_APB2Periph_ADC1
#define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOA
#define ADC_PORT GPIOA
#define NOFCHANEL 1 //使用一个通道测试
#define ADC_PIN1 GPIO_Pin_0
#define ADC_CHANNEL1 ADC_Channel_0
// ADC1 对应 DMA1通道1,ADC3对应DMA2通道5,ADC2没有DMA功能
#define ADC_x ADC1
#define ADC_DMA_CHANNEL DMA1_Channel1
#define ADC_DMA_CLK RCC_AHBPeriph_DMA1
// ADC1转换的电压值通过MDA方式传到SRAM
extern __IO uint16_t ADC_ConvertedValue[NOFCHANEL];
/**************************函数声明********************************/
void ADCx_Init ( void );
#endif /* __ADC_H */
首先在头文件中定义用到的时钟和端口,如果要修改采样的AD口时,直接在头文件中修改就行,程序中就不需要修改了,方便代码的移植。下面编写ADC代码。
#include "bsp_adc.h"
__IO uint16_t ADC_ConvertedValue[NOFCHANEL] = {1000};
/**
* @brief ADC GPIO 初始化
* @param 无
* @retval 无
*/
static void ADCx_GPIO_Config( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
// 打开 ADC IO端口时钟
ADC_GPIO_APBxClock_FUN ( ADC_GPIO_CLK, ENABLE );
// 配置 ADC IO 引脚模式
GPIO_InitStructure.GPIO_Pin = ADC_PIN1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
// 初始化 ADC IO
GPIO_Init( ADC_PORT, &GPIO_InitStructure );
}
/**
* @brief 配置ADC工作模式
* @param 无
* @retval 无
*/
static void ADCx_Mode_Config( void )
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
// 打开DMA时钟
RCC_AHBPeriphClockCmd( ADC_DMA_CLK, ENABLE );
// 打开ADC时钟
ADC_APBxClock_FUN ( ADC_CLK, ENABLE );
// 复位DMA控制器
DMA_DeInit( ADC_DMA_CHANNEL );
// 配置 DMA 初始化结构体
// 外设基址为:ADC 数据寄存器地址
DMA_InitStructure.DMA_PeripheralBaseAddr = ( u32 ) ( & ( ADC_x->DR ) );
// 存储器地址
DMA_InitStructure.DMA_MemoryBaseAddr = ( u32 )ADC_ConvertedValue;
// 数据源来自外设
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
// 缓冲区大小,应该等于数据目的地的大小
DMA_InitStructure.DMA_BufferSize = NOFCHANEL;
// 外设寄存器只有一个,地址不用递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 存储器地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据大小为半字,即两个字节
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
// 内存数据大小也为半字,跟外设数据大小相同
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
// 循环传输模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
// 禁止存储器到存储器模式,因为是从外设到存储器
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
// 初始化DMA
DMA_Init( ADC_DMA_CHANNEL, &DMA_InitStructure );
// 使能 DMA 通道
DMA_Cmd( ADC_DMA_CHANNEL, ENABLE );
// ADC 模式配置
// 只使用一个ADC,属于单模式
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
// 扫描模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE ;
// 连续转换模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
// 不用外部触发转换,软件开启即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
// 转换结果右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
// 转换通道个数
ADC_InitStructure.ADC_NbrOfChannel = NOFCHANEL;
// 初始化ADC
ADC_Init( ADC_x, &ADC_InitStructure );
// 配置ADC时钟N狿CLK2的8分频,即9MHz
RCC_ADCCLKConfig( RCC_PCLK2_Div8 );
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig( ADC_x, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5 );
// 使能ADC DMA 请求
ADC_DMACmd( ADC_x, ENABLE );
// 开启ADC ,并开始转换
ADC_Cmd( ADC_x, ENABLE );
// 初始化ADC 校准寄存器
ADC_ResetCalibration( ADC_x );
// 等待校准寄存器初始化完成
while( ADC_GetResetCalibrationStatus( ADC_x ) );
// ADC开始校准
ADC_StartCalibration( ADC_x );
// 等待校准完成
while( ADC_GetCalibrationStatus( ADC_x ) );
// 由于没有采用外部触发,所以使用软件触发ADC转换
ADC_SoftwareStartConvCmd( ADC_x, ENABLE );
}
/**
* @brief ADC初始化
* @param 无
* @retval 无
*/
void ADCx_Init( void )
{
ADCx_GPIO_Config();
ADCx_Mode_Config();
}
/*********************************************END OF FILE**********************/
设置ADC为DMA传输,将采样的数据由DMA自动存储到 ADC_ConvertedValue[ ] 数组中,这里虽然只使用了一个ADC采样通道,但是定义了一个数组来存放采样结果,如果想要实现多通道采样值,只需要将其他通道的初始化代码添加上,同时将数组长度,也就是通道数修改一下就可以使用了。初始化ADC和DMA后,ADC采样并通过 DMA传输的功能就可使用了。然后串口输出数据的时候直接从ADC的采样结果的数组中取值就可以了。
下面编写串口相关代码
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include // 串口1-USART1 #define DEBUG_USARTx USART1 #define DEBUG_USART_CLK RCC_APB2Periph_USART1 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_BAUDRATE 921600 // USART GPIO 引脚宏定义 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA) #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define DEBUG_USART_TX_GPIO_PORT GPIOA #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9 #define DEBUG_USART_RX_GPIO_PORT GPIOA #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10 将串口的端口和时钟也使用宏定义的方式,如果要改为其他串口输出时,直接修改头文件就行。下来初始化串口。 void USART_Config( void ) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 打开串口GPIO的时钟 DEBUG_USART_GPIO_APBxClkCmd( DEBUG_USART_GPIO_CLK, ENABLE ); // 打开串口外设的时钟 DEBUG_USART_APBxClkCmd( DEBUG_USART_CLK, ENABLE ); // 将USART Tx的GPIO配置为推挽复用模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init( DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure ); // 将USART Rx的GPIO配置为浮空输入模式 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init( DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure ); // 配置串口的工作参数 // 配置波特率 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; // 配置 针数据字长 USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 配置停止位 USART_InitStructure.USART_StopBits = USART_StopBits_1; // 配置校验位 USART_InitStructure.USART_Parity = USART_Parity_No ; // 配置硬件流控制 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 配置工作模式,收发一起 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 完成串口的初始化配置 USART_Init( DEBUG_USARTx, &USART_InitStructure ); // 使能串口 USART_Cmd( DEBUG_USARTx, ENABLE ); } 下来设置串口为DMA输出 #ifndef __DMA_H #define __DMA_H #include "stm32f10x.h" // 串口对应的DMA请求通道 #define USART_TX_DMA_CHANNEL DMA1_Channel4 // 外设寄存器地址 //#define USART_DR_ADDRESS (USART1_BASE+0x04) //使用地址偏移值 #define USART_DR_ADDRESS ((u32)&USART1->DR) //直接使用寄存器地址 // 一次发送的数据量 #define SENDBUFF_SIZE 6 extern uint8_t SendBuff[SENDBUFF_SIZE]; void USARTx_DMA_Config(void); void USARTx_DMA_Restart( void ); #endif 在头文件中定义串口发送端的 DMA通道,将串口数据寄存器作为DMA源地址,将SendBuff[ ]数组中的内容作为内存地址,数据方向为内存到源,这样就直接将SendBuff[ ]数组中的数据通过DMA直接传输到了串口中。 void USARTx_DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; // 开启DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 设置DMA源地址:串口数据寄存器地址*/ DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
上一篇:STM32F103单片机ADC功能使用
下一篇:记一次ST-LINK维修及刷固件过程
推荐阅读最新更新时间:2024-11-08 15:34
推荐帖子
- 学电源的视频教程,为啥没有100%的进度?
- 今晚怎么打不开了视频教程?几个单元,前面几个都看了,中间也没落下什么,从头看到尾,为什么各个部分还是不到100%?学电源的视频教程,为啥没有100%的进度?多看几遍撒~如楼上所说这么智能啊,还能算好了你学到了百分之几十,多学习几遍就达到100%楼主不要太在意那几%了吧,,觉得都看完了,就赶快去考试,只要考过,就说明你学的好的。这和大学一样,不一定非听完老师的才行滴。活动要求至少学习进度在80%以上,就可以去考试。不一定非100%。只是觉得进度问题奇怪而已,差最后一节和没考试,
- xingkong911 模拟与混合信号
- 【GD32307E-START】03-拓展板原理图规划
- 由于自己所在的是工控行业,所以基本都是做RS485接口,还有网口,自己在设计原理图的时候拓展了485接口、2个CAN接口、1个以太网接口(RMII),还有就是一个IIC的0.96寸的OLED接口,外加了SPI-Flash,做来测试芯片应该是差不多的。下面把原理图放上来,麻烦大佬们给个意见,有可能原理图有错的地方,希望大佬们指点一下。原理图文件【GD32307E-START】03-拓展板原理图规划兆易GD32307E-START测评汇总http://bbs.eew
- 申小林 GD32 MCU
- verilog 如何才能学好?
- 我对verilog非常感兴趣!现在也在努力学习,可是不知道怎么才能学好!就像我在学c语言时,看了好多的程序,可是一上机就不知道该怎么做了!就连一个小程序写起来都很费劲!现在我就怕把verilog学到后期也成这样子了!那就不好了。我是学微电子的!现在在上大三,想在毕业前能做出点东西!我现在好些专业课如半导体物理都不怎么好!,就是想把这学好,出来能找到个好的专业!谢谢…………verilog如何才能学好?
- lmx5078 嵌入式系统
- 错误1406 "无法创建最上层子窗口"怎么解决?附代码
- boolCMyEdit::Create(CString&str,intiId){intres=CEdit::Create(WS_CHILD|WS_VISIBLE,CRect(0,0,0,0),this,iId);TRACE(L%d,GetLastError());//这里得到错误1406,无法创建最上层子窗口,为什么?returnres;}错误1406\"无法创建最上层子窗口\"怎么解决?附代码
- bin8888 嵌入式系统
- 基于MB90F428的汽车仪表设计
- 引言汽车仪表是人和汽车的交互界面,为驾驶员提供所需的汽车运行参数、故障、里程等信息,是每一辆汽车必不可少的部件。它经历了机械式、电气式、模拟电路电子式的发展过程,随着汽车电子的网络化,CAN总线技术在汽车领域得到了越来越广泛的应用,因此,CAN总线、嵌入式就成为了汽车仪表未来发展的必然趋势。汽车仪表的基本结构和功能汽车上较常用的有四种指示仪表,即车速里程表、发动机水温表、发动机转速表、燃油表等。分别显示汽车行驶速度、单里程和总里程数、发动机冷却液温度、汽车行驶时发动机旋转速度及汽车
- frozenviolet 汽车电子
- 地址和时序问题
- 用一个单片机stc89le52rc的p0口和cpld的8个i/o口相连,在寻址的时候为什么基地址从0xff00开始呢,cpld和单片机之间的数据传输是不是不虚言考虑时序啊?地址和时序问题1.可以肯定的说单片机与CPLD数据传递需要考虑时序。至于为啥P0寻址地址从0XFF00开始,这个与程序规划有关,程序规划的段地址为0XFF00,则访问P0可需要从0XFF00开始。2.CPLD访问单片机P0口的数据或传递数据给单片机P0,我想单片机的以下信号是需要给CPLD的,P0口8位
- eeleader-mcu FPGA/CPLD
设计资源 培训 开发板 精华推荐
- CH340C串口【已验证】
- LT1072CSW 为离线应用驱动高压 FET 的典型应用
- 使用符合 EN55022 B 类(24Vin 和 48Vin,双输出)具有 EMC 滤波的 RP40-2412SFR DC/DC 转换器的典型应用
- 使用 ON Semiconductor 的 NCP5006 的参考设计
- 具有独立数字滤波器的隔离式分流电流测量参考设计
- AD9834设计
- 使用 NXP Semiconductors 的 TL431AC 的参考设计
- 使用 ADA4077-4ARUZ 双电源高精度放大器用于低功耗线性化 RTD 电路的典型应用电路
- DM160226,使用 MGC3030 单区 3D 手势控制器的 Woodstar 开发套件
- AM6TW-2407DH35Z ±7.2V 6 瓦单输出 DC-DC 转换器的典型应用