ADC

【内核和外设学习营】十里 ADC光敏电阻电压采集实验

发布于 2018-09-05 18:12:24
    本帖最后由 hyz_rtt 于 2018-9-6 19:40 编辑


1、实验要求:
要求硬件上:至少有一路带ADC输入功能的GPIO,能够用来连接光敏电阻;
有一路串口用来做 msh shell 终端
软件上:BSP 中已经实现如下驱动:串口驱动,PIN 驱动,ADC驱动(裸机写的)
2、准备工作:
硬件:野火霸天虎stm32407开发板
软件:rtt3.1.0 stm32f40x(STD库)、env0.7.1
3、示例代码
(1)adc_test()可以直接在主函数调用,这里采用MSH输出
#include 
#include
#include "stm32f4xx.h"
#include "stm32f4_adc.h"

// ADC1转换的电压值通过MDA方式传到SRAM
extern __IO uint16_t ADC_ConvertedValue;

// 局部变量,用于保存转换计算后的电压值
int ADC_ConvertedValueLocal;
void adc_test(void)
{
/* 初始化滑动变阻器用到的DAC,ADC数据采集完成后直接由DMA运输数据到ADC_ConvertedValue变量
DMA直接改变ADC_ConvertedValue的值*/
Rheostat_Init();

rt_kprintf("\r\n ----这是一个光敏电阻电压采集实验(DMA传输)----\r\n");

while (1)
{
ADC_ConvertedValueLocal =ADC_ConvertedValue*(3.3/4096); // 读取转换的AD值

rt_kprintf("\r\n The current AD value = 0x%04X \r\n", ADC_ConvertedValue);
rt_kprintf("\r\n The current AD value = %d \r\n",ADC_ConvertedValueLocal);


rt_thread_delay(100);
}
}
//导出到MSH命令
MSH_CMD_EXPORT(adc_test,adc test)

(2)裸机驱动:ADC的GPIO配置、ADC的DMA传输配置
#include 
#include "stm32f4_adc.h"

__IO uint16_t ADC_ConvertedValue;

//配置ADC的GPIO
static void Rheostat_ADC_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

// 使能 GPIO 时钟
RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK, ENABLE);

// 配置 IO
GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉
GPIO_Init(RHEOSTAT_ADC_GPIO_PORT, &GPIO_InitStructure);
}

//配置ADC,DMA传输
static void Rheostat_ADC_Mode_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;

// 开启ADC时钟
RCC_APB2PeriphClockCmd(RHEOSTAT_ADC_CLK , ENABLE);

// ------------------DMA Init 结构体参数 初始化--------------------------
// ADC1使用DMA2,数据流0,通道0,这个是手册固定死的
// 开启DMA时钟
RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE);
// 外设基址为:ADC 数据寄存器地址
DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR;
// 存储器地址,实际上就是一个内部SRAM的变量
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&ADC_ConvertedValue;
// 数据传输方向为外设到存储器
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
// 缓冲区大小为1,缓冲区的大小应该等于存储器的大小
DMA_InitStructure.DMA_BufferSize = 1;
// 外设寄存器只有一个,地址不用递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// 存储器地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
// // 外设数据大小为半字,即两个字节
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 FIFO ,使用直连模式
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
// FIFO 大小,FIFO模式禁止时,这个不用配置
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
// 选择 DMA 通道,通道存在于流中
DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL;
//初始化DMA流,流相当于一个大的管道,管道里面有很多通道
DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure);
// 使能DMA流
DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE);

// -------------------ADC Common 结构体 参数 初始化------------------------
// 独立ADC模式
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
// 时钟为fpclk x分频
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
// 禁止DMA直接访问模式
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
// 采样时间间隔
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);

// -------------------ADC Init 结构体 参数 初始化--------------------------
ADC_StructInit(&ADC_InitStructure);
// ADC 分辨率
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
// 禁止扫描模式,多通道采集才需要
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
// 连续转换
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
//禁止外部边沿触发
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
//外部触发通道,本例子使用软件触发,此值随便赋值即可
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
//数据右对齐
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
//转换通道 1个
ADC_InitStructure.ADC_NbrOfConversion = 1;
ADC_Init(RHEOSTAT_ADC, &ADC_InitStructure);
//---------------------------------------------------------------------------

// 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期
ADC_RegularChannelConfig(RHEOSTAT_ADC, RHEOSTAT_ADC_CHANNEL, 1, ADC_SampleTime_3Cycles);
// 使能DMA请求 after last transfer (Single-ADC mode)
ADC_DMARequestAfterLastTransferCmd(RHEOSTAT_ADC, ENABLE);
// 使能ADC DMA
ADC_DMACmd(RHEOSTAT_ADC, ENABLE);
// 使能ADC
ADC_Cmd(RHEOSTAT_ADC, ENABLE);
//开始adc转换,软件触发
ADC_SoftwareStartConv(RHEOSTAT_ADC);
}


//ADC1初始化
void Rheostat_Init(void)
{
Rheostat_ADC_GPIO_Config();
Rheostat_ADC_Mode_Config();
}


(3)串口部分:串口驱动里已经有了
(4)宏定义
#ifndef __STM32F4_ADC_H__
#define __STM32F4_ADC_H__


#include "stm32f4xx.h"

// RHEOSTAT: 滑动变阻器/电位器

// ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里
#define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1+0x4c)

// ADC IO宏定义
#define RHEOSTAT_ADC_GPIO_PORT GPIOB
#define RHEOSTAT_ADC_GPIO_PIN GPIO_Pin_1
#define RHEOSTAT_ADC_GPIO_CLK RCC_AHB1Periph_GPIOB

// ADC 序号宏定义
#define RHEOSTAT_ADC ADC1
#define RHEOSTAT_ADC_CLK RCC_APB2Periph_ADC1
#define RHEOSTAT_ADC_CHANNEL ADC_Channel_9

// ADC DMA 通道宏定义,这里我们使用DMA传输
#define RHEOSTAT_ADC_DMA_CLK RCC_AHB1Periph_DMA2
#define RHEOSTAT_ADC_DMA_CHANNEL DMA_Channel_0
#define RHEOSTAT_ADC_DMA_STREAM DMA2_Stream0

void Rheostat_Init(void);


#endif /*__STM32F4_ADC_H__ */

4、实验步骤
env配置芯片型号stm32f407ZG、外部时钟源HSE25M、串口驱动、PIN驱动、ADC驱动(移植裸机)
5、实验结果:如下图
打印整型.png
6、遇到的问题:打印不出浮点型电压值
adc_test()部分的

int ADC_ConvertedValueLocal;
rt_kprintf("\r\n The current AD value = %d \r\n",ADC_ConvertedValueLocal);
若改为
float ADC_ConvertedValueLocal;
rt_kprintf("\r\n The current AD value = %f \r\n",ADC_ConvertedValueLocal);
打印结果如下图所示
打印浮点型失败.png
7、解决方法
百度了一下,有人说RTT不支持浮点型,自己看了一下rtdef.h文件里没有关于浮点型的定义。
还有就是有人说添加libc API配置,如下图
ENV配置选项.png
配置之后,再实验,问题仍未得到解决。
可以肯定的是裸机是可以打印出来浮点型的。如下图
裸机支持浮点型.png

查看更多

关注者
0
被浏览
1.3k
3 个回答
hyz_rtt
hyz_rtt 2018-09-06
    本帖最后由 hyz_rtt 于 2018-9-6 19:41 编辑


(1)原因分析、解决思路针对小数部分无法显示的原因,经过大神的指点,M4的芯片是支持浮点型数据据处理的,
打印不出小数部分是因为rt_kprintf()函数不支持浮点型打印,具体解决的思路是:将读
取的值先扩大(例如100000倍,可根据需要扩大),然后分别把电压值的整数和小数部分
通过处理获得,最后打印出来即可。
(2)具体措施,如下程序:
将之前void adc_test(void)里的while(1)循环里的部分做修改如下
//定义转换的变量
int inter_part;//采集电压值整数部分
int fraction_part;//采集电压值小数部分
while (1)
{
/*鉴于rt_kprintf函数不能打印浮点类型数据,所以对打印出的带有小数点的电压值做如下处理*/
// 读取转换的AD值并扩大100000倍
ADC_ConvertedValueLocal = ADC_ConvertedValue*100000*(3.3/4096);
//整数部分用整除得到,因为整型不会有小数点
inter_part = ADC_ConvertedValueLocal/100000;
//小数部分的数字利用取余得到
fraction_part = ADC_ConvertedValueLocal%100000;
rt_kprintf("\r\n The current AD value = 0x%04X——", ADC_ConvertedValue);
//打印扩大100000倍后的值
rt_kprintf("\r\n The current AD value = %d ", ADC_ConvertedValueLocal);
//打印整数部分
rt_kprintf("\r\n The current AD value = %d",inter_part);
//打印小数部分
rt_kprintf(".%d\r\n",fraction_part);
//每1秒读取一次光敏电阻电压值
rt_thread_delay(100);
}


(3)打印输出
可打印小数.png用手遮挡光敏电阻,测出的电压值变化.png
也可看到如果用手遮挡光敏电阻,电压值会变化
(4)易错点
扩大时一定是对ADC_ConvertedValue就扩大,不能是ADC_ConvertedValueLocal,
因为ADC_ConvertedValueLocal是整型,扩大再处理还是整型,而ADC_ConvertedValue
是浮点型的。
打印时“%d.%d”、“%d.” 这种写法在编译时会报错(“.%d”则是合法的),
所以利用多个rt_kprintf函数。


yangjie
yangjie 2018-09-06
    本帖最后由 yangjie 于 2018-9-6 17:20 编辑


贴代码的时候不以正文的方式贴,可以修改帖子中代码部分改为插入代码的方式,这样看起来清晰很多呢, 如下:

1536224275(1).jpg
1536224275(1).jpg
hyz_rtt
hyz_rtt 2018-09-06
yangjie 发表于 2018-9-6 17:02
贴代码的时候不以正文的方式贴,可以修改帖子中代码部分改为插入代码的方式,这样看起来清晰很多呢, 如下 ...


好的。发过的帖子题目如何更改

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览