看到标题的诸君可能有疑虑了: CDK里面不是有现成的IIC外设接口吗?为什么还要多此一举自己写IIC驱动呢
官方关于iic外设使用链接 IIC接口实现
2.辩解(吐槽)诸君稍安勿躁 待我慢慢道来。我打算自己写的原因有二
其一 IIC引脚不够用下面这张图是官方提供的RVB2601开发板用户手册里面附录带的一个“GPIO 复用关系表”
里面黄框代表IIC复用引脚 红框代表它连接对象 因为我的项目需要用到音频、W800所以 引脚不够用
其二 官方提供的使用硬件IIC接口 效果不好把控。因为我在STM32上使用过硬件IIC 配置也比较简单 使用官方手册里面提供的IIC外设接口方法 效果可以实现 但是不同引脚的效果也不一样 让人心烦。
所以 我打算自己用软件写IIC驱动 个人使用效果—很nice。
IIC1.简介要点iic总线只有两根双向信号线。一根是数据线SDA 另一根是时钟线SCL 所有操作都靠这两根线完成。iic总线进行数据传送时 时钟信号为高电平期间 数据线上的数据必须保持稳定 只有在时钟线上的信号为低电平期间 数据线上的高电平或低电平状态才允许变化。参考图1可以看出SDA高低电平的变化都是在SCL低电平期间。2.时序图详解我写的驱动包括 起始信号、终止信号、主机等待从机应答信号、主机读取数据的应答信号、主机读取最后一个数据的非应答信号、主机发送一个字节数据、主机读取一个字节数据、主机给从机写数据、主机读取从机数据。 下面给大家详细介绍下 希望可以带大家回顾下IIC协议的时序。
(1)引脚定义和基本函数实现
1. //iic.h 2. //需要的头文件省略没写 这里重点说时序 4. csi_gpio_pin_t pin_SCL; 5. csi_gpio_pin_t pin_SDA; 7. #define IIC_SCL PA21 //这里引脚可以自己选 8. #define IIC_SDA PA22 10. #define SDA_IN() { csi_gpio_pin_dir( pin_SDA, GPIO_DIRECTION_INPUT);} //SDA为输入引脚 11. #define SDA_OUT() { csi_gpio_pin_dir( pin_SDA, GPIO_DIRECTION_OUTPUT);} //SDA为输出引脚 14. #define IIC_SCL1 {csi_gpio_pin_write( pin_SCL, GPIO_PIN_HIGH);} //给SCL写高电平 15. #define IIC_SCL0 {csi_gpio_pin_write( pin_SCL, GPIO_PIN_LOW);} //给SCL写低电平 17. #define IIC_SDA1 {csi_gpio_pin_write( pin_SDA, GPIO_PIN_HIGH);} //给SDA写高电平 18. #define IIC_SDA0 {csi_gpio_pin_write( pin_SDA, GPIO_PIN_LOW);} //给SDA写低电平 20. #define READ_SDA csi_gpio_pin_read( pin_SDA) //读取SDA的值 c
1. //iic引脚初始化 2. void IIC_Init(void) 3. { 4. csi_pin_set_mux(IIC_SCL, PIN_FUNC_GPIO); //SCL复用为GPIO 5. csi_pin_set_mux(IIC_SDA, PIN_FUNC_GPIO); //SDA复用为GPIO 7. csi_gpio_pin_init( pin_SCL, IIC_SCL); 8. csi_gpio_pin_dir( pin_SCL, GPIO_DIRECTION_OUTPUT); //SCL为输出 9. csi_gpio_pin_init( pin_SDA, IIC_SDA); 10. csi_gpio_pin_dir( pin_SDA, GPIO_DIRECTION_OUTPUT); //先配置成输出 12. IIC_SCL1; //先把SCL和SDA拉高的目的 给SDA一个下降沿 时序就开始了 13. IIC_SDA1; 14. } c
(2)起始信号和终止信号
如下图
SCL信号线为高电平期间 SDA线由高电平向低电平变化表示起始信号SCL信号线为高电平期间 SDA线由低电平向高电平变化表示终止信号因此
1. //函 数 名 void IIC_Start(void) 2. //功 能 IIC起始信号 3. void IIC_Start(void) 5. SDA_OUT(); //sda线输出 6. IIC_SDA1; 7. delay_us(4); 8. IIC_SCL1; 9. delay_us(4); 10. IIC_SDA0;//START:when CLK is high,DATA change form high to low 11. delay_us(4); 12. IIC_SCL0;//钳住I2C总线 准备发送或接收数据 13. //delay_us(4); 14. } 16. //函 数 名 void IIC_Stop() 17. //功 能 IIC停止信号 18. void IIC_Stop(void) 19. { 20. SDA_OUT();//sda线输出 21. IIC_SCL0; 22. delay_us(4); 23. IIC_SDA0;//STOP:when CLK is high DATA change form low to high 24. delay_us(4); 25. IIC_SCL1; 26. delay_us(4); 27. IIC_SDA1;//发送I2C总线结束信号 28. delay_us(4); 29. } c
(3)等待从机应答ACK
数据传送格式 每一个字节必须保证时8位长度。数据传送时 先传高位 每个被传送的字节后面必须跟随一位应答位 在主机向从机传送数据时应答位由从机产生 主机检测从机产生的应答信号 如果从机应答时间超时 主机默认数据传输完成 本段代码就是检测从机的应答信号。
1. //等待应答信号到来 2. //返回值 1 接收应答失败 3. // 0 接收应答成功 4. uint8_t IIC_Wait_Ack(void) 6. uint8_t ucErrTime 7. SDA_IN(); //SDA设置为输入 8. IIC_SDA1;delay_us(6); 9. IIC_SCL1;delay_us(6); 10. while(READ_SDA) 11. { 12. ucErrTime 13. if(ucErrTime 250) 14. { 15. IIC_Stop(); 16. return 1; 17. } 18. } 19. IIC_SCL0;//时钟输出0 20. return 0; 21. } c
(4)主机读数据产生的应答信号或非应答信号
主机读数据时有这样的规定 当主机每读一个数据主机都要回复应答信号。当最后一个字节数据读完后 主机要返回以“非应答” 并终止信号读出操作 这里的应答信号和非应答信号作用是给从机判断是否继续读取数据。由图可以看出应答信号是在SDA低电平期间 SCL由低置高再置低。非应答信号是在SDA位高电平期间SCL由低置高再置低。
1. //函 数 名 void IIC_Ack(void) 2. //功 能 IIC产生ACK应答信号 3. void IIC_Ack(void) 5. IIC_SCL0; 6. SDA_OUT(); 7. IIC_SDA0; 8. delay_us(10); 9. IIC_SCL1; 10. delay_us(10); 11. IIC_SCL0; 12. } 15. //函 数 名 void IIC_NAck(void) 16. //功 能 IIC产生NACK应答信号 17. void IIC_NAck(void) 18. { 19. IIC_SCL0; 20. SDA_OUT(); 21. IIC_SDA1; 22. delay_us(10); 23. IIC_SCL1; 24. delay_us(10); 25. IIC_SCL0; 26. } 28. C c
(5)主机发送数据
主设备在传输有效数据之前要先指定从设备的地址 地址指定的过程和上面数据传输的过程一样 只不过大多数从设备的地址是7位的 然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向 0表示主设备向从设备写数据 1表示主设备向从设备读数据。
主设备往从设备中写数据。数据传输格式如下
1. //IIC发送一个字节 2. //返回从机有无应答 3. //1 有应答 4. //0 无应答 5. void IIC_Send_Byte(uint8_t txd) 6. { 7. uint8_t t; 8. SDA_OUT(); 9. IIC_SCL0;//拉低时钟开始数据传输 10. for(t t t ) 11. { 12. if((txd 0x80) 7) 13. { 14. IIC_SDA1; 15. } 16. else 17. IIC_SDA0; 19. txd 20. delay_us(10); //这三个延时都是必须的 21. IIC_SCL1; 22. delay_us(10); 23. IIC_SCL0; 24. delay_us(10); 25. } 26. } 28. //完整IIC主机写一个字节数据 29. uint8_t IIC_Write_1Byte(uint8_t SlaveAddress, uint8_t REG_Address,uint8_t REG_data) 30. { 31. IIC_Start(); 32. IIC_Send_Byte(SlaveAddress); 33. if(IIC_Wait_Ack()) 34. { 35. IIC_Stop();//释放总线 36. return 1;//没应答则退出 38. } 39. IIC_Send_Byte(REG_Address); 40. IIC_Wait_Ack(); 41. IIC_Send_Byte(REG_data); 42. IIC_Wait_Ack(); 43. IIC_Stop(); 45. return 0; 46. } c
(5)主机读取数据
主设备从从设备中读数据。数据传输格式如下
1. //读1个字节 ack 1时 发送ACK ack 0 发送nACK 2. uint8_t IIC_Read_Byte(unsigned char ack) 4. unsigned char i,receive 5. SDA_IN();//SDA设置为输入 6. for(i i i ) 8. IIC_SCL0; 9. delay_us(10); 10. IIC_SCL1; 11. receive 12. if(READ_SDA)receive 13. delay_us(5); 14. } 15. if (!ack) 16. IIC_NAck();//发送nACK 17. else 18. IIC_Ack(); //发送ACK 19. return receive; 20. } 23. //IIC读一个字节数据 24. uint8_t IIC_Read_1Byte(uint8_t SlaveAddress, uint8_t REG_Address,uint8_t *REG_data) 25. { 26. IIC_Start(); 27. IIC_Send_Byte(SlaveAddress);//发写命令 28. if(IIC_Wait_Ack()) 29. { 30. IIC_Stop();//释放总线 31. return 1;//没应答则退出 32. } 33. IIC_Send_Byte(REG_Address); 34. IIC_Wait_Ack(); 35. IIC_Start(); 36. IIC_Send_Byte(SlaveAddress|0x01);//发读命令 37. IIC_Wait_Ack(); 38. *REG_data IIC_Read_Byte(0); 39. IIC_Stop(); 41. return 0; 42. } c结束语
以上就是一个完整的iic驱动解析 大家有问题欢迎在评论区交流 一起学习 一起进步。下面附录是写的iic驱动的代码 本人能力有限 还请各位大佬多多指教。
附录驱动下载链接iic驱动
云虚拟主机 可以干什么?云 虚拟主机 可以是搭 建网站 的重要产品,可用来存放网...
客户简介 趣医网(quyiyuan.com)创立于2014年,为京颐集团重要成员企业之一,是...
3月24日,腾讯发布2020年Q4及全年财报,其中金融科技及企业服务第四季收入385亿...
??提到慕尼黑,大家第一个想到总是啤酒节,其实慕尼黑的文化同样闻名世界。慕尼...
客户简介 全民直播是一家涵盖游戏、娱乐、户外等多领域泛娱乐的直播平台。2015年...
排查思路 无法通过远程桌面连接裸金属服务器时,我们推荐您按照以下思路排查问题...
案例背景 高校健康打卡项目发起于北京大学软件与微电子学院,是该学院张齐勋老师...
公司介绍 我们公司是全球法律服务整合平台,已有的4万多名律师遍布全国359个城市...
注册了 域名 不备案可以吗?可以的。 注册域名 并不是一定要备案的,只有搭 建网...
API风格说明 当前ECS服务对外开放两类风格的API: ECS服务自定义规范的API(以下...