• RK3588 添加I2C模拟芯片CH423


    一.简介

    有时候会遇到IO不够用的情况,例如说驱动LED灯,那么有没有什么便宜的,容易买到的芯片?我这次就考虑使用WCH的CH423S,这是一个比较新的IO扩展芯片。

    二.硬件原理图

    使用gpio来模拟IIC,飞线处理:IO_SDA_ctl链接GPIO3_B2  IO_SCL_ctl链接GPIO4_ B6。

    三.添加设备树节点

    1. ch423: ch423@40{
    2. status ="okay";
    3. compatible ="ch423";
    4. ch423-clk-gpios =<&gpio3 RK PB2 GPIO ACTIVE HIGH>;
    5. ch423-dat-gpios= <&gpio4 RK_PB6 GPIO ACTIVE_HIGH>;
    6. }

    使用gpio命令查看,两个模拟gpio iic模拟脚已经注册成功。 

    四.模拟iic伪代码

    1. #define DELAY 1
    2. typedef unsigned char u8;
    3. typedef unsigned short u16;
    4. /* 这些代码是框架,直接使用即可 */
    5. // 定义一个延时xms毫秒的延时函数
    6. void delay(unsigned int xms) // xms代表需要延时的毫秒数
    7. {
    8. unsigned int x,y;
    9. for(x = xms; x > 0; x--)
    10. for(y = 110; y > 0; y--);
    11. }
    12. /******************************start*****************************************/
    13. static void i2c_start(void)
    14. {
    15. gpio_direction_output(SDA_PIN, 1);
    16. gpio_direction_output(CLK_PIN, 1);
    17. delay(DELAY);
    18. gpio_set_value(SDA_PIN, 0);
    19. delay(DELAY);
    20. gpio_set_value(CLK_PIN, 0);
    21. delay(DELAY);
    22. }
    23. /**********************************************stop****************************************/
    24. static void i2c_stop(void)
    25. {
    26. gpio_set_value(CLK_PIN, 0);
    27. gpio_set_value(SDA_PIN, 0);
    28. delay(DELAY);
    29. gpio_set_value(CLK_PIN, 1);
    30. delay(DELAY);
    31. gpio_set_value(SDA_PIN, 1);
    32. delay(DELAY);
    33. }
    34. /********************************send_ack****************************************/
    35. /* 参数ack为1时发送NACK,参数ack为0时表示发送ACK */
    36. static void i2c_send_ack(u8 ack)
    37. {
    38. if(ack)
    39. gpio_direction_output(SDA_PIN, 1);
    40. else
    41. gpio_direction_output(SDA_PIN, 0);
    42. delay(DELAY);
    43. gpio_set_value(CLK_PIN, 1);
    44. delay(DELAY);
    45. gpio_set_value(CLK_PIN, 0);
    46. delay(DELAY);
    47. }
    48. /********************************receive_ack*********************************/
    49. /* 返回1表示无效ACK,返回0表示有效ACK */
    50. static u8 i2c_receive_ack(void)
    51. {
    52. u8 rc = 0;
    53. gpio_direction_input(SDA_PIN);
    54. gpio_set_value(CLK_PIN, 1);
    55. delay(DELAY);
    56. /* 获取ack信号,如果信号为低,则为有效ACK */
    57. if(gpio_get_value(SDA_PIN))
    58. {
    59. rc = 1;
    60. }
    61. gpio_set_value(CLK_PIN, 0);
    62. gpio_direction_output(SDA_PIN, 1);
    63. return rc;
    64. }
    65. /********************************send****************************************/
    66. /* 返回值为0,表示接收到ACK信号,表示发送成功
    67. * 返回值为1,表示没有收到ACK信号,表示发送失败
    68. */
    69. static u8 i2c_send_byte(u8 send_byte)
    70. {
    71. u8 rc = 0;
    72. u8 out_mask = 0x80;
    73. u8 value;
    74. u8 count = 8;
    75. while(count > 0)
    76. {
    77. value = ((send_byte & out_mask) ? 1 : 0);
    78. if (value == 1)
    79. {
    80. gpio_set_value(SDA_PIN, 1);
    81. }
    82. else
    83. {
    84. gpio_set_value(SDA_PIN, 0);
    85. }
    86. delay(DELAY);
    87. gpio_set_value(CLK_PIN, 1);
    88. delay(DELAY);
    89. gpio_set_value(CLK_PIN, 0);
    90. delay(DELAY);
    91. out_mask >>= 1;
    92. count--;
    93. }
    94. gpio_set_value(SDA_PIN, 1);
    95. rc = i2c_receive_ack();
    96. return rc;
    97. }
    98. /**********************************************receive*************************************/
    99. static void i2c_read_byte(u8 *buffer, u8 ack)
    100. {
    101. u8 count = 0x08;
    102. u8 data = 0x00;
    103. u8 temp = 0;
    104. gpio_direction_input(SDA_PIN);
    105. while(count > 0)
    106. {
    107. gpio_set_value(CLK_PIN, 1);
    108. delay(DELAY);
    109. temp = gpio_get_value(SDA_PIN);
    110. data <<= 1;
    111. if (temp)
    112. data |= 0x01;
    113. gpio_set_value(CLK_PIN, 0);
    114. delay(DELAY);
    115. count--;
    116. }
    117. i2c_send_ack(ack);//0 = ACK, 1 = NACK
    118. *buffer = data;
    119. }
    120. //向client的某个寄存器写入多个字节,len是要写入的数据的长度???
    121. /***********************************************write_bytes********************************/
    122. /* 返回值为0表示收到ACK信号,发送成功
    123. * 返回值为1表示没有收到ACK信号,发送失败
    124. */
    125. static u8 i2c_write(u8 device_id, u8 reg_address, u8* data, u8 len)
    126. {
    127. u8 rc = 0;
    128. u8 i;
    129. i2c_start();
    130. rc |= i2c_send_byte( (device_id << 1) | 0x00 );
    131. if ( 0 != rc )
    132. {
    133. printf("i2c_send_byte failed, no ack back\n");
    134. return rc;
    135. }
    136. delay(10);
    137. rc |= i2c_send_byte(reg_address);
    138. if ( 0 != rc )
    139. {
    140. printf("i2c_send_byte failed, no ack back\n");
    141. return rc;
    142. }
    143. delay(10);
    144. if(data==NULL ||0==len)
    145. {
    146. i2c_stop();
    147. return rc;
    148. }
    149. delay(10);
    150. for(i=0; i<len; i++)
    151. {
    152. rc |= i2c_send_byte(*data);
    153. delay(10);
    154. if ( 0 != rc )
    155. {
    156. printf("i2c_send_byte failed, no ack back\n");
    157. return rc;
    158. }
    159. data++;
    160. }
    161. i2c_stop();
    162. return rc;
    163. }
    164. //从某个register中读取len个字节放在长度为len的缓冲区buffer中???
    165. /***********************************************read_bytes********************************/
    166. /* 返回值为0表示收到ACK信号,发送成功
    167. * 返回值为1表示没有收到ACK信号,发送失败
    168. */
    169. static u8 i2c_read(u8 device_id, u8 reg_address, u8 *buffer, u16 len)
    170. {
    171. u8 rc = 0;
    172. u16 i;
    173. i2c_start();
    174. rc |= i2c_send_byte( (device_id << 1) | 0x00 );
    175. if ( 0 != rc )
    176. {
    177. printf("i2c_send_byte failed, no ack back\n");
    178. return rc;
    179. }
    180. delay(10);
    181. rc |= i2c_send_byte(reg_address);
    182. if ( 0 != rc )
    183. {
    184. printf("i2c_send_byte failed, no ack back\n");
    185. return rc;
    186. }
    187. delay(10);
    188. i2c_start();//restart I2C
    189. rc |= i2c_send_byte( (device_id << 1) | 0x01 );
    190. if ( 0 != rc )
    191. {
    192. printf("i2c_send_byte failed, no ack back\n");
    193. return rc;
    194. }
    195. delay(10);
    196. for(i=0;i<len;i++)
    197. {
    198. i2c_read_byte(buffer++, !(len-i-1));// !(len-i-1)? 这个用来保证在读到每个字节后发送一个ACK并能在最后一个字节读完后发送一个NACK
    199. delay(10);
    200. }
    201. i2c_stop();
    202. return rc;
    203. }
    1. /* 设置gpio为输出功能,并且设置输出值为value */
    2. static void gpio_direction_output(unsigned int gpio, int value)
    3. {
    4. //实现具体平台的代码
    5. }
    6. /* 设置gpio为输入功能 */
    7. static void gpio_direction_input(unsigned int gpio)
    8. {
    9. //实现具体平台的代码
    10. }
    11. /* 设置gpo的值为value */
    12. static void gpio_set_value(unsigned int gpio, int value)
    13. {
    14. //实现具体平台的代码
    15. }
    16. //返回gpio的值
    17. static unsigned int gpio_get_value(unsigned int gpio)
    18. {
    19. //实现具体平台的代码
    20. }

    五.log分析

    模拟IIC一直返回ACK为1.

    ACK返回1,说明IIC没有写进去,获取不到IIC的返回值。

    六.使用逻辑分析仪解析IIC时序

    逻辑分析仪使用链接:

    第十三章 Kingst VIS逻辑分析仪的使用_la1010软件_KermanXin的博客-CSDN博客

    注意需要选择好解析器的类型:

    发现解析不到IIC时序,应该波形有问题。

    确认IIC波形:

    发现IIC的波形颠倒了,把data引脚和clk引脚的GPIO交换试一下。

    1. ch423: ch423@401{
    2. status ="okay";
    3. compatible ="ch423":
    4. ch423-dat-gpios = <&gpio3 RK PB2 GPIO ACTIVE HIGH>;
    5. ch423-clk-gpios = <&gpio4 RK PB6 GPIO ACTIVE HIGH>;
    6. }

    此时没有报ACK错误,IIC时序正确,IIC写入成功。

  • 相关阅读:
    单片机之瑞萨RL78 串口通信的例子
    JAVA大学生备考平台计算机毕业设计Mybatis+系统+数据库+调试部署
    瞪羚企业申报程序和要求 ?
    【堆的应用】细说Top-K问题
    Linux操作系统 Vim编辑器基础操作指令
    转型阵痛期,好未来减亏容易增收难?
    csharp开发日常之Activator.CreateInstance构造函数生成实例
    图文版实现无头非循环单链表的增加和删除操作
    火狐挂代理访问问题Software is preventing Firefox from safely connecting to this site
    指令重排以及案例
  • 原文地址:https://blog.csdn.net/weixin_49303682/article/details/132921219