1. 引言
本文围绕嵌入式开发中 I2C 驱动的两个典型优化点展开:一是兼容多字节寄存器地址的读写逻辑,二是基于 LM75 温度传感器的数据读取实现。同时补充了 ARM 平台下 FPU 使能的汇编配置,帮助读者在底层驱动开发中快速定位并解决问题。
2. 代码优化:兼容多字节寄存器地址
在 I2C 通信中,部分传感器或外设的寄存器地址不止一个字节。若驱动只按单字节地址处理,会导致高位地址丢失,从而无法正确访问目标寄存器。下面这段代码通过循环逐字节发送寄存器地址,实现多字节地址兼容。
c
// 3. 发送要读取的内存地址
int i = reg_len - 1;
for (; i >= 0; i--)
{
base->I2DR = (reg_addr >> (8 * i)) & 0XFF;
status = i2c_wait_iif(base);
if (status != 0) goto stop;
}
上述实现从最高字节开始,依次将寄存器地址的每个字节写入 I2C 数据寄存器,并在每次写入后等待传输完成。这样无论寄存器地址是 8 位、16 位还是 32 位,都能按序完整发送。
3. LM75 温度传感器读取
LM75 是一款常见的 I2C 接口数字温度传感器,温度数据存放在寄存器 0x00 中,共 2 个字节。下面给出基于 I2C 消息结构体的读取实现。
c
float get_temp_value(void)
{
unsigned short t = 0;
struct I2C_Msg msg = {
.dev_addr = 0x48,
.reg_addr = 0x00,
.reg_len = 1,
.data = rcv_buffer,
.len = 2,
.dir = I2C_read
};
i2c_transfer(I2C1, &msg);
//i2c_read(I2C1, 0x48, 0x00, 1, rcv_buffer, 2);
t |= (rcv_buffer[0] << 8) | (rcv_buffer[1] << 0);
t = t >> 7;
return t * 0.5;
}
读取流程说明:
- 设备地址为 0x48,寄存器地址为 0x00,读取长度为 2 字节。
- 通过 i2c_transfer 完成一次完整的读事务。
- 将两个字节拼成 16 位数据后右移 7 位,得到 9 位有效温度值。
- 每个 LSB 对应 0.5 摄氏度,因此乘以 0.5 得到实际温度。
4. ARM 平台 FPU 使能配置
在 ARM Cortex-A 系列处理器上,若需要使用浮点运算单元(FPU),必须先完成寄存器配置。下面是一段典型的 FPU 使能汇编代码。
arm
enable_fpu:
// 1. 设置CPACR寄存器使能FPU访问
mrc p15, 0, r0, c1, c0, 2 // 读取CPACR
orr r0, r0, #(0xF << 20) // 设置CP10和CP11为完全访问
mcr p15, 0, r0, c1, c0, 2 // 写回CPACR
// 2. 使能FPU
mov r0, #0x40000000 // 设置FPEXC的EN位
vmsr fpexc, r0 // 写入FPEXC
// 3. 配置FPSCR
mov r0, #0x00000000 // 清除所有标志位
vmsr fpscr, r0 // 写入FPSCR
bx lr // 返回
配置步骤说明:
- 第一步通过 CPACR 寄存器开放 CP10 和 CP11 协处理器的完全访问权限。
- 第二步设置 FPEXC 的 EN 位,正式启用 FPU。
- 第三步清零 FPSCR 标志位,确保浮点运算从干净状态开始。
5. ADC 模拟量采集
ADC(Analog-to-Digital Converter,模拟到数字转换器)是一种电子设备或模块,用于将连续变化的模拟信号转换为离散的数字信号,以便数字系统(如微处理器、微控制器等)能够对其进行处理和分析。这里的模拟信号是狭义的模拟信号,一般指模拟电压信号转换为数字信号。
相关概念说明:
- 模拟信号:物理世界中连续变换的物理量。
- 数字信号:一种离散的、不连续的信号。
- 传感器:能够探测、感知外部环境信息(如温度、光线、压力、运动等),并将这些信息转换成电信号(通常是电压或电流)的装置或器件。
6. 原理图与参考手册分析
IMX6ULL 的 ADC 模块集成于 SoC 内部,参考电压引脚为 ADC_VREFH。在 Mini 底板原理图中,GPIO_1 的 7 号引脚用于 ADC 输入。参考手册中 ADC1 的输入通道与 GPIO 引脚的对应关系如下:
| Signal | Description | Pad | Mode | Direction |
|---|---|---|---|---|
| ADC_VREFH | Voltage reference high | ADC_VREFH | - | I |
| ADC1_IN0 | Analog channel 1 input 0 | GPIO1_IO00 | - | I |
| ADC1_IN1 | Analog channel 1 input 1 | GPIO1_IO01 | - | I |
| ADC1_IN2 | Analog channel 1 input 2 | GPIO1_IO02 | - | I |
| ADC1_IN3 | Analog channel 1 input 3 | GPIO1_IO03 | - | I |
| ADC1_IN4 | Analog channel 1 input 4 | GPIO1_IO04 | - | I |
| ADC1_IN5 | Analog channel 1 input 5 | GPIO1_IO05 | - | I |
| ADC1_IN6 | Analog channel 1 input 6 | GPIO1_IO06 | - | I |
| ADC1_IN7 | Analog channel 1 input 7 | GPIO1_IO07 | - | I |
| ADC1_IN8 | Analog channel 1 input 8 | GPIO1_IO08 | - | I |
| ADC1_IN9 | Analog channel 1 input 9 | GPIO1_IO09 | - | I |
时钟源选择(ADICLK)说明:
- 00:IPG clock
- 01:IPG clock divided by 2
- 10:Reserved
- 11:Asynchronous clock(ADACK)
ADC 模块还支持自动校准功能,具体原理可阅读参考手册对应章节。
7. ADC 寄存器配置
IMX6ULL 的 ADC 相关寄存器配置要点如下:
| 寄存器 | 位域 | 说明 | 默认值 |
|---|---|---|---|
| ADCx_HC0 | AIEN b7 | 转换完成中断启用/禁用控制 | 0 |
| ADCx_HC0 | ADCH b0-b4 | 选择 ADC 通道,每次切换通道时会引发一次 ADC 转换 | 1 |
| ADCx_HS | COCO0 b0 | 判断 ADC 转换是否完成,读 1 表示转换完成,为 0 则未完成 | - |
| ADCx_R0 | CDATA b0-b11 | ADC 转换完的最终结果,读低十二位 | - |
| ADCx_CFG | OVWREN b16 | 数据覆盖使能 | 0 |
| ADCx_CFG | AVGS b15-b14 | 硬件平均选择 | 00 |
| ADCx_CFG | ADTRG b13 | 转换触发选择 | 0 |
| ADCx_CFG | REFSEL b12-b11 | 电压参考源选择 | 00 |
| ADCx_CFG | ADHSC b10 | 高速配置 | 0 |
| ADCx_CFG | ADSTS b9-b8 | 采样时间选择 | 00 |
| ADCx_CFG | ADLPC b7 | 低功耗配置 | 0 |
| ADCx_CFG | ADIV b6-b5 | 时钟分频选择 | 00 |
| ADCx_CFG | ADLSMP b4 | 长采样时间配置 | 0 |
| ADCx_CFG | MODE b3-b2 | 转换模式选择(分辨率) | 10 |
| ADCx_CFG | ADICLK b1-b0 | 输入时钟源选择 | 11 |
| ADCx_GC | CAL b7 | 校准启动,写 1 启动,读 0 校准完成 | - |
| ADCx_GC | ADCO b6 | 连续转换使能 | 0 |
| ADCx_GC | AVGE b5 | 硬件平均使能 | 0 |
| ADCx_GC | ACFE b4 | 比较功能使能 | 0 |
| ADCx_GC | ACFGT b3 | 比较功能"大于"使能 | 0 |
| ADCx_GC | ACREN b2 | 比较功能范围使能 | 0 |
| ADCx_GC | DMAEN b1 | DMA 使能 | 0 |
| ADCx_GC | ADACKEN b0 | 异步时钟输出使能 | 1 |
| ADCx_GS | CALF b1 | 校准失败标志,0 校准成功,1 校准失败,需要写 1 清零 | - |
8. 光敏传感器与代码编写
光敏传感器的原理图可参考 SmartDraw 图。代码编写主要包含三个步骤:ADC 校准、采样和均值滤波。
在编写代码前,需要理解以下几个核心问题:
- 什么是 ADC?
- 什么是 ADC 的基准电压?
- ADC 的工作原理是什么?
- 什么是 ADC 的分辨率?常见的分辨率有哪些?
- 假设采用 12 位分辨率,基准电压为 3.3V,量化结果为 n 时的实际电压应该如何计算?
对于最后一个问题,12 位分辨率下量化结果为 n 时,实际电压的计算公式为:实际电压 = n × 3.3V / 4096。
5. 总结
本文介绍了 I2C 驱动中多字节寄存器地址的兼容写法、LM75 温度传感器的读取流程,以及 ARM 平台 FPU 的使能方法。这些代码片段可直接用于实际驱动开发,也可作为底层调试的参考。建议读者结合具体芯片手册,进一步验证寄存器地址长度和时序配置。