一、ADC概述
ADC(模拟量转数字量转换器),在 STM32 开发中,利用 ADC 端口的电压数据,转换为对应的具体数字量数据内容。可通过 ADC 方式获取常用数据内容有:
- 光敏电阻、电池电量、油箱油量
ADC 转换的数据,可用于执行器控制行为,如低电量警告、油箱油量不足警告(即【阈值警告】 【阈值处理】 ,通过设置阈值,数据达到或超出时触发相应警示或控制逻辑 ) 。
二、ADC 工作原理
1.ADC****的主要特征


2. ADC****内部架构框图

一、整体架构与核心组件
- 模拟输入部分
- 外部输入通道 :
ADCx_IN0 ~ ADCx_IN15
是 16 组外部模拟信号输入引脚,可连接光敏电阻、电压传感器等外设,采集外部模拟电压。- 内部传感器 :集成温度传感器(需结合
V_REFINT
参考电压工作 ),用于检测芯片内部温度,方便实现温度补偿、过热保护等功能。- 模拟多路开关:最多支持 16 通道规则通道、4 通道注入通道切换,按需选通一路模拟信号送入转换器,实现多通道分时复用。
- 转换核心
- 模拟至数字转换器 :采用逐次逼近法,以
ADCCLK
(来自 ADC 预分频器,需配置分频保证时钟稳定 )为基准时钟,将模拟电压与内部基准电压比较,逐位确定数字量,完成0~3.3V
(Vref+ = 3.3V
、Vref- = 0V
时 )模拟信号到 12 位数字量(范围0000 0000 0000 ~ 1111 1111 1111
)的转换。- 参考电压 :
V_REF+
、V_REF-
是 ADC 转换基准,决定量程。3.3V
输入时,电压分辨率为3.3V/4096 ≈ 0.0008V
,即 1 个数字量对应约0.8mV
电压变化。
二、通道与转换模式
- 规则通道
- 通道特点:最多 16 通道,用于常规、连续的多通道采样,像循环采集电池电压、多路传感器数据。
- 触发控制 :通过
EXTSEL[2:0]
选择定时器触发源(如TIM1_CH1
、TIM2_CH2
等 ),实现定时采样;也可由外部中断触发,灵活适配不同场景需求。- 数据存储 :转换结果存入
规则通道数据寄存器(16 位)
,支持 DMA 请求,转换完成后直接通过 DMA 传输数据到内存,减轻 CPU 负担。- 注入通道
- 通道特点:最多 4 通道,优先级高于规则通道,用于紧急、需快速响应的采样(如安全阈值监测 )。
- 触发控制 :由
JEXTSEL[2:0]
选定时器触发源(如TIM1_TRGO
、TIM4_CH3
等 ),或JEXTRIG
控制位手动触发。- 数据存储 :结果存入
注入通道数据寄存器(4×16 位)
,可独立处理,快速响应特殊需求。
三、中断与阈值监测(模拟看门狗)
- 中断机制
- 转换结束中断 :规则通道(
EOC
)、注入通道(JEOC
)转换完成时,置标志位并可使能中断,触发ADC中断
到 NVIC,通知 CPU 读取数据,实现实时处理。- 阈值中断 :模拟看门狗比较转换结果与
阈值高限(12 位)
、阈值低限(12 位)
,超出范围置AWD
标志位,使能中断(AWDIE
)后触发中断,用于电压超限报警(如电池过压 / 欠压、传感器异常 )。- 模拟看门狗:实时监控 ADC 转换结果,一旦超出设定阈值,立即触发中断或标志位,快速响应异常,保障系统安全。
四、工作流程总结
- 信号输入 :外部 / 内部模拟信号经
ADCx_IN
或内部传感器进入,模拟多路开关选通通道。- 触发转换 :规则 / 注入通道通过定时器、外部中断等触发,启动
模拟至数字转换器
工作。- 数据转换:逐次逼近法转换模拟电压为 12 位数字量,存入对应数据寄存器。
- 结果处理:可触发中断通知 CPU 读取,或通过 DMA 传输数据;模拟看门狗实时监测,超限触发报警,实现从模拟信号采集到数字信号处理、异常响应的完整流程,支撑 STM32 对模拟量的精准采集与智能控制 。
五、举例讲解
一、场景与需求
- 常规任务 :每隔 100ms 采集 3 路信号
- 电池电压(
ADC1_IN0
,规则通道)- 车外温度(
ADC1_IN1
,规则通道,接温度传感器)- 光照强度(
ADC1_IN2
,规则通道,接光敏电阻)- 紧急任务 :实时监测电池电压,一旦超出
2.8V~3.6V
范围,立即触发报警
- 复用电池电压信号到
ADC1_IN8
(注入通道,优先级更高)二、工作流程拆解(多转换触发逻辑)
1. 硬件连接与通道准备
- 外部信号接入 :
- 电池电压、温度传感器、光敏电阻的模拟信号,分别接到
ADC1_IN0
/IN1
/IN2
(规则通道);- 电池电压同时接到
ADC1_IN8
(注入通道,用于紧急阈值监测)。- 多路开关配置 :
- 规则通道:使能
IN0
/IN1
/IN2
,共 3 路,用于循环采样;- 注入通道:使能
IN8
,共 1 路,用于紧急监测。2. 触发转换的两种方式
(1) 规则通道触发(常规采集)
- 触发源 :定时器触发(如
TIM3_TRGO
,配置为 100ms 触发一次)。- 流程 :
① 定时器每 100ms 产生一个触发信号 → 触发规则通道转换;
② 模拟多路开关按顺序选通IN0
→IN1
→IN2
;
③ ADC 依次对 3 路信号进行转换(逐次逼近法),结果存入规则数据寄存器。(2) 注入通道触发(紧急监测)
- 触发源:软件触发 + 模拟看门狗(双重保障)。
- 流程 :
① 初始触发 :系统启动时,手动触发一次注入通道转换(读取初始电池电压);
② 持续监测 :ADC 转换后,模拟看门狗自动比较结果与阈值(2.8V~3.6V
):
- 若在范围内:不触发中断,等待下一次规则通道触发时,顺带重新触发注入转换(或定时触发);
- 若超出范围:立即置
AWD
标志位 → 触发注入中断 → CPU 跳转到中断函数处理(如点亮故障灯、记录日志)。3. 数据处理与响应
规则通道数据 :
转换完成后,通过 DMA 自动将 3 路结果搬运到内存数组 → 程序读取数组,计算电池电量、温度值、光照强度,更新仪表盘显示。
注入通道数据 :
若触发中断(电压超限):
① 中断函数中读取注入数据寄存器 → 获取实时电池电压;
② 执行紧急逻辑(如:点亮红色故障灯、发送 CAN 报警帧、限制非关键用电设备)。
4. 多转换并行的关键逻辑
- 优先级:注入通道优先级 > 规则通道。若规则通道转换中触发注入中断,ADC 会暂停规则转换,优先处理注入通道,保障紧急任务响应。
- 资源复用:同一模拟信号(如电池电压)可接入多个通道(规则 + 注入),实现 "常规轮询 + 紧急监测" 的差异化需求。
3. ADC****数据转换规则
ADC 转换核心信息:
- 转换方法:ADC 转换器采用逐次逼近法进行数据转换
- 采样精度 :12 位,数值范围
0000 0000 0000 ~ 1111 1111 1111
(对应十进制0 ~ 4095
) - 参考电压 :
Vref+ = 3.3V
,Vref- = 0V
- 电压分辨率 :
3.3V / 4096 ≈ 0.0008V
(即 1 个 ADC 数值对应约 0.8mV 电压 ) - 示例场景 :假设 ADC 读取到电压为
1.83V
,可基于上述参数换算数字量

三、ADC 编程实现和相关寄存器

1. ADC 时钟问题
- 分析依据:根据原理图进行分析
- 引脚与通道对应:当前引脚为 PF8 引脚,其对应的 ADC 通道是 ADC3_IN6
- 时钟归属:ADC3 所在的时钟为 APB2 总线时钟,即 ADC3 的时钟由 APB2 总线提供 ,在 STM32 中,不同的外设会挂载在不同的总线(如 APB1、APB2 等 )上,其时钟由对应总线时钟源分频等配置后提供,这里明确了 ADC3 依赖 APB2 总线时钟来进行工作时序的驱动 。

2.ADC_CR1****寄存器


3. ADC_CR2****寄存器
当前寄存器主要控制,ADC 采用数据通道,通信触发规则,数据对齐方式,校验和 ADC开启。


ADC_CR2寄存器的配置内容:
- SWSTART(位 22):置 1,用于开始规则通道的转换
- EXTTRIG(位 20):置 1,使能规则通道的外部触发转换模式
- EXTSEL(位 19 - 17):设为 111,选择启动规则通道组转换的外部事件
- ALIGN(位 11):置 0,配置数据右对齐
- RSTCAL(位 3):置 1,执行复位校准操作
- CAL(位 2):置 1,进行 A/D 校准
- CONT(位 1):置 1,使能连续转换模式
- ADON(位 0):置 1,开启 A/D 转换器 ,各配置项共同定义了 ADC 的转换启动、触发、校准、数据对齐及运行模式等关键参数 。
总结理解:
当
EXTRIG
位置为 0 的时候,转换完全是由软件也就是手动触发,每次将SWSTART
置为 1 的时候触发一次转换,转换的配置按照预设的通道序列(ADC_SQRx
)、采样时间(ADC_SMPRx
)、分辨率(ADC_CR1.RES
)等位配置执行,硬件会在启动转换的同时自动清除SWSTART
位。而当
SWSTART
置 1、EXTRIG
也为 1,且EXTSEL
为 111 时,转换的触发源仍为软件写SWSTART=1
,但触发信号需先进入外部触发选择系统,再由该系统转发给 ADC 以触发转换;转换配置同样遵循预设的通道、采样时间等位配置,硬件也会在启动转换时自动清除SWSTART
位,核心是通过 "外部触发框架" 实现软件触发,可复用外部触发的联动功能。两种软件触发场景的关键差异对比表
维度 EXTRIG=0
+ 写SWSTART
(纯软件触发)EXTRIG=1
+EXTSEL=111
+ 写SWSTART
(软件模拟外部触发)触发路径 软件 → 直接发送触发信号给 ADC,无中间环节 软件 → 写 SWSTART
生成信号 → 外部触发选择系统(EXTSEL
控制)→ 转发信号给 ADC硬件联动能力 仅执行基础转换,结果需软件手动读取(或单独配置中断) 可复用外部触发的配套功能,如转换后自动启动 DMA 传输、触发特定中断等 触发延迟 低(信号直达 ADC,无额外逻辑开销) 略高(多一层外部触发系统的信号转发,存在微小延迟) 核心适用场景 简单手动触发需求,如初始化检测、按键触发单次采集,无需额外硬件联动 需软件触发 + 外部触发配套功能的场景,如软件手动触发后希望 DMA 自动搬运数据,或统一触发逻辑框架 SWSTART
清除时机硬件启动转换的同时自动清 0 硬件启动转换的同时自动清 0(与纯软件触发一致) 外部事件干扰 无(外部触发系统已关闭,定时器、GPIO 等无法触发) 无( EXTSEL=111
仅响应软件SWSTART
信号,其他外部事件不干扰)
4. ADC_SMPR****寄存器


5. ADC_SQR****寄存器
在 ADC 规则通道配置中,需两个寄存器配合实现:
- ADC_SQR1:用于配置规则通道开启的数量(即决定要转换的规则通道总数 )。
- ADC_SQR3 :用于配置 SQ1(规则通道序列中的第 1 个转换通道 )寄存器位对应的具体通道,此处为
ADC3_IN6
(指定第 1 个转换的规则通道是ADC3
的IN6
通道 ) 。

总结理解:
当配置了多个规则序列(如 SQ1 = 通道 A、SQ2 = 通道 B)并开启连续转换(CONT=1)时,扫描模式(SCAN)的状态决定了 ADC 的采集行为:开启扫描模式(SCAN=1)时,ADC 会按 SQ1→SQ2 的顺序循环采集所有配置通道,转换结果交替存入 ADC_DR(前序数据会被覆盖);关闭扫描模式(SCAN=0)时,ADC 仅重复采集 SQ1 通道,完全忽略其他序列配置,ADC_DR 中始终存储 SQ1 通道的最新数据。简言之,SCAN 位是多通道序列执行的总开关,控制 ADC 是否按完整序列顺序采集,还是仅固定采集第一个通道。
四、示例代码:
adc.c:
cpp
#include "adc.h"
void LSEN_Init(void)
{
// 1. RCC 时钟使能,需要提供 GPIOF 和 ADC3
RCC->APB2ENR |= (0x01 << 7) | (0x01 << 15);
// 2. GPIOF --> PF8 模拟输入模式 ==> 0000
GPIOF->CRH &= ~(0x0F);
/*
3. ADC 配置
*/
/*
3.1 ADC 预分配倍数配置
因为当前 STM32F103ZET6 对应 72 MHz,
ADCCLK 不得大于 14 MHz,预分频倍数最小可以选择 6
*/
RCC->CFGR &= ~(0x03 << 14);
RCC->CFGR |= (0x02 << 14);
/*
3.2 配置 ADC 的工作通道
选择工作通道为 ADC3_IN6,规则通道打开一个,配置 SQ1
*/
ADC3->SQR1 &= ~(0x0F << 20);
ADC3->SQR3 &= ~(0x1F);
ADC3->SQR3 |= 0x06;
/*
3.3 ADC 采用周期
采样周期选择 239.5 + 12.5 最大 ADC 采样周期,可以
获取到更大的数据精度。
*/
ADC3->SMPR2 |= (0x07 << 18);
/*
3.4. 配置 ADC CR 寄存器相关内容
CR1
- DUALMOD位 [19:16] : ADC 独立模式 ==> 0000
- SCAN [位8] : 扫描模式关闭 ==> 0
CR2
- SWSTART [位22] : 开始转换规则通道 ==> 1
- EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
- EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
- ALIGN [位11]::数据对齐(Data alignment) ==> 0 右对齐
- CONT [位1]::连续转换(Continuous conversion) ==> 1
*/
ADC3->CR1 &= ~(0x0F << 16);
ADC3->CR1 &= ~(0x01 << 8);
ADC3->CR2 &= ~(0xFFFFFFFF);
ADC3->CR2 |= (0x01 << 22); // SWSTART [位22] : 开始转换规则通道 ==> 1
ADC3->CR2 |= (0x01 << 20); // EXTTRIG [位20]:规则通道的外部触发转换模式 ==> 1
ADC3->CR2 |= (0x07 << 17); // EXTSEL [位19:17]: 选择启动规则通道组转换的外部事件 ==> 111
ADC3->CR2 &= ~(0x01 << 11); // ALIGN [位11]::数据对齐(Data alignment) ==> 0 右对齐
ADC3->CR2 |= (0x01 << 1);
/*
3.5 ADC 复位 + 校准
ADC 自校准 + 重启过程
- RSTCAL [位3]::复位校准(Reset calibration) ==> 1
- CAL [位2]::A/D校准(A/D Calibration) ==> 1
- ADON [位0]::开/关A/D转换器(A/D converter ON/ OFF) ==> 1
*/
ADC3->CR2 &= ~(0x01); // 关闭 ADC
Delay_ms(10); // 延时 10 ms
ADC3->CR2 |= 0x01; // 打开 ADC
/*
开始复位校准,给予对应寄存器标志位 1,如果 ADC 复位校准结束
对应寄存器位置硬件清除为 0
*/
ADC3->CR2 |= (0x01 << 3);
// while 循环是等待当前复位校准结束
while ((ADC3->CR2 & (0x01 << 3)));
Delay_ms(10);
/*
开始 A/D 校准,给予对应寄存器标志位 1,ADC A/D 校准之后
对应寄存器位置硬件清除为 0
*/
ADC3->CR2 |= (0x01 << 2);
// while 循环是等待当前 A/D 校准结束
while ((ADC3->CR2 & (0x01 << 2)));
ADC3->CR2 |= 0x01; // 打开 ADC
}
u16 LSEN_GetValue(void)
{
/*
ADC->SR 状态寄存器 EOC [位1] 位置,如果数据未转换完成
EOC 为 0 ,转换完成 EOC 为 1
*/
while (!(ADC3->SR & (0x01 << 1)));
return ADC3->DR;
}
adc.h:
cpp
#ifndef _ADC_H
#define _ADC_H
#include "stm32f10x.h"
#include "delay.h"
/**
* @brief 光敏电阻初始化函数
*/
void LSEN_Init(void);
u16 LSEN_GetValue(void);
#endif
delay.c:
cpp
#include "delay.h"
void Delay_us(u32 us)
{
while (us--)
{
// 利用 __NOP() 操作占用 MCU 一次执行周期特征,调用 72 个 __NOP
// 不建议使用 for 循环或者 while 循环,循环判断都需要占用一个 MCU 执行周期
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
__NOP();__NOP();
}
}
void Delay_ms(u32 ms)
{
Delay_us(ms * 1000);
}
delay.h:
cpp
#ifndef _DELAY_H
#define _DELAY_H
#include "stm32f10x.h"
/**
* @brief 延时微秒控制函数,延时单位是 us
*
* @param us 延时微秒时间
*/
void Delay_us(u32 us);
/**
* @brief 延时毫秒控制函数,延时单位是 ms
*
* @param ms 延时毫秒时间
*/
void Delay_ms(u32 ms);
#endif
usart.c:
cpp
#include "usart1.h"
USART1_Data usart1_val = {0};
void USART1_Init(u32 brr)
{
/*
1. 时钟使能 GPIOA 和 USART1,两者都在 APB2 时钟控制
USART1 对应位 14,GPIOA 对应位2
*/
RCC->APB2ENR |= (0x01 << 2) | (0x01 << 14);
/*
2. PA9 和 PA10 GPIO 配置
PA9 是 MCU 的 TX 数据发送端,GPIO 工作模式选择【复用推挽输出模式】
PA10 是 MCU 的 RX 数据发送端,GPIO 工作模式选择【浮空输入模式】
*/
GPIOA->CRH &= ~(0x00FF << 4);
GPIOA->CRH |= 0x0B << 4; // PA9 --> TX【复用推挽输出模式】
GPIOA->CRH |= 0x04 << 8; // PA10 --> RX【浮空输入模式】
/*
3. USART1 串口配置
3.1 8n1 配置,NRZ 数据格式配置,8个数据位,0 个校验位,1 个停止位
3.2 USART1 对应 TE 和 RE 开启,打开 USART1 的发送数据和读取数据能力
3.3 USART1 BRR 波特率配置
*/
// 3.1 8n1 配置,如果仅使用寄存器方式配置当前代码,可以省略一下过程
// 当前代码是为了后续的 【标准库】和【Hal库】,也是代码逻辑的一部分
USART1->CR1 &= ~(0x01 << 12); // 【8】USART1->CR1 控制寄存器对应 M (位12) 明确当前数据字长为 8 数据位
USART1->CR1 &= ~(0x01 << 10); // 【n】USART1->CR1 控制寄存器对应 PCE (位10), 明确当前不使用校验位
USART1->CR2 &= ~(0x03 << 12); // 【1】USART1->CR2 控制寄存器对应 STOP (位13,12), 限制当前数据停止位为 1
// 3.2 USART1 对应 TE 和 RE 开启
USART1->CR1 |= (0x03 << 2); // TE(位3) RE(位2) 进行赋值 1 开始操作
// 3.3 USART1 BRR 波特率配置
// 假设波特率是 115200 ==> USARTDIV 数据
float usart_div = 72 * 1000 * 1000 / (16 * brr);
// usart_div == 39.0625
/*
将 usart_div 进行拆解,分别对应整数部分和小数部分内容,提供给当前 USART1 中用于
计算波特率对应寄存器位。
*/
int usart_div_Mantissa = (u32)usart_div;
int usart_div_fraction = (u32)((usart_div - usart_div_Mantissa) * 16);
// 两个数据进行组合 提供给 USART1 波特率寄存器的数据为
USART1->BRR |= (usart_div_Mantissa << 4) | usart_div_fraction;
// 4. 启动 USART1
USART1->CR1 |= (0x01 << 13);
}
void USART1_SendByte(u8 byte)
{
/*
利用 USART1_SR 寄存器,判断之前的数据内容是否发送完成,如果没有
发送完成,本次发送操作进入【阻塞状态】
如果 USART1_SR TC ==> 0 表示之前的数据发送未完成
如果 USART1_SR TC ==> 1 表示之前的数据发送完毕
TC Transmission Complete
*/
while (0 == (USART1->SR & (0x01 << 6)));
/*
将需要发送的数据存储到 USART1->DR 数据寄存器中,
DR 会将数据直接提供给 TDR 寄存器,TDR 寄存器会将
数据提供给移位寄存器,SR 寄存器 TC TC 寄存器位置 0
发送完毕会将 SR 寄存器的中,TC 寄存器位置修改为 1
*/
USART1->DR = byte;
}
void USART1_SendBuffer(u8 *buffer, u16 count)
{
while (count--)
{
USART1_SendByte(*buffer);
buffer++;
}
}
void USART1_SendString(const char * str)
{
while (*str)
{
USART1_SendByte(*str);
str++;
}
}
u8 USART1_ReceiveByte(void)
{
u8 data = 0;
/*
判断在 USART1->SR 寄存器中,对应的 RXNE (Read data register not empty) 标志位
如果没有数据可以收到,RXNE 为 0
如果有数据可以读取,RXNE 为 1
while 进行 RXNE 标志位判断,如果没有数据当前循环【阻塞后续代码】
*/
while (0 == (USART1->SR & (0x01 << 5)));
data = (u8)USART1->DR;
return data;
}
/*
非重要知识点,仅实现 printf 函数功能重定向,可以实现
printf 打印操作数据 USART1 发送到 PC
*/
int fputc(int c, FILE *stream)
{
USART1_SendByte(c);
return c;
}
void USART1_Interrupt_Enable(void)
{
/*
当前 USART1 的控制寄存器中,
打开 IDLEIE 数据总线空闲中断使能
打开 RXNEIE 数据总线空闲中断使能
*/
USART1->CR1 |= (0x01 << 4) | (0x01 << 5);
/*
设置当前 USART1 对应的中断优先级为 0001 在
全局优先级设置为 2 的情况下 占先 0 次级 1
*/
NVIC_SetPriority(USART1_IRQn, 1); // 0001 占先 0 次级 1
/*
告知当前 MCU 使能对应的 USART1_IRQn 中断
*/
NVIC_EnableIRQ(USART1_IRQn);
}
/*
完成 USART1 对应的 USART1_IRQn 对应的中断处理函数
当前中断处理函数是用于接收的数据内容进行处置操作,将接收的数据
存储到 USART1_Data 结构体中,对应的 u8 data[DATA_SIZE] 数组
*/
void USART1_IRQHandler(void)
{
u32 val = 0;
/*
usart1_val.flag ==> 1 表示当前数据接收完毕,同时已经回显到
PC 端 USART 工具
*/
if (usart1_val.flag)
{
// 对当前数据空间进行擦除,
memset(&usart1_val, 0, sizeof(USART1_Data));
}
/*
如果当前触发的中断为 【RXNE 中断】,表示数据在通过串口
传递到 MCU 中
*/
if (USART1->SR & (0x01 << 5))
{
usart1_val.data[usart1_val.count++] = USART1->DR;
/*
当前接收到的有效字节个数 == DATA_SIZE,当前数据缓冲区数组已满
*/
if (DATA_SIZE == usart1_val.count)
{
USART1_SendBuffer(usart1_val.data, usart1_val.count);
usart1_val.flag = 1;
}
}
/*
如果当前数据总线空闲 【IDLE 中断】,表示数据传递完毕
*/
if (USART1->SR & (0x01 << 4))
{
/*
表示当前数据接收已完成
*/
usart1_val.flag = 1;
/*
需要完成对于当前 USART1->SR IDLE 数据总线空闲
中断标志位进行清除操作。
【官方要求】
1. 读取 USART1->SR 寄存器
2. 读取 USART1->DR 寄存器
*/
val = USART1->SR;
val = USART1->DR;
// 将数据回显到 PC 端 USART 调试工具
USART1_SendBuffer(usart1_val.data, usart1_val.count);
}
}
usart.h:
cpp
#ifndef _USART1_H
#define _USART1_H
#include "stm32f10x.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define DATA_SIZE (256)
typedef struct usart1_data
{
u8 data[DATA_SIZE]; // 接受数据缓冲区
u8 flag; // 数据处理标志位
u16 count; // 读取到的有效字节个数
} USART1_Data;
extern USART1_Data usart1_val;
/**
* @brief USART1 初始化函数,需要完成
* 1. PA9 和 PA10 GPIO 配置
* 2. USART1 配置
*
* @param brr 用户提供的对应当前 USART1 的波特率
*/
void USART1_Init(u32 brr);
/**
* @brief USART1 发送一个字节数据到其他设备
*
* @param byte 发送的字节数据。
*/
void USART1_SendByte(u8 byte);
void USART1_SendBuffer(u8 *buffer, u16 count);
void USART1_SendString(const char * str);
/**
* @brief USART1 接受外部输入的数据内容,当前函数是接收一个字节数据
*
* @return 返回值是接收到的数据内容
*/
u8 USART1_ReceiveByte(void);
/*
后续代码中需要利用【中断】对代码内容进行优化
1. 接收数据终止条件
2. 提供外部可以持续使用数据内容
3. 数据发送中断判断
*/
/**
* @brief USART1 串口中断使能函数
*/
void USART1_Interrupt_Enable(void);
#endif
main.c:
cpp
#include "stm32f10x.h"
//#include "led.h"
//#include "key.h"
#include "delay.h"
//#include "beep.h"
#include "usart1.h"
#include "adc.h"
int main(void)
{
//Led_Init();
LSEN_Init();
USART1_Init(115200);
USART1_Interrupt_Enable();
//Led1_Ctrl(1);
while (1)
{
u16 adc_data = LSEN_GetValue();
printf("adc_data : %d\r\n", adc_data);
printf("U_LSEN : %f\r\n", 3.3 / 4096 * adc_data);
Delay_ms(500);
}
}