目录
- [1 ADC介绍](#1 ADC介绍)
-
- [1.1 什么是ADC?](#1.1 什么是ADC?)
- [1.2 ADC工作原理(逐次逼近型)](#1.2 ADC工作原理(逐次逼近型))
- [1.3 ADC特性参数](#1.3 ADC特性参数)
- [2 ADC框图](#2 ADC框图)
- [3 ADC的一些细节](#3 ADC的一些细节)
-
- [3.1 输入通道](#3.1 输入通道)
- [3.2 规则组/注入组](#3.2 规则组/注入组)
- [3.3 转换顺序](#3.3 转换顺序)
- [3.4 触发转换方法](#3.4 触发转换方法)
- [3.5 转换时间](#3.5 转换时间)
- [3.6 中断及事件](#3.6 中断及事件)
- [3.7 校准](#3.7 校准)
- [3.8 单次转换和连续转换](#3.8 单次转换和连续转换)
- [3.9 扫描模式](#3.9 扫描模式)
- [4. ADC寄存器及库函数介绍](#4. ADC寄存器及库函数介绍)
- 小实验1:ADC单通道采集实验
- 小实验2:ADC单通道采集实验+DMA读取
- 小实验3:ADC多通道采集实验+DMA读取
- 项目:吸烟室管控系统
1 ADC介绍
1.1 什么是ADC?
全称:Analog-to-Digital Converter,指模拟/数字转换器。
ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。

12 位 ADC 是一种逐次逼近型模拟数字转换器(0~4095)。它有多达 18 个通道,可测量 16 个外部和 2 个内部信号源。各通道的 A/D 转换可以单次、连续、扫描或间断模式执行。 ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。
模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。
ADC 的输入时钟不得超过 14MHz,它是由 PCLK2 经分频产生。
STM32F103C8T6 ADC资源:ADC1、ADC2,10 个外部输入通道。
1.2 ADC工作原理(逐次逼近型)

1.3 ADC特性参数
ADC的性能指标
- 量程:能测量的电压范围
- 分辨率:ADC能辨别的最小模拟量,通常以输出二进制数的位数表示,比如:8、10、12、16位等;位数越多,分辨率越高,一般来说分辨率越高,转化时间越长
- 转化时间(采样时间):从转换开始到获得稳定的数字量输出所需要的时间称为转换时间。转换时间越长,转换结果相对越准确,但是转换速度就越慢
ADC特性
- 12 位精度下转换速度可高达1MHZ
- 供电电压:VSSA :0V,VDDA :2.4V~3.6V
- ADC输入范围:VREF- ≤ VIN ≤ VREF+,0~3.3V
- ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中
2 ADC框图


3 ADC的一些细节
3.1 输入通道

总共 2 个 ADC(ADC1,ADC2),每个 ADC 有 18 个转换通道: 16 个外部通道、 2 个内部通道(温度传感器、内部参考电压)。

外部的16个通道在转换时又分为规则通道和注入通道,其中规则通道最多有16路,注入通道最多有4路。
3.2 规则组/注入组


3.3 转换顺序
每个 ADC 规则通道只有一个数据寄存器,16个通道一起共用这个寄存器,所以需要指定规则转换通道的转换顺序。
规则通道中的转换顺序由三个寄存器控制:SQR1、SQR2、SQR3,它们都是32位寄存器。SQR寄存器控制着转换通道的数目和转换顺序,只要在对应的寄存器位SQx中写入相应的通道,这个通道就是第x个转换。

和规则通道转换顺序的控制一样,注入通道的转换也是通过注入寄存器来控制,只不过只有一个JSQR寄存器来控制,控制关系如下:

注入序列的转换顺序是从JSQx[ 4 : 0 ](x=4-JL[1:0])开始。只有当JL=4的时候,注入通道的转换顺序才会按照JSQ1、JSQ2、JSQ3、JSQ4的顺序执行。
3.4 触发转换方法
- 通过向控制寄存器 ADC-CR2 的 ADON 位写 1 来开启 ADC ,再将 SWSTART 位置 1 ,启动规则通道转换。
- 也可以通过外部事件(如定时器)进行转换。

3.5 转换时间
ADC 是挂载在 APB2 总线(PCLK2)上的,经过分频器得到 ADC 时钟(ADCCLK),最高 14 MHz。
c
转换时间=采样时间+12.5个周期

12.5个周期是固定的,一般我们设置 PCLK2=72M,经过 ADC 预分频器能分频到最大的时钟只能是 12M,采样周期设置为 1.5 个周期,算出最短的转换时间为 1.17us。
3.6 中断及事件

DMA请求(只适用于规则组)
规则组每个通道转换结束后,除了可以产生中断外,还可以产生DMA请求,我们利用DMA及时把转换好的数据传输到指定的内存里,防止数据被覆盖。
3.7 校准
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换中每个电容器上产生的误差。
通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束, CAL位被硬件复位,可以开始正常转换。建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。
建议在每次上电后执行一次校准。
3.8 单次转换和连续转换
单次转换:只转换一次
连续转换:转换一次之后,立马进行下一次转换
3.9 扫描模式
关闭扫描模式:只转换ADC_SQRx或ADC_JSQR选中的第一个通道
打开扫描模式:扫描所有被ADC_SQRx或ADC_JSQR选中的所有通道
4. ADC寄存器及库函数介绍






只列举部分,具体请查看STM32中文参考手册。
小实验1:ADC单通道采集实验
实验目的
使用 ADC1 采集通道 1 的电压值,通道 1 连接光敏电阻传感器。
流程

代码
main.c
c
adc.c
c
adc.h
c
小实验2:ADC单通道采集实验+DMA读取
实验目的
使用 ADC1 采集通道 1 的电压值+DMA读取,通道 1 连接光敏电阻传感器。
流程

代码
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"
uint16_t adc_result = 0;
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//adc相关初始化
adc_dma_init((uint32_t *)&adc_result);
printf("打印测试:hello world\r\n");
while(1)
{
//打印ADC转换结果
printf("adc result: %f\r\n",(float)adc_result/4096 * 3.3);
delay_ms(500);
}
}
adc.c
c
#include "adc.h"
//ADC句柄
ADC_HandleTypeDef adc_handle = {0};
//DMA句柄
DMA_HandleTypeDef dma_handle = {0};
//ADC配置
void adc_config(void)
{
adc_handle.Instance = ADC1; //选择ADC1
adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //数据右对齐
adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //不扫描
adc_handle.Init .ContinuousConvMode = ENABLE; //连续转换
adc_handle.Init.NbrOfConversion = 1; //转换个数为1
adc_handle.Init.DiscontinuousConvMode = DISABLE; //不采用间断模式
adc_handle.Init.NbrOfDiscConversion = 0; //间断模式个数为0
adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //采用软件触发
HAL_ADC_Init(&adc_handle); //调用初始化函数
//ADC校准
HAL_ADCEx_Calibration_Start(&adc_handle);
}
//DMA配置
void dma_config()
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel1; //DMA1通道5
dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //从外设到内存
//内存相关配置
dma_handle.Init .MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_CIRCULAR; //循环搬运
//初始化函数
HAL_DMA_Init(&dma_handle);
//链接串口和DMA
__HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}
//ADC相关硬件配置
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
//确认是否为ADC1
if(hadc->Instance == ADC1){
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
GPIO_InitTypeDef gpio_init_struct = {0};
//开启ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//设置PIN1口,模拟输入模式
gpio_init_struct.Pin = GPIO_PIN_1;
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
//GPIO初始化
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
//设置外设时钟选择为RCC外设ADC时钟
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//设置adc时钟分频因子 为6分频
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
//配置外设时钟
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
}
}
//ADC通道配置
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_config = {0};
//设置adc通道参数
adc_ch_config.Channel = ch; //设置通道
adc_ch_config.Rank = rank; //设置通道次序
adc_ch_config.SamplingTime = stime; //设置采样时间
//通道配置
HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}
//获取ADC的值
uint32_t adc_get_result(uint32_t ch)
{
//配置ADC通道
adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
//开始ADC转换
HAL_ADC_Start(&adc_handle);
//轮询方式等待ADC转换完成
HAL_ADC_PollForConversion(&adc_handle, 10);
//获取ADC转换结果
return (uint16_t)HAL_ADC_GetValue(&adc_handle);
}
//ADC和DMA初始化
void adc_dma_init(uint32_t *mar)
{
//ADC配置
adc_config();
//配置ADC通道
adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
//DMA配置
dma_config();
//开始ADC-DMA转换
HAL_ADC_Start_DMA(&adc_handle, mar, 1);
}
adc.h
c
#ifndef __ADC_H__
#define __ADC_H__
#include "sys.h"
//ADC和DMA初始化
void adc_dma_init(uint32_t *mar);
#endif
小实验3:ADC多通道采集实验+DMA读取
实验目的
使用 ADC1 采集通道 0 ~3 的电压值+DMA读取,通道 1 连接光敏电阻传感器
代码
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"
uint16_t adc_result[4] = {0};
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//adc相关初始化
adc_dma_init((uint32_t *)&adc_result);
printf("打印测试:hello world\r\n");
while(1)
{
//打印ADC转换结果
printf("通道0电压: %f\r\n",(float)adc_result[0]/4096 * 3.3);
printf("通道1电压: %f\r\n",(float)adc_result[1]/4096 * 3.3);
printf("通道2电压: %f\r\n",(float)adc_result[2]/4096 * 3.3);
printf("通道3电压: %f\r\n\r\n",(float)adc_result[3]/4096 * 3.3);
delay_ms(1000);
}
}
adc.c
c
#include "adc.h"
//ADC句柄
ADC_HandleTypeDef adc_handle = {0};
//DMA句柄
DMA_HandleTypeDef dma_handle = {0};
//ADC配置
void adc_config(void)
{
adc_handle.Instance = ADC1; //选择ADC1
adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //数据右对齐
adc_handle.Init.ScanConvMode = ADC_SCAN_ENABLE; //扫描
adc_handle.Init .ContinuousConvMode = ENABLE; //连续转换
adc_handle.Init.NbrOfConversion = 4; //转换个数为1
adc_handle.Init.DiscontinuousConvMode = DISABLE; //不采用间断模式
adc_handle.Init.NbrOfDiscConversion = 0; //间断模式个数为0
adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //采用软件触发
HAL_ADC_Init(&adc_handle); //调用初始化函数
//ADC校准
HAL_ADCEx_Calibration_Start(&adc_handle);
}
//DMA配置
void dma_config()
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel1; //DMA1通道5
dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //从外设到内存
//内存相关配置
dma_handle.Init .MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_CIRCULAR; //循环搬运
//初始化函数
HAL_DMA_Init(&dma_handle);
//链接串口和DMA
__HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}
//ADC相关硬件配置
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
//确认是否为ADC1
if(hadc->Instance == ADC1){
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
GPIO_InitTypeDef gpio_init_struct = {0};
//开启ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//设置PIN1口,模拟输入模式
gpio_init_struct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
//GPIO初始化
HAL_GPIO_Init(GPIOA,&gpio_init_struct);
//设置外设时钟选择为RCC外设ADC时钟
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//设置adc时钟分频因子 为6分频
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
//配置外设时钟
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
}
}
//ADC通道配置
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_config = {0};
//设置adc通道参数
adc_ch_config.Channel = ch; //设置通道
adc_ch_config.Rank = rank; //设置通道次序
adc_ch_config.SamplingTime = stime; //设置采样时间
//通道配置
HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}
//获取ADC的值
uint32_t adc_get_result(uint32_t ch)
{
//配置ADC通道
adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
//开始ADC转换
HAL_ADC_Start(&adc_handle);
//轮询方式等待ADC转换完成
HAL_ADC_PollForConversion(&adc_handle, 10);
//获取ADC转换结果
return (uint16_t)HAL_ADC_GetValue(&adc_handle);
}
//ADC和DMA初始化
void adc_dma_init(uint32_t *mar)
{
//ADC配置
adc_config();
//配置ADC通道
adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
adc_channel_config(&adc_handle, ADC_CHANNEL_2, ADC_REGULAR_RANK_2, ADC_SAMPLETIME_239CYCLES_5);
adc_channel_config(&adc_handle, ADC_CHANNEL_3, ADC_REGULAR_RANK_3, ADC_SAMPLETIME_239CYCLES_5);
adc_channel_config(&adc_handle, ADC_CHANNEL_4, ADC_REGULAR_RANK_4, ADC_SAMPLETIME_239CYCLES_5);
//DMA配置
dma_config();
//开始ADC-DMA转换
HAL_ADC_Start_DMA(&adc_handle, mar, 4);
}
adc.h
c
#ifndef __ADC_H__
#define __ADC_H__
#include "sys.h"
//ADC和DMA初始化
void adc_dma_init(uint32_t *mar);
#endif
项目:吸烟室管控系统
项目需求
- 使用 mq-2 获取环境烟雾值,并显示在 LCD1602 上;
- 按键修改阈值,并显示在 LCD1602 上;
- 烟雾值超过阈值时,蜂鸣器长响,风扇打开;烟雾值小于阈值时,蜂鸣器响,风扇关闭;
硬件清单
LCD1602
mq-2
蜂鸣器
继电器(模拟风扇)
按键
杜邦线
STM32
ST-Link
USB转TTL
硬件接线


项目框图

完整代码
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "adc.h"
#include "beep.h"
#include "fan.h"
#include "key.h"
#include "lcd1602.h"
int main(void)
{
float smoke_value = 0;
float limit_value = 1.0;
uint8_t key_num = 0;
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//串口1初始化
uart1_init(115200);
//ADC初始化
adc_dma_init();
//蜂鸣器初始化
beep_init();
//风扇初始化
fan_init();
//按键初始化
key_init();
//LCD1602初始化
lcd1602_init();
printf("打印测试:hello world\r\n");
//测试
//lcd1602_show_line(1, 1, "Smoking: 0.0 V");
//lcd1602_show_line(1, 2, "Limit: - 0.0 + ");
while(1)
{
//获取按下键的值
key_num = key_scan();
//比较判断
if(key_num == 1){
limit_value += 0.1;
}else if(key_num == 2){
limit_value -= 0.1;
}
//屏幕显示烟雾阈值
lcd1602_display_limit(limit_value);
//获取烟雾值
smoke_value = adc_get_smoke();
//屏幕显示烟雾值
lcd1602_display_smoke(smoke_value);
//比较判断
if(smoke_value > limit_value){
beep1_on();
fan_on();
}else{
beep1_off();
fan_off();
}
}
}
adc.c
c
#include "adc.h"
//定义ADC值
uint16_t adc_value = 0;
//ADC句柄
ADC_HandleTypeDef adc_handle = {0};
//DMA句柄
DMA_HandleTypeDef dma_handle = {0};
//ADC配置
void adc_config(void)
{
adc_handle.Instance = ADC1; //选择ADC1
adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //数据右对齐
adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE; //不扫描
adc_handle.Init .ContinuousConvMode = ENABLE; //连续转换
adc_handle.Init.NbrOfConversion = 1; //转换个数为1
adc_handle.Init.DiscontinuousConvMode = DISABLE; //不采用间断模式
adc_handle.Init.NbrOfDiscConversion = 0; //间断模式个数为0
adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //采用软件触发
HAL_ADC_Init(&adc_handle); //调用初始化函数
//ADC校准
HAL_ADCEx_Calibration_Start(&adc_handle);
}
//DMA配置
void dma_config()
{
__HAL_RCC_DMA1_CLK_ENABLE();
dma_handle.Instance = DMA1_Channel1; //DMA1通道1
dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY; //从外设到内存
//内存相关配置
dma_handle.Init .MemDataAlignment = DMA_MDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.MemInc = DMA_MINC_ENABLE; //数据增长方式
//外设相关配置
dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; //数据对齐方式
dma_handle.Init.PeriphInc = DMA_PINC_DISABLE; //数据增长方式
//优先级和模式
dma_handle.Init.Priority =DMA_PRIORITY_MEDIUM;
dma_handle.Init.Mode = DMA_CIRCULAR; //循环搬运
//初始化函数
HAL_DMA_Init(&dma_handle);
//链接串口和DMA
__HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}
//ADC相关硬件配置
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
//确认是否为ADC1
if(hadc->Instance == ADC1){
RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
GPIO_InitTypeDef gpio_init_struct = {0};
//开启ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//设置PIN1口,模拟输入模式
gpio_init_struct.Pin = GPIO_PIN_0;
gpio_init_struct.Mode = GPIO_MODE_ANALOG;
//GPIO初始化
HAL_GPIO_Init(GPIOB,&gpio_init_struct);
//设置外设时钟选择为RCC外设ADC时钟
adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//设置adc时钟分频因子 为6分频
adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
//配置外设时钟
HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
}
}
//ADC通道配置
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch,uint32_t rank, uint32_t stime)
{
ADC_ChannelConfTypeDef adc_ch_config = {0};
//设置adc通道参数
adc_ch_config.Channel = ch; //设置通道
adc_ch_config.Rank = rank; //设置通道次序
adc_ch_config.SamplingTime = stime; //设置采样时间
//通道配置
HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}
//获取ADC的值
uint32_t adc_get_result(uint32_t ch)
{
//配置ADC通道
adc_channel_config(&adc_handle, ch, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
//开始ADC转换
HAL_ADC_Start(&adc_handle);
//轮询方式等待ADC转换完成
HAL_ADC_PollForConversion(&adc_handle, 10);
//获取ADC转换结果
return (uint16_t)HAL_ADC_GetValue(&adc_handle);
}
//ADC和DMA初始化
void adc_dma_init(void)
{
//ADC配置
adc_config();
//配置ADC通道
adc_channel_config(&adc_handle, ADC_CHANNEL_8, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
//DMA配置
dma_config();
//开始ADC-DMA转换
HAL_ADC_Start_DMA(&adc_handle, (uint32_t *)&adc_value, 1);
}
//ADC烟雾值获取
float adc_get_smoke(void)
{
return (float)adc_value /4096 * 3.3;
}
adc.h
c
#ifndef __ADC_H__
#define __ADC_H__
#include "sys.h"
//ADC和DMA初始化
void adc_dma_init(void);
//ADC烟雾值获取
float adc_get_smoke(void);
#endif
beep.c
c
#include "beep.h"
#include "sys.h"
//初始化GBIO口函数
void beep_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_5; //beep1对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭beep1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
}
//开启beep1的函数
void beep1_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); //拉低beep1引脚,开启beep1
}
//关闭beep1的函数
void beep1_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //拉高beep1引脚,关闭beep1
}
//翻转beep1的函数
void beep1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5); //翻转beep1引脚电平
}
beep.h
c
#ifndef __BEEP_H__
#define __BEEP_H__
//初始化GBIO口函数
void beep_init(void);
//开启beep1的函数
void beep1_on(void);
//关闭beep1的函数
void beep1_off(void);
//翻转beep1的函数
void beep1_toggle(void);
#endif
fan.c
c
#include "fan.h"
#include "sys.h"
//初始化GBIO口函数
void fan_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_6; //fan对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭plugin
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
}
//打开fan的函数
void fan_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_RESET); //拉低fan引脚,关闭fan
}
//关闭fan的函数
void fan_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET); //拉高fan引脚,关闭fan
}
//翻转fan的函数
void fan_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6); //翻转fan引脚电平
}
//返回fan的状态
uint8_t fan_status_get(void)
{
return (uint8_t)HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_6);
}
fan.h
c
#ifndef __FAN_H__
#define __FAN_H__
#include "stdint.h"
#define FAN_STATUS_ON 0
#define FAN_STATUS_OFF 0
//初始化GBIO口函数
void fan_init(void);
//打开fan的函数
void fan_on(void);
//关闭fan的函数
void fan_off(void);
//翻转fan的函数
void fan_toggle(void);
//返回继电器的状态
uint8_t fan_status_get(void);
#endif
lcd1602.c
c
#include "lcd1602.h"
#include "sys.h"
#include "delay.h"
#include "stdio.h"
//RS引脚定义
#define RS_GPIO_Port GPIOB
#define RS_GPIO_Pin GPIO_PIN_1
#define RS_HIGH HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_SET);
#define RS_LOW HAL_GPIO_WritePin(RS_GPIO_Port,RS_GPIO_Pin,GPIO_PIN_RESET);
//RW引脚定义
#define RW_GPIO_Port GPIOB
#define RW_GPIO_Pin GPIO_PIN_2
#define RW_HIGH HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_SET);
#define RW_LOW HAL_GPIO_WritePin(RW_GPIO_Port,RW_GPIO_Pin,GPIO_PIN_RESET);
//EN引脚定义
#define EN_GPIO_Port GPIOB
#define EN_GPIO_Pin GPIO_PIN_10
#define EN_HIGH HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_SET);
#define EN_LOW HAL_GPIO_WritePin(EN_GPIO_Port,EN_GPIO_Pin,GPIO_PIN_RESET);
//LCD1602初始化
void lcd1602_init(void)
{
//初始化GPIO
lcd1602_gpio_init();
//上电初始化
lcd1602_start();
}
//LCD1602GPIO初始化
void lcd1602_gpio_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//使能GPIOB时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 |
GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; //D0-D7对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_10; //RS、RW、EN对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
}
//LCD1602启动
void lcd1602_start(void)
{
//(1)延时 15ms
delay_ms(15);
//(2)写指令 38H(不检测忙信号)
lcd1602_write_cmd(0x38);
//(3)延时 5ms
delay_ms(5);
//(4)以后每次写指令,读/写数据操作均需要检测忙信号
//(5)写指令 38H:显示模式设置
lcd1602_write_cmd(0x38);
//(6)写指令 08H:显示关闭
lcd1602_write_cmd(0x08);
//(7)写指令 01H:显示清屏
lcd1602_write_cmd(0x01);
//(8)写指令 06H:显示光标移动设置
lcd1602_write_cmd(0x06);
//(9)写指令 0CH:显示开及光标设置
lcd1602_write_cmd(0x0C);
}
//LCD1602写指令
void lcd1602_write_cmd(char cmd)
{
RS_LOW;
RW_LOW;
EN_LOW;
GPIOA->ODR = cmd;
delay_ms(5);
EN_HIGH;
delay_ms(5);
EN_LOW;
}
//LCD1602写数据
void lcd1602_write_data(char data)
{
RS_HIGH;
RW_LOW;
EN_LOW;
GPIOA->ODR = data;
delay_ms(5);
EN_HIGH;
delay_ms(5);
EN_LOW;
}
//LCD1602显示一个字符
void lcd1602_show_char(char row,char col,char data)
{
//显示位置
if(row == 1){
lcd1602_write_cmd(0x00+0x80+col-1);
}else if(row ==2){
lcd1602_write_cmd(0x40+0x80+col-1);
}
//显示数据
lcd1602_write_data(data);
}
//LCD1602显示一行
void lcd1602_show_line(char row,char col,char *string)
{
switch(row){
case 1:
lcd1602_write_cmd(0x00+0x80+col-1);
while(*string){
lcd1602_write_data(*string);
string++;
}
break;
case 2:
lcd1602_write_cmd(0x40+0x80+col-1);
while(*string){
lcd1602_write_data(*string);
string++;
}
break;
}
}
//LCD1602显示烟雾值
void lcd1602_display_smoke(float smoke_value)
{
char msg[16] = {0};
sprintf(msg, "Smoke: %0.2f V",smoke_value);
lcd1602_show_line(1, 1, msg);
}
//LCD1602显示烟雾阈值
void lcd1602_display_limit(float limit_value)
{
char msg[16] = {0};
sprintf(msg, "Limit: - %0.2f +",limit_value);
lcd1602_show_line(2, 1, msg);
}
lcd1602.h
c
#ifndef __LCD1602_H__
#define __LCD1602_H__
//LCD1602初始化
void lcd1602_init(void);
//LCD1602GPIO初始化
void lcd1602_gpio_init(void);
//LCD1602启动
void lcd1602_start(void);
//LCD1602写指令
void lcd1602_write_cmd(char cmd);
//LCD1602写数据
void lcd1602_write_data(char data);
//LCD1602显示一个字符
void lcd1602_show_char(char row,char col,char data);
//LCD1602显示一行
void lcd1602_show_line(char row,char col,char *string);
//LCD1602显示烟雾值
void lcd1602_display_smoke(float smoke_value);
//LCD1602显示烟雾阈值
void lcd1602_display_limit(float limit_value);
#endif
key.c
c
#include "key.h"
#include "delay.h"
//初始化GPIO
void key_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_13 | GPIO_PIN_14; //KEY1,KEY2对应引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; //输入
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
}
//按键扫描函数
uint8_t key_scan(void)
{
//检测按键1是否按下
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13) == GPIO_PIN_RESET){
//消抖
delay_ms(10);
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13) == GPIO_PIN_RESET){
//如果确实按下,那么等待按键松开
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_13) == GPIO_PIN_RESET);
//返回按键值
return 1;
}
}
//检测按键2是否按下
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_RESET){
//消抖
delay_ms(10);
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_RESET){
//如果确实按下,那么等待按键松开
while(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_14) == GPIO_PIN_RESET);
//返回按键值
return 2;
}
}
//返回默认值
return 0;
}
key.h
c
#ifndef __KEY_H__
#define __KEY_H__
#include "sys.h"
void key_init(void);
uint8_t key_scan(void);
#endif
项目实物图
