【蓝桥杯嵌入式】ADC数据传输与EEPROM实现数据的读写
ADC的单通道数据传输(常用)
ADC
ADC的转化方式:
- 单次转化,一次只转化一个通道
- 连续转化,转换完成一个通道 后 立即自动执行下一个通道的转换
- 扫描模式,开启一次后,自动的连续读取多个通道
ADC的三种工作方式:
- 阻塞模式(查询模式)
- 中断模式
- DMA模式
单通道/多通道
单通道: 扫描模式关闭
- 配置为“单次转换模式”,ADC通道转换一次后,就停止转换。等待再次使能后才会重新转化 。
- 配置为“连续转换模式”,ADC通道转换一次后,接着进行下一次转换,不斯连续。
**多通道:**扫描模式使能
- 配置为“单次转换模式”,ADC的多个通道,按照配置的顺序依次转换一次后,就停止转换。等到再次使能后才会重新转换
- 配置为“连续转换楔式”,ADC的多个通遊,按照配置的顺序依次转换一次后,接着进行下一次转换,不断连续。
cubemx配置
通过查看原理可得,开发板自带2路可调电阻用于ADC检测端口电压值,分别通过跳线帽接到单片机的PB15与PB12接口
cubemx引脚配置
因此将单片机的PB15引脚配置为ADC2_IN15,将PB12引脚配置ADC1_IN11,2路ADC用于采集引脚电压值。
启动ADC的单通道数据采集
程序设计
main.c初始化中,启动ADC自校准, 保证数据的准确性,否则会有偏差
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);
HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
ADC获取模拟电压功能函数
double getAdcVal(ADC_HandleTypeDef *pin)
{
double dat;
HAL_ADC_Start(pin); //启动ADC
dat = HAL_ADC_GetValue(pin); //获取ADC数值
return dat * 3.3 / 4096; //对ADC数据进行转化
}
ADC进程函数,记得需要把adc_process()函数放到main.c的while(1)中执行!!!
u32 adc_tick = 0;
void adc_process(void)
{
//每200ms读取一次ADC数据
if(uwTick - adc_tick < 200)
return;
adc_tick = uwTick;
vr37 = getAdcVal(&hadc2);
vr38 = getAdcVal(&hadc1);
}
将采集到的ADC数据放到LCD显示进程函数中进行显示
void lcd_process(void)
{
if(ui == 0)
{
sprintf(text," vr37:%.2fV ",vr37);
LCD_DisplayStringLine(Line3, (unsigned char *)text);
sprintf(text," vr38:%.2fV ",vr38);
LCD_DisplayStringLine(Line5, (unsigned char *)text);
}
}
I2C协议与EEPROM实现数据的读写
通过查看原理图,开发板自带一个24C02的EEPROM芯片,用于数据的掉电保存,该芯片通过I2C总线的形式连接到单片机的PB6和PB7引脚
cubemx配置(可不配 / 通过程序设置)
将官方给的i2c_hal.c源文件添加至工程,并初始化通过程序初始化I2C(相当于cubemx的配置工作)
I2CInit();
EEPROM实现u8数据的读写(常用)
在 i2c_hal.c文件中定义数据的存储与读取函数如下所示:
eeprom的数据读取函数
uchar eeprom_read(uchar addr)
{
uchar dat;
I2CStart(); //启动I2C
I2CSendByte(0xa0); //连接芯片
I2CWaitAck(); //等待应答
I2CSendByte(addr); //发送要读取的地址
I2CWaitAck(); //等待应答
I2CStop(); //停止I2C
I2CStart(); //重新打开I2C
I2CSendByte(0xa1); //启动读取数据
I2CWaitAck(); //等待应答
dat = I2CReceiveByte(); //接收数据
I2CWaitAck(); //等待应答
I2CStop(); //停止I2C
return dat; //发送数据
}
eeprom的数据存储函数
void eeprom_write(uchar addr, uchar dat)
{
I2CStart(); //启动I2C
I2CSendByte(0xa0); //连接芯片
I2CWaitAck(); //等待应答
I2CSendByte(addr); //发送存储地址
I2CWaitAck(); //等待应答
I2CSendByte(dat); //发送存储的数据
I2CWaitAck(); //等待应答
I2CStop(); //停止
HAL_Delay(5); //延时等待
}
其他类似数据的读写
u16/u32类型数据的读写: 将u16类型的数据的高8位与低8位分别进行读写操作即可
void eeprom_write_uint(uchar addr, uint dat)
{
eeprom_write(addr,dat & 0xff); //存储低八位
eeprom_write(addr+1,(dat>>8) & 0xff); //存储高八位
}
uint eeprom_read_uint(uchar addr)
{
//获取高8位 + 低8位 并返回
uint dat = (eeprom_read(addr+1) << 8) + eeprom_read(addr);
return dat;
}
浮点类型数据的读写: 首先将浮点类型的数据*100,转化为16位的数据,再进行数据存储,在读取返回时获取16位的数据,并除以100即得到浮点类型的目标数据。
void eeprom_write_double(uchar addr, double dat_double)
{
uint dat = (uint)(dat_double * 100); //现将浮点数转化为uint类型
eeprom_write(addr,dat & 0xff);
eeprom_write(addr+1,(dat>>8) & 0xff);
}
double eeprom_read_double(uchar addr)
{
uint dat = (eeprom_read(addr+1) << 8) + eeprom_read(addr);\
return dat / 100.0;
}
开发板的首次上电与默认参数设置
void set_up(void)
{
//首次上电 地址中数据为空(255)
if(eeprom_read(123) != 123)
{
eeprom_write(123,123); //特定地址写入数据
eeprom_write(1,60); //设置默认参数
eeprom_write_double(2,1.50);
eeprom_write_double(4,1.50);
pwm = eeprom_read(1);
}
//开发板非第一次上电 直接读取eeprom中的数据
else
{
pwm = eeprom_read(1);
}
}
数字电位器与ADC多通道的DMA传输
MCP4017数字电位器
通过查看开发板原理图,I2C总线上还挂载了一个MCP4017芯片,其中MCP4017是一个128步进,100KΩ的数字电位器,该电路是一个电阻分压结构,与一个10KΩ的R17串联,电压输出连接到开发板的PB14引脚,通过设置MCP4017的步进来改变电阻,最后可读取PB14的电压值,其中PB14的电压计算公式为:V=RWB/(RWB+ 10)* 3.3;
其中MCP4017硬件电路原理图如下所示:
MCP数字电位器更改阻值函数:
- 数字电位器的地址为0x5e
- 数字电位器实际的阻值 RWB = (dat / 127) * 100 KΩ
void mcp_write(uchar dat)
{
I2CStart(); //启动I2C
I2CSendByte(0x5e); //写入地址
I2CWaitAck(); //等待应答
I2CSendByte(dat); //写入步进长度
I2CWaitAck(); //等待应答
I2CStop(); //停止
HAL_Delay(5);
}
ADC多通道的DMA传输
DMA
DMA是内存到内存或内存到存储的直接映射,数据不用经过CPU而直接由硬件进行数据的传递。
可以直接将读取的ADC值放到内存变量中。
STM32系列的大多数产品ADC属于SAR型 (逐次逼近型),每次转换需要一定的时间,尤其是多通道转换的场合。
cubemx配置
通过同时采集PB14引脚及其PB12引脚的R38的电压值,通过ADC1的多个通道转化,并通过DMA数据传输。
首先对cubemx进行引脚配置,并设置ADC1通道5和通道11为单端模式,并为ADC1添加DMA,模式为轮询模式
其次对ADC进行参数设置,设置转化通道数,使能连续转换、使能DMA连续请求,并设置各个通道的采样时间
程序设计
uchar adc_buf[2]; //存放adc数据buf
mcp_write(63); //设置mcp电位器电阻 50KΩ
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED); //ADC1自校准
HAL_ADC_Start_DMA(&hadc1,(uint *)adc_buf,2); //启动ADC1的DMA传输
adc数据显示
sprintf(text," adc1:%.2f",adc_buf[0]*3.3/4096);
LCD_DisplayStringLine(Line8, (unsigned char *)text); //LCD显示函数
sprintf(text," adc2:%.2f",adc_buf[1]*3.3/4096);
LCD_DisplayStringLine(Line9, (unsigned char *)text); //LCD显示函数