在U-Boot中为RK3588添加SARADC检测:从零到一的硬件识别方案 电子说
在嵌入式开发中,我们经常需要在系统启动的早期阶段(U-Boot)识别硬件版本或配置。本文将详细介绍如何在 U-Boot 中为 RK3588 平台添加 SARADC(Successive Approximation Register Analog-to-Digital Converter)检测功能,通过读取 ADC 电压值来区分不同的硬件版本,并将结果存入环境变量,为后续的启动流程提供依据。

为什么需要在 U-Boot 中做 ADC 检测?
U-Boot 作为引导加载程序,运行在操作系统内核之前,拥有极高的硬件访问权限。在这个阶段进行 ADC 检测,可以实现:
•硬件版本自动识别:通过读取主板上由电阻分压网络决定的 ADC 引脚电压,U-Boot 可以判断出当前硬件的具体型号或版本。
•动态配置加载:根据识别出的硬件版本,U-Boot 可以加载不同的设备树(Device Tree)、启动参数或执行特定的初始化脚本,实现 “一镜像适配多硬件” 的目标。
•故障诊断:检测关键电源电压或传感器状态,在启动早期发现硬件异常。
要在 U-Boot 中使用 SARADC,首先需要确保其设备树节点已正确配置并启用。
文件路径: u-boot/arch/arm/dts/rk3588-u-boot.dtsi
/* 在 rk3588-u-boot.dtsi 文件中找到并确认 saradc 节点 */&saradc {/** u-boot, dm-pre-reloc; 是一个关键属性,* 它告诉 U-Boot 的驱动模型(Driver Model)* 在 relocation(重定位)之前就初始化这个设备。* 因为我们要在 board_init_f 阶段(重定位前)读取ADC,* 所以必须添加此属性。*/u-boot, dm-pre-reloc;status = "okay";};
代码解读:
•&saradc: 引用主设备树中定义的saradc节点。
•u-boot, dm-pre-reloc;: 核心配置。确保 SARADC 驱动在 U-Boot 早期阶段(内存重定位前)就被初始化,这样我们才能在board_init_f或board_init_r中调用其功能。
•status = "okay";: 启用这个设备节点。
接下来,我们在板级初始化文件中添加实际的检测代码。
文件路径: u-boot/arch/arm/mach-rockchip/board.c
/* 在 board.c 文件的合适位置(如 board_init_r 函数之前)添加此函数 */void sbc_id_detection(void){/* 1. 定义变量 */int closest_level = 7; // 默认的最高等级unsigned int adc_value; // 存储原始ADC读数 (0-4095 for 12-bit)unsigned int voltage_mv; // 转换后的电压值 (mV)/* 2. 定义电压区间与硬件版本的对应关系 *//* 这是一个典型的电阻分压网络设计,不同的电阻组合产生不同的电压。你需要根据自己的硬件原理图来修改这两个数组中的值。 */const int min_mv[] = {1640, 1430, 1220, 1000, 790, 560, 350, 0};const int max_mv[] = {1840, 1630, 1420, 1200, 990, 770, 550, 250};/* 3. 读取ADC通道3的值 *//* "saradc" 是设备树中的节点名,3 是要读取的通道号 */if (adc_channel_single_shot("saradc", 3, &adc_value) == 0) {/* 4. 将ADC原始值转换为电压值 (mV) *//*计算公式:Vout = Vin * (ADC_Value / 2^n)- Vin (参考电压) = 1800 mV (根据瑞芯微文档)- n (ADC位数) = 12 bits, 所以 2^12 = 4096*/voltage_mv = (adc_value * 1800) / 4095;/* 5. 根据电压值查找匹配的硬件等级 */for (int i = 0; i < 8; i++) {if (voltage_mv >= min_mv[i] && voltage_mv <= max_mv[i]) {closest_level = i;break; // 找到后立即跳出循环}}/* 6. 将检测结果存入U-Boot环境变量 *//* 环境变量 SBCID 可以在后续的启动脚本中被读取和使用 */env_set_ulong("SBCID", closest_level);/* 调试信息,可以在串口终端看到 */printf("SARADC: Voltage detected: %d mV, SBCID set to %dn", voltage_mv, closest_level);} else {/* 7. 处理ADC读取失败的情况 */env_set("SBCID", "unknown");printf("SARADC: Failed to read from channel 3. SBCID set to 'unknown'n");}}/** 将检测函数注册到U-Boot的初始化序列中。* board_init_r 是一个合适的位置,它在设备初始化完成后、* 环境变量初始化后、启动倒计时开始前执行。*/// 注意:具体的注册方式可能因U-Boot版本而异,// 请查阅你所用U-Boot版本的板级文件,找到合适的位置调用 sbc_id_detection();// 例如,在 board_r.c 的某个初始化函数中添加:// sbc_id_detection();
代码解读:
1.adc_channel_single_shot(): U-Boot 提供的便捷 API,用于单次读取指定 ADC 设备和通道的值。
2.电压换算: 根据瑞芯微手册,SARADC 的参考电压Vref为 1.8V(1800mV)。代码将 12 位的 ADC 读数(范围 0-4095)线性换算为实际电压。
3.等级匹配: 通过for循环将计算出的电压值与预设的min_mv和max_mv数组进行比较,找到对应的硬件等级closest_level。
4.环境变量: env_set_ulong()将检测到的等级存入名为SBCID的环境变量。这个变量非常关键,后续的启动脚本(如boot.cmd)就可以通过判断SBCID的值来执行不同的操作。
检测结果SBCID已存入环境变量,现在你可以在 U-Boot 的启动脚本中灵活运用它。
文件路径: u-boot/board/rockchip/your_board/your_board.env (或类似的脚本文件)
# 在 U-Boot 启动脚本中,可以这样使用 SBCIDif test ${SBCID} -eq 0; thenecho "Detected Board Version A. Loading corresponding Device Tree..."setenv fdtfile rockchip/rk3588-board-version-a.dtbelif test ${SBCID} -eq 1; thenecho "Detected Board Version B. Loading corresponding Device Tree..."setenv fdtfile rockchip/rk3588-board-version-b.dtbelseecho "Detected unknown board or using default configuration."setenv fdtfile rockchip/rk3588-board-default.dtbfi# 后续的 bootcmd 会使用 $fdtfile 变量来加载正确的设备树# run bootcmd
bdinfo
1.时序问题: 确保 ADC 检测函数在所有必要驱动初始化之后调用。board_init_r是一个安全的选择。
2.硬件依赖: 此方案完全依赖于硬件设计。分压电阻的精度、焊接质量、以及 ADC 引脚的电气连接都会影响检测结果。
3.区间设计: 在设计min_mv和max_mv数组时,要考虑到电阻容差和 ADC 采样误差,为每个区间留出足够的余量,避免电压值落在区间边界导致误判。
4.调试: 在开发阶段,可以将printf语句打开,通过串口终端观察 ADC 的原始值、换算后的电压值以及最终的SBCID,这对于调试硬件和校准区间非常有帮助。
•代码集成: 将上述代码片段集成到您的 U-Boot 源码树中,并根据您的具体硬件原理图,精确调整min_mv和max_mv数组的值。
•脚本优化: 完善您的 U-Boot 启动脚本,利用SBCID环境变量实现更复杂的启动逻辑,例如加载不同的内核参数或根文件系统。
希望这篇文章能帮助您顺利完成 U-Boot 中的 ADC 检测功能开发。
全部0条评论
快来发表一下你的评论吧 !