有时候会遇到IO不够用的情况,例如说驱动LED灯,那么有没有什么便宜的,容易买到的芯片?我这次就考虑使用WCH的CH423S,这是一个比较新的IO扩展芯片。
使用gpio来模拟IIC,飞线处理:IO_SDA_ctl链接GPIO3_B2 IO_SCL_ctl链接GPIO4_ B6。
- ch423: ch423@40{
- status ="okay";
- compatible ="ch423";
- ch423-clk-gpios =<&gpio3 RK PB2 GPIO ACTIVE HIGH>;
- ch423-dat-gpios= <&gpio4 RK_PB6 GPIO ACTIVE_HIGH>;
- }
-
使用gpio命令查看,两个模拟gpio iic模拟脚已经注册成功。
- #define DELAY 1
- typedef unsigned char u8;
- typedef unsigned short u16;
-
-
- /* 这些代码是框架,直接使用即可 */
-
-
- // 定义一个延时xms毫秒的延时函数
- void delay(unsigned int xms) // xms代表需要延时的毫秒数
- {
- unsigned int x,y;
- for(x = xms; x > 0; x--)
- for(y = 110; y > 0; y--);
- }
-
-
-
-
- /******************************start*****************************************/
- static void i2c_start(void)
- {
- gpio_direction_output(SDA_PIN, 1);
- gpio_direction_output(CLK_PIN, 1);
- delay(DELAY);
- gpio_set_value(SDA_PIN, 0);
- delay(DELAY);
- gpio_set_value(CLK_PIN, 0);
- delay(DELAY);
-
- }
-
-
-
-
-
- /**********************************************stop****************************************/
- static void i2c_stop(void)
- {
- gpio_set_value(CLK_PIN, 0);
- gpio_set_value(SDA_PIN, 0);
- delay(DELAY);
-
- gpio_set_value(CLK_PIN, 1);
- delay(DELAY);
- gpio_set_value(SDA_PIN, 1);
- delay(DELAY);
-
- }
-
-
-
-
- /********************************send_ack****************************************/
- /* 参数ack为1时发送NACK,参数ack为0时表示发送ACK */
- static void i2c_send_ack(u8 ack)
- {
- if(ack)
- gpio_direction_output(SDA_PIN, 1);
- else
- gpio_direction_output(SDA_PIN, 0);
- delay(DELAY);
-
- gpio_set_value(CLK_PIN, 1);
- delay(DELAY);
- gpio_set_value(CLK_PIN, 0);
- delay(DELAY);
- }
-
-
-
-
-
- /********************************receive_ack*********************************/
- /* 返回1表示无效ACK,返回0表示有效ACK */
- static u8 i2c_receive_ack(void)
- {
- u8 rc = 0;
- gpio_direction_input(SDA_PIN);
- gpio_set_value(CLK_PIN, 1);
- delay(DELAY);
-
- /* 获取ack信号,如果信号为低,则为有效ACK */
- if(gpio_get_value(SDA_PIN))
- {
- rc = 1;
- }
- gpio_set_value(CLK_PIN, 0);
- gpio_direction_output(SDA_PIN, 1);
- return rc;
- }
-
-
-
-
- /********************************send****************************************/
- /* 返回值为0,表示接收到ACK信号,表示发送成功
- * 返回值为1,表示没有收到ACK信号,表示发送失败
- */
- static u8 i2c_send_byte(u8 send_byte)
- {
- u8 rc = 0;
- u8 out_mask = 0x80;
- u8 value;
- u8 count = 8;
- while(count > 0)
- {
- value = ((send_byte & out_mask) ? 1 : 0);
- if (value == 1)
- {
- gpio_set_value(SDA_PIN, 1);
- }
- else
- {
- gpio_set_value(SDA_PIN, 0);
- }
- delay(DELAY);
-
- gpio_set_value(CLK_PIN, 1);
- delay(DELAY);
- gpio_set_value(CLK_PIN, 0);
- delay(DELAY);
- out_mask >>= 1;
- count--;
- }
-
- gpio_set_value(SDA_PIN, 1);
- rc = i2c_receive_ack();
- return rc;
- }
-
-
-
-
-
- /**********************************************receive*************************************/
- static void i2c_read_byte(u8 *buffer, u8 ack)
- {
- u8 count = 0x08;
- u8 data = 0x00;
- u8 temp = 0;
-
- gpio_direction_input(SDA_PIN);
- while(count > 0)
- {
- gpio_set_value(CLK_PIN, 1);
- delay(DELAY);
- temp = gpio_get_value(SDA_PIN);
- data <<= 1;
- if (temp)
- data |= 0x01;
- gpio_set_value(CLK_PIN, 0);
- delay(DELAY);
- count--;
- }
- i2c_send_ack(ack);//0 = ACK, 1 = NACK
- *buffer = data;
- }
-
-
-
-
-
-
- //向client的某个寄存器写入多个字节,len是要写入的数据的长度???
- /***********************************************write_bytes********************************/
- /* 返回值为0表示收到ACK信号,发送成功
- * 返回值为1表示没有收到ACK信号,发送失败
- */
- static u8 i2c_write(u8 device_id, u8 reg_address, u8* data, u8 len)
- {
- u8 rc = 0;
- u8 i;
- i2c_start();
-
- rc |= i2c_send_byte( (device_id << 1) | 0x00 );
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
- delay(10);
-
- rc |= i2c_send_byte(reg_address);
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
- delay(10);
-
- if(data==NULL ||0==len)
- {
- i2c_stop();
- return rc;
- }
- delay(10);
-
- for(i=0; i<len; i++)
- {
- rc |= i2c_send_byte(*data);
- delay(10);
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
-
- data++;
- }
-
- i2c_stop();
-
- return rc;
- }
-
-
-
-
- //从某个register中读取len个字节放在长度为len的缓冲区buffer中???
- /***********************************************read_bytes********************************/
- /* 返回值为0表示收到ACK信号,发送成功
- * 返回值为1表示没有收到ACK信号,发送失败
- */
- static u8 i2c_read(u8 device_id, u8 reg_address, u8 *buffer, u16 len)
- {
- u8 rc = 0;
- u16 i;
- i2c_start();
- rc |= i2c_send_byte( (device_id << 1) | 0x00 );
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
- delay(10);
- rc |= i2c_send_byte(reg_address);
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
-
- delay(10);
- i2c_start();//restart I2C
- rc |= i2c_send_byte( (device_id << 1) | 0x01 );
- if ( 0 != rc )
- {
- printf("i2c_send_byte failed, no ack back\n");
- return rc;
- }
-
- delay(10);
- for(i=0;i<len;i++)
- {
- i2c_read_byte(buffer++, !(len-i-1));// !(len-i-1)? 这个用来保证在读到每个字节后发送一个ACK并能在最后一个字节读完后发送一个NACK
- delay(10);
-
- }
-
- i2c_stop();
- return rc;
- }
-
-
-
-
- /* 设置gpio为输出功能,并且设置输出值为value */
- static void gpio_direction_output(unsigned int gpio, int value)
- {
-
- //实现具体平台的代码
- }
-
-
- /* 设置gpio为输入功能 */
- static void gpio_direction_input(unsigned int gpio)
- {
-
- //实现具体平台的代码
- }
-
-
-
- /* 设置gpo的值为value */
- static void gpio_set_value(unsigned int gpio, int value)
- {
- //实现具体平台的代码
-
- }
-
-
- //返回gpio的值
- static unsigned int gpio_get_value(unsigned int gpio)
- {
- //实现具体平台的代码
-
- }
模拟IIC一直返回ACK为1.
ACK返回1,说明IIC没有写进去,获取不到IIC的返回值。
逻辑分析仪使用链接:
第十三章 Kingst VIS逻辑分析仪的使用_la1010软件_KermanXin的博客-CSDN博客
注意需要选择好解析器的类型:
发现解析不到IIC时序,应该波形有问题。
确认IIC波形:
发现IIC的波形颠倒了,把data引脚和clk引脚的GPIO交换试一下。
- ch423: ch423@401{
- status ="okay";
- compatible ="ch423":
- ch423-dat-gpios = <&gpio3 RK PB2 GPIO ACTIVE HIGH>;
- ch423-clk-gpios = <&gpio4 RK PB6 GPIO ACTIVE HIGH>;
- }
此时没有报ACK错误,IIC时序正确,IIC写入成功。