1.ADC(采样和转换时间、常规单通道转换、定时器触发、串口示波器)
1.ADC
- 单片机里的ADC
2.ADC采样深度
3.ADC类型
4.ADC的内部结构
- 输入一个脉冲,计划执行一遍
- 每输入一个脉冲,ADC就转换一次
5.ADC采样时间和转换时间
5.1 ADC时钟,采样和转换时间
5.2 转换时间的计算
5.3 采样时间的计算
- 理想状态下ADC每s最多执行多少次转换?
6. ADC常规单通道转换实验
- 光敏传感器模块
- 计算信号源光敏传感器的内阻?
- 戴维南等效定律
6.1 启动常规序列
6.2 等待转换结束
- 是阻塞式等待 ADC 转换完成标志(EOC),它本身只是一个「状态查询」函数,既不是注入式、也不是常规式,只是一个通用的等待接口。
- 它到底对应哪种模式,取决于你之前配置和启动的 ADC 通道类型。
6.3获取转换结果
7. 定时器触发常规序列实验
- 实现间隔为1ms的采样。
intmain(void){//开启定时器3HAL_TIM_Base_Start(&htim3);HAL_ADC_Start(&hadc1);while(1){// #1. 等待转换结束HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);// #2. 读取转换结果uint32_tdr=HAL_ADC_GetValue(&hadc1);// #3. 将结果转换成电压floatvoltage=dr*(3.3f-0.0f)/4095.0f;// #4. 通过串口发送给电脑HAL_UART_Transmit(&huart1,(uint8_t*)buffer,strlen((buffer)),HAL_MAX_DELAY);}}6.4 串口示波器
我来帮你把这个问题拆开讲清楚,你就能明白「定时器外部触发」和「while循环里读数据」的关系了。
一、先理解你图里的时序
- 外部触发信号:每1ms来一次脉冲,触发ADC开始转换。
- ADC转换时间:需要
(7.5+12.5)个时钟周期,假设ADC时钟是4MHz,总耗时是20/4MHz = 5us,和图里标注的5us完全对应。 - 读取结果:转换完成后,你需要把结果读出来,这一步是在转换结束之后才做的。
二、为什么很多教程会用while循环?
你截图里的代码,是**「软件轮询模式」**的ADC,它的逻辑是这样的:
HAL_ADC_Start(&hadc1);// 启动转换(或者由外部触发启动)while(1){// 1. 死等转换完成(轮询标志位)HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);// 2. 读取结果uint32_tdr=HAL_ADC_GetValue(&hadc1);}这里的while循环,本质上是为了重复执行“等待转换完成+读取结果”这一整套动作,实现连续采样。
三、问题的核心:「定时器触发」和「while循环」的矛盾点
你现在的困惑是:既然已经用定时器每1ms触发一次ADC转换了,为什么还要用while循环去读?
这就要分两种情况来看了:
情况1:你用的是「定时器触发ADC + 软件轮询读取」(也就是你截图里的模式)
- 定时器的作用:每1ms自动给ADC一个启动信号,让ADC开始转换。
- while循环里的
HAL_ADC_PollForConversion作用:检查这次转换是不是已经完成了。因为ADC转换需要几微秒,触发信号来了,转换并不会立刻结束,你需要等它完成才能读结果。 - 为什么要循环?因为下一次1ms触发信号来了,又会启动一次新的转换,你需要再等它完成、再读结果。循环就是为了处理一次又一次的转换结果。
但这种模式效率很低:HAL_ADC_PollForConversion会一直占用CPU,直到转换完成,CPU在这期间什么都干不了。
情况2:你用的是「定时器触发ADC + DMA/中断」(更推荐的方式)
这种模式下,你完全不需要在while循环里轮询:
- 定时器触发ADC开始转换。
- ADC转换完成后,自动通过DMA把数据搬运到内存,或者触发一个中断。
- 你在中断服务函数里读取结果,或者直接处理DMA里的数据。
- 主循环里CPU可以做其他事情,不会被ADC阻塞。
四、你截图里的代码,有一个“时序错位”的问题
你图里的外部触发是每1ms一次,转换时间只有5us,这没问题。
但你代码里用了HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);,这意味着:
- 如果ADC转换没完成,CPU会一直卡在这行代码,直到超时或者转换完成。
- 对于5us的转换来说,这行代码虽然耗时很短,但它会把CPU锁死在这里,无法做其他任何事。
- 这种方式和“定时器外部触发”的优势是矛盾的——定时器触发本来就是为了让ADC和CPU并行工作,结果你用轮询把CPU又绑死了。
五、给你一个更合理的「定时器触发+非阻塞读取」方案
如果你已经配置了定时器触发ADC,推荐用中断模式,就不用while循环里轮询了:
// 1. 在初始化里配置:定时器触发ADC、开启转换完成中断// MX_ADC1_Init();// MX_TIM2_Init();// HAL_ADC_Start_IT(&hadc1); // 启动ADC并开启中断// 2. 中断回调函数:转换完成后自动调用voidHAL_ADC_ConvCpltCallback(ADC_HandleTypeDef*hadc){if(hadc->Instance==ADC1){// 这里读取结果,不会阻塞主循环uint32_tdr=HAL_ADC_GetValue(&hadc1);floatvoltage=dr*3.3f/4095.0f;// 处理数据,比如发串口}}// 主循环里可以做其他事情intmain(void){while(1){// 这里放你的其他业务逻辑,不会被ADC阻塞}}💡 总结一下:
- 「while循环里读数据」是软件轮询模式的产物,目的是连续等待和读取转换结果。
- 如果你用了「定时器外部触发」,搭配中断或DMA模式才是更高效的用法,不需要while循环里轮询。
- 你截图里的代码,是“定时器触发+软件轮询”的低效组合,没有发挥出定时器触发的优势。
要不要我帮你把当前的「定时器触发+轮询」代码,改成更高效的「定时器触发+ADC中断」版本?
