20. 基于CW32F030C8T6与光敏电阻的ADC单通道采集实战最近有朋友在玩地文星的CW32F030C8T6开发板问我怎么用上面的ADC功能来测量光敏电阻的电压变化实现一个简单的环境光检测。这确实是嵌入式入门的一个经典实验能让你把ADC配置、数据采集、传感器应用串起来理解。今天我就手把手带你走一遍从硬件接线到代码编写最后在串口上看到光照变化的数据。咱们这个实验的目标很明确用开发板上的ADC去读取光敏电阻模块输出的模拟电压光线越强电压越高ADC读到的数字值就越大。通过串口打印出来你就能直观地看到环境光的变化了。1. 硬件准备与接线工欲善其事必先利其器。我们先来看看需要哪些硬件以及怎么把它们连起来。1.1 认识光敏电阻模块我用的这个模块很常见淘宝上几块钱一个。它本质上是一个电阻值会随光照强度变化的光敏电阻配上简单的分压电路做成一个模块方便我们使用。模块有四个引脚VCC 接电源正极工作电压范围是3.3V到5V咱们用开发板的3.3V供电正合适。GND 接地接开发板的GND。AO模拟量输出。这是咱们今天的主角它会输出一个连续的电压信号电压值随光照变化。这个引脚需要接到MCU的ADC输入引脚上。DO 数字量输出。模块内部有个比较器当光照超过或低于某个设定的阈值时这个引脚会输出高或低电平。今天我们用ADC所以这个引脚暂时不用。模块的尺寸是31.1mm x 14.1mm很小巧用杜邦线连接非常方便。1.2 连接开发板接线是第一步千万别接错了。CW32F030C8T6的ADC通道是固定在某些引脚上的我们需要查一下数据手册。根据资料我们把光敏电阻模块的AO输出接到了PA5引脚因为PA5对应着ADC的通道5。具体的接线对照表如下光敏电阻模块引脚地文星开发板引脚说明AOPA5模拟信号输出接ADC输入通道DOPA2 (或其他GPIO)数字信号输出本实验未使用可悬空GNDGND电源地VCC3V33.3V电源注意接线时务必确保VCC和GND没有接反否则可能损坏模块或开发板。2. 软件配置让ADC工作起来硬件接好了接下来就是重头戏配置CW32F030的ADC外设。这个过程就像给一个复杂的仪器设定工作模式咱们一步步来。2.1 第一步配置时钟任何外设要工作首先得给它“上电”也就是开启时钟。CW32F030的ADC挂载在PCLK外设时钟总线上这个总线频率默认是64MHz。但是ADC这个“仪器”有个最高工作频率的限制。根据芯片手册当使用VDDA约3.3V作为参考电压时ADC时钟最高不能超过24MHz。所以我们需要对64MHz的PCLK进行分频。分频因子可以选2、4、6、8等。这里我们选择4分频这样ADC的实际工作时钟就是 64MHz / 4 16MHz完全满足小于24MHz的要求。2.2 第二步配置GPIO引脚光敏电阻的AO线接到了PA5引脚上我们需要把这个引脚设置为“模拟输入”模式。为什么因为ADC要测量的是引脚上的模拟电压值如果引脚被配置成数字输入或输出模式内部的电路会干扰模拟信号的读取。配置GPIO分三步开启GPIOA端口的时钟。开启ADC外设的时钟上一步其实已经隐含了这里再明确一下。将PA5引脚的功能设置为模拟输入。在CW32的库函数里这几步可以用宏定义非常简洁地完成/* GPIO配置 */ __RCC_GPIOA_CLK_ENABLE(); // 开启GPIOA端口时钟 __RCC_ADC_CLK_ENABLE(); // 开启ADC外设时钟 PA05_ANALOG_ENABLE(); // 将PA05设置为模拟输入功能2.3 第三步详细配置ADC参数这是核心部分我们需要初始化几个结构体来告诉ADC具体怎么工作。主要涉及三个结构体ADC_InitTypeDefADC基础配置、ADC_WdtTypeDef看门狗本次不用、ADC_SingleChTypeDef单通道配置。咱们来详细看看每个参数的意义/* ADC配置 */ ADC_InitTypeDef ADC_InitStructure; // ADC初始化结构体 ADC_WdtTypeDef ADC_WdtStructure; // ADC看门狗结构体 ADC_SingleChTypeDef ADC_SingleChStructure; // ADC单通道转换结构体 // 1. 配置ADC基础工作模式 ADC_InitStructure.ADC_OpMode ADC_SingleChOneMode; // 工作模式单通道单次转换 ADC_InitStructure.ADC_ClkDiv ADC_Clk_Div4; // 时钟分频4分频 (64MHz/416MHz) ADC_InitStructure.ADC_SampleTime ADC_SampTime5Clk; // 采样时间5个ADC时钟周期 ADC_InitStructure.ADC_VrefSel ADC_Vref_VDDA; // 参考电压选择VDDA约3.3V ADC_InitStructure.ADC_InBufEn ADC_BufDisable; // 输入缓冲器关闭 ADC_InitStructure.ADC_TsEn ADC_TsDisable; // 温度传感器关闭我们不用测温度 ADC_InitStructure.ADC_DMAEn ADC_DmaDisable; // DMA功能关闭简单读取不用DMA ADC_InitStructure.ADC_Align ADC_AlignRight; // 数据对齐方式右对齐 ADC_InitStructure.ADC_AccEn ADC_AccDisable; // 累加功能关闭不进行多次采样累加 // 2. 初始化看门狗本次实验未使用保持默认即可 ADC_WdtInit(ADC_WdtStructure); // 3. 配置单通道转换的具体参数 ADC_SingleChStructure.ADC_DiscardEn ADC_DiscardNull; // 溢出处理不丢弃数据 ADC_SingleChStructure.ADC_Chmux ADC_ExInputCH5; // 选择通道外部输入通道5 (即PA5) ADC_SingleChStructure.ADC_InitStruct ADC_InitStructure; // 关联基础配置 ADC_SingleChStructure.ADC_WdtStruct ADC_WdtStructure; // 关联看门狗配置 // 4. 调用函数将以上配置生效 ADC_SingleChOneModeCfg(ADC_SingleChStructure);关键参数解读单通道单次模式 (ADC_SingleChOneMode) 这是最简单的模式。每次我们发一个“开始转换”命令ADC就对我们指定的那个通道PA5采样一次转换完就停下等待下一次命令。采样时间 (ADC_SampTime5Clk) ADC采样需要时间这个时间越长采样越稳定精度可能越高但转换速度会变慢。对于光敏电阻这种变化不快的信号5个时钟周期足够了。参考电压 (ADC_Vref_VDDA) 这是ADC测量的“尺子”的满刻度值。我们选VDDA约3.3V意味着当PA5引脚电压为3.3V时ADC会输出最大值12位ADC就是4095。电压为0V时输出0。右对齐 (ADC_AlignRight) 12位的ADC转换结果0-4095在32位的寄存器里存放时可以靠左放左对齐或靠右放右对齐。右对齐是常规操作读取后的值就是0-4095非常直观。2.4 第四步使能ADC与校准配置好参数后ADC还没开始工作需要打开它的总开关并进行一次自校准。校准能消除ADC内部电路的一些微小误差让测量更准确。ADC_Enable(); // 使能ADC模块 ADC_StartCalibration(); // 开始ADC自校准 while (ADC_GetCalibrationStatus() ! RESET); // 等待校准完成提示校准只需要在ADC初始化完成后进行一次之后除非重新初始化否则不需要重复校准。3. 编写数据采集函数硬件和驱动都准备好了现在我们来写函数读取ADC的值。3.1 单次读取函数这个函数的功能很简单启动一次转换然后读取结果。/** * brief 读取一次ADC转换值 * param 无 * retval 12位ADC转换结果 (0-4095) */ uint32_t ADC_GET(void) { ADC_SoftwareStartConvCmd(ENABLE); // 软件触发启动一次ADC转换 // 可以在此处添加一个短暂等待确保转换完成但库函数通常已处理 uint32_t adcValue ADC_GetConversionValue(); // 从数据寄存器中读取转换结果 return adcValue; }代码逻辑很清晰发命令开始转换 - 读取结果 - 返回。ADC_GetConversionValue()这个库函数会帮我们等待转换完成并取出数据。3.2 平均值滤波函数在实际应用中单次采样容易受到偶然干扰比如电源纹波、电磁噪声导致数据跳动。一个常见的做法是连续采样多次然后取平均值这样得到的数值更稳定。/** * brief 获取指定次数的ADC采样平均值 * param times: 采样次数 * retval 多次采样的平均值 */ uint32_t Get_Adc_Average(uint8_t times) { uint32_t value 0; uint8_t t; uint8_t valid_times times; // 记录有效采样次数 for(t0; ttimes; t) { uint32_t temp ADC_GET(); // 读取一次ADC值 if(temp 0) // 简单的错误处理如果读到0认为可能读取失败 { valid_times--; // 减少一次有效计数 continue; // 跳过这次无效数据 } value temp; // 累加有效数据 delay_ms(5); // 每次采样间隔5ms避免过于密集 } // 防止除零错误并计算平均值 if(valid_times 0) { return 0; } return value / valid_times; }这个函数里我加了一个简单的“去零”判断如果某次读到0在光敏电阻实验中除非完全遮光并接地否则不应为0就认为可能是读取异常将其剔除。delay_ms(5)让每次采样有点间隔给ADC和外电路一个稳定时间。你可以根据实际需要调整采样次数times次数越多越平滑但响应会变慢。4. 实验现象与主函数最后我们把所有功能在main函数里组织起来。4.1 主函数代码int32_t main(void) { board_init(); // 开发板基础初始化系统时钟、滴答定时器等 uart1_init(115200); // 初始化串口1波特率115200用于打印数据 ADC_Config(); // 调用我们前面写的ADC配置函数 printf(CW32F030 ADC Light Sensor Test Start!\r\n); while(1) { // 采集10次取平均值并通过串口打印 uint32_t adc_result Get_Adc_Average(10); printf(ADC Value %lu\r\n, adc_result); delay_ms(1000); // 每秒打印一次 } }4.2 观察实验现象将代码编译下载到地文星开发板打开串口助手波特率115200你就能看到不断打印的ADC数值了。在正常室内光照下ADC值大约在4000左右。根据公式电压 (ADC值 / 4095) * 3.3V这对应的电压接近3.3V说明光敏电阻阻值变小AO引脚电压被拉高。用手完全遮住光敏电阻ADC值会下降到1000左右对应电压约0.8V。此时光敏电阻阻值极大分压后AO引脚电压变低。验证测试可选如果你没有光敏电阻可以直接用杜邦线将PA5引脚接到3.3V或GND来测试ADC是否工作正常。接3.3V时读数应接近4095接GND时读数应接近0。看到这些变化的数据就说明你的ADC采集环境光强度的实验成功啦这个框架是基础你可以在此基础上拓展比如设定一个阈值当光线暗到一定程度就让一个LED亮起做一个自动小夜灯。或者把ADC值映射成具体的照度等级显示在屏幕上。嵌入式开发的乐趣就是从这样一个个小实验开始的。
20. 基于CW32F030C8T6与光敏电阻的ADC单通道采集实战
20. 基于CW32F030C8T6与光敏电阻的ADC单通道采集实战最近有朋友在玩地文星的CW32F030C8T6开发板问我怎么用上面的ADC功能来测量光敏电阻的电压变化实现一个简单的环境光检测。这确实是嵌入式入门的一个经典实验能让你把ADC配置、数据采集、传感器应用串起来理解。今天我就手把手带你走一遍从硬件接线到代码编写最后在串口上看到光照变化的数据。咱们这个实验的目标很明确用开发板上的ADC去读取光敏电阻模块输出的模拟电压光线越强电压越高ADC读到的数字值就越大。通过串口打印出来你就能直观地看到环境光的变化了。1. 硬件准备与接线工欲善其事必先利其器。我们先来看看需要哪些硬件以及怎么把它们连起来。1.1 认识光敏电阻模块我用的这个模块很常见淘宝上几块钱一个。它本质上是一个电阻值会随光照强度变化的光敏电阻配上简单的分压电路做成一个模块方便我们使用。模块有四个引脚VCC 接电源正极工作电压范围是3.3V到5V咱们用开发板的3.3V供电正合适。GND 接地接开发板的GND。AO模拟量输出。这是咱们今天的主角它会输出一个连续的电压信号电压值随光照变化。这个引脚需要接到MCU的ADC输入引脚上。DO 数字量输出。模块内部有个比较器当光照超过或低于某个设定的阈值时这个引脚会输出高或低电平。今天我们用ADC所以这个引脚暂时不用。模块的尺寸是31.1mm x 14.1mm很小巧用杜邦线连接非常方便。1.2 连接开发板接线是第一步千万别接错了。CW32F030C8T6的ADC通道是固定在某些引脚上的我们需要查一下数据手册。根据资料我们把光敏电阻模块的AO输出接到了PA5引脚因为PA5对应着ADC的通道5。具体的接线对照表如下光敏电阻模块引脚地文星开发板引脚说明AOPA5模拟信号输出接ADC输入通道DOPA2 (或其他GPIO)数字信号输出本实验未使用可悬空GNDGND电源地VCC3V33.3V电源注意接线时务必确保VCC和GND没有接反否则可能损坏模块或开发板。2. 软件配置让ADC工作起来硬件接好了接下来就是重头戏配置CW32F030的ADC外设。这个过程就像给一个复杂的仪器设定工作模式咱们一步步来。2.1 第一步配置时钟任何外设要工作首先得给它“上电”也就是开启时钟。CW32F030的ADC挂载在PCLK外设时钟总线上这个总线频率默认是64MHz。但是ADC这个“仪器”有个最高工作频率的限制。根据芯片手册当使用VDDA约3.3V作为参考电压时ADC时钟最高不能超过24MHz。所以我们需要对64MHz的PCLK进行分频。分频因子可以选2、4、6、8等。这里我们选择4分频这样ADC的实际工作时钟就是 64MHz / 4 16MHz完全满足小于24MHz的要求。2.2 第二步配置GPIO引脚光敏电阻的AO线接到了PA5引脚上我们需要把这个引脚设置为“模拟输入”模式。为什么因为ADC要测量的是引脚上的模拟电压值如果引脚被配置成数字输入或输出模式内部的电路会干扰模拟信号的读取。配置GPIO分三步开启GPIOA端口的时钟。开启ADC外设的时钟上一步其实已经隐含了这里再明确一下。将PA5引脚的功能设置为模拟输入。在CW32的库函数里这几步可以用宏定义非常简洁地完成/* GPIO配置 */ __RCC_GPIOA_CLK_ENABLE(); // 开启GPIOA端口时钟 __RCC_ADC_CLK_ENABLE(); // 开启ADC外设时钟 PA05_ANALOG_ENABLE(); // 将PA05设置为模拟输入功能2.3 第三步详细配置ADC参数这是核心部分我们需要初始化几个结构体来告诉ADC具体怎么工作。主要涉及三个结构体ADC_InitTypeDefADC基础配置、ADC_WdtTypeDef看门狗本次不用、ADC_SingleChTypeDef单通道配置。咱们来详细看看每个参数的意义/* ADC配置 */ ADC_InitTypeDef ADC_InitStructure; // ADC初始化结构体 ADC_WdtTypeDef ADC_WdtStructure; // ADC看门狗结构体 ADC_SingleChTypeDef ADC_SingleChStructure; // ADC单通道转换结构体 // 1. 配置ADC基础工作模式 ADC_InitStructure.ADC_OpMode ADC_SingleChOneMode; // 工作模式单通道单次转换 ADC_InitStructure.ADC_ClkDiv ADC_Clk_Div4; // 时钟分频4分频 (64MHz/416MHz) ADC_InitStructure.ADC_SampleTime ADC_SampTime5Clk; // 采样时间5个ADC时钟周期 ADC_InitStructure.ADC_VrefSel ADC_Vref_VDDA; // 参考电压选择VDDA约3.3V ADC_InitStructure.ADC_InBufEn ADC_BufDisable; // 输入缓冲器关闭 ADC_InitStructure.ADC_TsEn ADC_TsDisable; // 温度传感器关闭我们不用测温度 ADC_InitStructure.ADC_DMAEn ADC_DmaDisable; // DMA功能关闭简单读取不用DMA ADC_InitStructure.ADC_Align ADC_AlignRight; // 数据对齐方式右对齐 ADC_InitStructure.ADC_AccEn ADC_AccDisable; // 累加功能关闭不进行多次采样累加 // 2. 初始化看门狗本次实验未使用保持默认即可 ADC_WdtInit(ADC_WdtStructure); // 3. 配置单通道转换的具体参数 ADC_SingleChStructure.ADC_DiscardEn ADC_DiscardNull; // 溢出处理不丢弃数据 ADC_SingleChStructure.ADC_Chmux ADC_ExInputCH5; // 选择通道外部输入通道5 (即PA5) ADC_SingleChStructure.ADC_InitStruct ADC_InitStructure; // 关联基础配置 ADC_SingleChStructure.ADC_WdtStruct ADC_WdtStructure; // 关联看门狗配置 // 4. 调用函数将以上配置生效 ADC_SingleChOneModeCfg(ADC_SingleChStructure);关键参数解读单通道单次模式 (ADC_SingleChOneMode) 这是最简单的模式。每次我们发一个“开始转换”命令ADC就对我们指定的那个通道PA5采样一次转换完就停下等待下一次命令。采样时间 (ADC_SampTime5Clk) ADC采样需要时间这个时间越长采样越稳定精度可能越高但转换速度会变慢。对于光敏电阻这种变化不快的信号5个时钟周期足够了。参考电压 (ADC_Vref_VDDA) 这是ADC测量的“尺子”的满刻度值。我们选VDDA约3.3V意味着当PA5引脚电压为3.3V时ADC会输出最大值12位ADC就是4095。电压为0V时输出0。右对齐 (ADC_AlignRight) 12位的ADC转换结果0-4095在32位的寄存器里存放时可以靠左放左对齐或靠右放右对齐。右对齐是常规操作读取后的值就是0-4095非常直观。2.4 第四步使能ADC与校准配置好参数后ADC还没开始工作需要打开它的总开关并进行一次自校准。校准能消除ADC内部电路的一些微小误差让测量更准确。ADC_Enable(); // 使能ADC模块 ADC_StartCalibration(); // 开始ADC自校准 while (ADC_GetCalibrationStatus() ! RESET); // 等待校准完成提示校准只需要在ADC初始化完成后进行一次之后除非重新初始化否则不需要重复校准。3. 编写数据采集函数硬件和驱动都准备好了现在我们来写函数读取ADC的值。3.1 单次读取函数这个函数的功能很简单启动一次转换然后读取结果。/** * brief 读取一次ADC转换值 * param 无 * retval 12位ADC转换结果 (0-4095) */ uint32_t ADC_GET(void) { ADC_SoftwareStartConvCmd(ENABLE); // 软件触发启动一次ADC转换 // 可以在此处添加一个短暂等待确保转换完成但库函数通常已处理 uint32_t adcValue ADC_GetConversionValue(); // 从数据寄存器中读取转换结果 return adcValue; }代码逻辑很清晰发命令开始转换 - 读取结果 - 返回。ADC_GetConversionValue()这个库函数会帮我们等待转换完成并取出数据。3.2 平均值滤波函数在实际应用中单次采样容易受到偶然干扰比如电源纹波、电磁噪声导致数据跳动。一个常见的做法是连续采样多次然后取平均值这样得到的数值更稳定。/** * brief 获取指定次数的ADC采样平均值 * param times: 采样次数 * retval 多次采样的平均值 */ uint32_t Get_Adc_Average(uint8_t times) { uint32_t value 0; uint8_t t; uint8_t valid_times times; // 记录有效采样次数 for(t0; ttimes; t) { uint32_t temp ADC_GET(); // 读取一次ADC值 if(temp 0) // 简单的错误处理如果读到0认为可能读取失败 { valid_times--; // 减少一次有效计数 continue; // 跳过这次无效数据 } value temp; // 累加有效数据 delay_ms(5); // 每次采样间隔5ms避免过于密集 } // 防止除零错误并计算平均值 if(valid_times 0) { return 0; } return value / valid_times; }这个函数里我加了一个简单的“去零”判断如果某次读到0在光敏电阻实验中除非完全遮光并接地否则不应为0就认为可能是读取异常将其剔除。delay_ms(5)让每次采样有点间隔给ADC和外电路一个稳定时间。你可以根据实际需要调整采样次数times次数越多越平滑但响应会变慢。4. 实验现象与主函数最后我们把所有功能在main函数里组织起来。4.1 主函数代码int32_t main(void) { board_init(); // 开发板基础初始化系统时钟、滴答定时器等 uart1_init(115200); // 初始化串口1波特率115200用于打印数据 ADC_Config(); // 调用我们前面写的ADC配置函数 printf(CW32F030 ADC Light Sensor Test Start!\r\n); while(1) { // 采集10次取平均值并通过串口打印 uint32_t adc_result Get_Adc_Average(10); printf(ADC Value %lu\r\n, adc_result); delay_ms(1000); // 每秒打印一次 } }4.2 观察实验现象将代码编译下载到地文星开发板打开串口助手波特率115200你就能看到不断打印的ADC数值了。在正常室内光照下ADC值大约在4000左右。根据公式电压 (ADC值 / 4095) * 3.3V这对应的电压接近3.3V说明光敏电阻阻值变小AO引脚电压被拉高。用手完全遮住光敏电阻ADC值会下降到1000左右对应电压约0.8V。此时光敏电阻阻值极大分压后AO引脚电压变低。验证测试可选如果你没有光敏电阻可以直接用杜邦线将PA5引脚接到3.3V或GND来测试ADC是否工作正常。接3.3V时读数应接近4095接GND时读数应接近0。看到这些变化的数据就说明你的ADC采集环境光强度的实验成功啦这个框架是基础你可以在此基础上拓展比如设定一个阈值当光线暗到一定程度就让一个LED亮起做一个自动小夜灯。或者把ADC值映射成具体的照度等级显示在屏幕上。嵌入式开发的乐趣就是从这样一个个小实验开始的。