1-wire 总线的特点
1-wire 协议是用一条数据线作为总线进行数据通信的协议.
1-wire 总线有以下特点:
1. 可以组建网络, 个数没有限制.
2. 使用 GPIO 的特性就可以, 不需要专门的控制器.
3. 总线网络中只有一个主动设备, 其它设备均为从设备. 主设备发起通信, 从设备应答. 从设备不能发起通信.
4. 属于串行异步通信.
5. 通信时序严格, 在操作系统中, 必须在数据通信阶段阶段关闭调度(调度带来的延迟可以能有数毫秒, 将破坏时序).
6. 不同的 1-wire 设备, 通信协议和时序很可能不一样. 组建网络时, 确保使用遵守相同协议和时序的设备.
TPS6116x 的功能框图
1. 从上电谈起
上电后, 默认工作在 PWM 模式, 参考电压为 200mV. 如果要让设备工作在 1-wire 模式, 则要按照时序要求设置 CTRL.
上电的时候, 如果 CTRL 是高电平, 对是否 1-wire 模式的侦测被立即触发. 如果 CTRL 不符合时序要求, 则退出侦测.
任何时候, 如果 CTRL 是低电平, 且维持 2.5ms 以上, 则设备关机.
2. 无论是 PWM 模式, 还是 1-wire 模式, 都是在设置参考电压(Reference Control).
FB 的目标值是参考电压(Error Amplier).FB 与参考电压的误差值被输入 PWM Control, 以修正 PWM 的有效宽度.
3. Rset 电阻用来调整最大亮度. 参考电压的最大值为 200mV, 这样, 如果 Rset 的阻值为 10 欧姆, 那么 LED 的最大电流是 20mA.
4. PWM 的频率是 600KHz.
5. 设备 Shut down 的时候(CTRL 一直为低), 没有 PWM 输出. Vin 可以直接流向 LED. 所以 Vin 在设计上要小于 LEDs 的开启电压.
6. 设备从 Shut down 状态复位时 (CTRL 变高触发复位, 设备开机). 开机时如果 FB 寄存器值(用来设置参考电压的寄存器) 为 0(范围 0-31), 则 FB 寄存器被设定为 31(200mV).
开机时如果 FB 寄存器不为 0, 则不会修改 FB 寄存器的值. Datasheet 中指出, 不要直接设定 FB 寄存器值为 0, 如果想关机, 可以令设备 Shutdown(CTRL 置低 2.5ms).
7. Soft start-up 控制电路控制电压一级一级往上升, 以避免电流冲击.
TPS6116x 的时序
在 TPS6116x 的 Datasheet 中, 6.6 节详细列出时序要求.
名字 | 含义 | 电平 | 最小值 | 最大值 | 单位 |
tvalACKN | ACK 准备时间,从设备在此区间准备 ACK. TPS6116x 只有在数据最高位 (RFA) 为 1 时,从设备返回 ACK。并且从设备是 OpenDrain 输出的。 | 高 | 2 | us | |
tACKN | ACK 的有效区间。数据线被从设备拉低。 | 低 | 512 | us | |
toff | 设备关机时间。相当于电脑的长按开机键进行强制关机。 | 低 | 2500 | us | |
tes_det | EasyScale 模式侦测的真正开始。260us 超过 PWM 模式的最大低电平宽度(5kHz, 200us). | 低 | 260 | 2500 | us |
tes_delay | TPS6116x 从 Shutdown 状态退出 (2.5ms 后变高) 时开始侦测 EasyScale 模式的进入。必须维持至少 100us 的高位。也可能一直高。 | 高 | 100 | us | |
tes_win | EasyScale 模式侦测的总体时间。TPS6116x 从 Shutdown 状态退出 (2.5ms 后变高) 时开始侦测 EasyScale 模式的进入。包括 tes_delay 和 tes_det。 | 1000 | us | ||
tSTART | 字节处理的预备时间。也可以理解为字节之间的间隔。 | 高 | 2 | us | |
tEOS | 字节的结束。相当于 UART 的 STOP 位。 | 低 | 2 | 360 | us |
tH_LB | 逻辑 0 的高电平时间 | 高 | 2 | 180 | us |
tL_LB | 逻辑 0 的低电平时间 | 低 | 2 × tH_LB | 360 | us |
tL_HB | 逻辑 1 的低电平时间 | 低 | 2 | 180 | us |
tH_HB | 逻辑 1 的高电平时间 | 高 | 2 × tL_HB | 360 | us |
1. 上电
如果 CTRL PIN 引脚电平是高, 进入 tes_win, 开始判断是进入 EasyScale 的 1-wire 模式, 还是进入 PWM 模式.
如果 CTRL PIN 引脚电平是低, 2.5ms 之后设备 shutdown, 进入低耗电模式.
2. 进入 EasyScale 的 1-wire 模式.
1. 为了避免不确定的时序状态, 先置 CTRL PIN 引脚电平为低 3 秒. 设备进入 shutdown 状态.
- static void tps6116_shutdown(unsigned int gpio) {
- gpio_set_value(gpio, 0);
- mdelay(3); // tps6116 shut down.
- }
2. 置 CTRL PIN 为高, 进入 tes_win.tes_win 遵循下面的时序要求, 以进入 EasyScale 的 1-wire 模式.
- static void tps6116_reset(unsigned int gpio){
- gpio_set_value(gpio, 0);
- mdelay(3); // tps6116 shut down.
- gpio_set_value(gpio, 1); // reset, FB = 200mv. start EasyScale detection Windows.
- usleep(500); // tes_delay. min 100us
- preempt_disable(); // avoid the possbile shutdown. let me do it over.
- gpio_set_value(gpio, 0);
- usleep(300); // tes_det, min 260us
- gpio_set_value(gpio, 1);
- preempt_enable();
- usleep(500); // tes_win, min 1000us. 500 + 300 + 500 = 1300> 1000
- }
3. 发送字符 1.
- static void tps6116_send_bit_1(unsigned int gpio){
- gpio_set_value(gpio, 0);
- udelay(20); // pull low for 20us
- gpio_set_value(gpio,1);
- udelay(80); // pull high for 80us
- }
4. 发送字符 0.
- static void tps6116_send_bit_0(unsigned int gpio){
- gpio_set_value(gpio, 0);
- udelay(80); // pull low for 80us
- gpio_set_value(gpio,1);
- udelay(20); // pull high for 20us
- }
5. 发送字节.
- static void tps6116_send_byte(unsigned int gpio, unsigned char value){
- int i;
- int bit;
- udelay(50); // start
- for (i = 7; i>= 0; i++){
- bit = value & (1 << i); // High bit first
- if (bit){
- tps6116_send_bit_1(gpio);
- }
- else{
- tps6116_send_bit_0(gpio);
- }
- }
- gpio_set_value(gpio, 0); // End of start
- udelay(50);
- gpio_set_value(gpio, 1); // idle
- }
6. 发送帧.
数据帧包含两个字节, 第一个字节是地址 0x72(固定的). 第二个地址是数据, bit[0-4]是 FB 寄存器值 (0-31),bit[5-6] 是设备地址 (0),bit[7] 是 RFA, 表示是否希望设备传回 ACK. 使用 RFA, 通信会更复杂, 驱动还要改变 GPIO 方向, 并且由于设备 CTRL 是 OpenDrain 的, 主机端要加 4.7K 的上拉电阻.
- // irq code running time is very short, assume it does not effect the timming here.
- // if disable irq, the performance of the system will be effected.
- static void tps6116_set_fb_value(unsigned int gpio, unsigned char value){
- value = value & 0x1F; // RFA: 0, A1=A0=0
- preempt_disable(); // let me do it over.
- tps6116_send_byte(gpio, 0x72); // address: 0x72
- tps6116_send_byte(gpio, value);
- preempt_enable();
- }
TPS6116x 的 Linux 驱动的其它部分
1. 设置好平台驱动的数据结构.
- static struct platform_driver tps6116_bl_driver = {
- .driver = {
- .name = "tps6116-bl",
- .pm = &tps6116_bl_pm_ops,
- .of_match_table = of_match_ptr(tps6116_bl_of_match),
- },
- .probe = tps6116_bl_probe,
- .remove = tps6116_bl_remove,
- .shutdown = tps6116_bl_shutdown,
- };
- module_platform_driver(tps6116_bl_driver);
2. probe 的工作
1. 分配设备私有数据.
2. 从 dts 中获取必要的信息: default-brightness, max_brightness, gpio.
3. 初始化 gpio.
4. 注册背光驱动.
5. 初始设置背光.
6. 设置平台驱动数据. 这样, 从平台设备可以得到背光设备结构, 从背光设备结构可以得到设备数据. 这对应那些回调函数是很有必要的.
- struct backlight_device *bl = platform_get_drvdata(pdev);
- struct tps6116_bl *tps6116_bl = bl_get_data(bl);
最快的方法是找一个相似的背光驱动, 比如 pwm_bl.c, 看它是怎么与驱动框架交互的.
后语
看 Data sheet 或者看别人写的示例代码, 会忽略很多细节. 而在整理资料的过程当中, 为了把内容细致, 逻辑地呈现出来, 就会反复阅读和理解手头的资料, 很多细节就被挖掘出来. 所以养成资料整理的习惯是很有必要的.
来源: https://www.cnblogs.com/zjsxdmif/p/10144800.html