I2c.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. #include "I2c.h"
  2. #include "systick.h"
  3. //读取SDA端口
  4. #define READ_I2CSDA() gpio_input_bit_get(GPIOB, GPIO_PIN_7)
  5. //时钟线SCL输出高电平
  6. #define SET_I2C_SCL() gpio_bit_set(GPIOB, GPIO_PIN_6)
  7. //时钟线SCL输出低电平
  8. #define CLR_I2C_SCL() gpio_bit_reset(GPIOB, GPIO_PIN_6)
  9. //数据线SDA输出高电平
  10. #define SET_I2C_SDA() gpio_bit_set(GPIOB, GPIO_PIN_7)
  11. //数据线SDA输出低电平
  12. #define CLR_I2C_SDA() gpio_bit_reset(GPIOB, GPIO_PIN_7)
  13. static void ConfigI2CGPIO(void); //配置I2C的GPIO
  14. static void SetI2CSDAAsInput(void); //将SDA端设置为输入
  15. static void SetI2CSDAAsOutput(void); //将SDA端设置为输出
  16. static void ConfigI2CGPIO(void)
  17. {
  18. //配置PC13
  19. rcu_periph_clock_enable(RCU_GPIOB); //使能GPIOC的时钟
  20. gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5);
  21. gpio_bit_reset(GPIOB,GPIO_PIN_5);
  22. //配置PB7
  23. gpio_init(GPIOB,GPIO_MODE_OUT_OD,GPIO_OSPEED_50MHZ,GPIO_PIN_7); //设置GPIO输出模式及速度 SDA
  24. gpio_bit_set(GPIOB,GPIO_PIN_7);
  25. //配置PB6
  26. gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_6); //设置GPIO输出模式及速度 SCL
  27. gpio_bit_set(GPIOB,GPIO_PIN_6);
  28. }
  29. /*********************************************************************************************************
  30. * 函数名称: SetI2CSDAAsInput
  31. * 函数功能: 将SDA端设置为输入
  32. * 输入参数: void
  33. * 输出参数: void
  34. * 返 回 值: void
  35. * 创建日期: 2021年07月01日
  36. * 注 意:
  37. *********************************************************************************************************/
  38. static void SetI2CSDAAsInput(void)
  39. {
  40. //gpio_init(GPIOB,GPIO_MODE_IPD,GPIO_OSPEED_50MHZ,GPIO_PIN_7);
  41. //gpio_mode_set(GPIOF, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO_PIN_0); //配置PF0为输入模式
  42. }
  43. /*********************************************************************************************************
  44. * 函数名称: SetI2CSDAAsOutput
  45. * 函数功能: 将SDA端设置为输出
  46. * 输入参数: void
  47. * 输出参数: void
  48. * 返 回 值: void
  49. * 创建日期: 2021年07月01日
  50. * 注 意:
  51. *********************************************************************************************************/
  52. static void SetI2CSDAAsOutput(void)
  53. {
  54. //gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0); //设置PF0为输出模式
  55. //gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0); //设置输出模式及速度
  56. //gpio_bit_set(GPIOF, GPIO_PIN_7); //状态默认设置为高电平
  57. //gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_7);
  58. //gpio_bit_set(GPIOB, GPIO_PIN_7);
  59. }
  60. /*********************************************************************************************************
  61. * API函数实现
  62. *********************************************************************************************************/
  63. /*********************************************************************************************************
  64. * 函数名称: InitI2C
  65. * 函数功能: 初始化IIC
  66. * 输入参数: void
  67. * 输出参数: void
  68. * 返 回 值: void
  69. * 创建日期: 2021年07月01日
  70. * 注 意:
  71. *********************************************************************************************************/
  72. void InitI2C(void)
  73. {
  74. ConfigI2CGPIO(); //配置I2C的GPIO
  75. }
  76. /*********************************************************************************************************
  77. * 函数名称: GenI2CStartSig
  78. * 函数功能: 产生IIC起始时序,准备发送或接收数据前必须由起始序列开始
  79. * 输入参数: void
  80. * 输出参数: void
  81. * 返 回 值: void
  82. * 创建日期: 2021年07月01日
  83. * 注 意: SCL为高电平时,SDA由高电平向低电平跳变,开始传输数据
  84. * 生成下图所示的波形图,即为起始时序
  85. * 1 2 3 4
  86. * __________
  87. * SCL : __/ \_____
  88. * ________
  89. * SDA : \___________
  90. *********************************************************************************************************/
  91. void GenI2CStartSig(void)
  92. {
  93. SetI2CSDAAsOutput(); //将数据线SDA设置为输出
  94. SET_I2C_SDA(); //1#数据线SDA输出高电平
  95. SET_I2C_SCL(); //2#时钟线SCL输出高电平,2-3之间的间隔须>4.7us
  96. DelayNus(4); //延时4us
  97. CLR_I2C_SDA(); //3#数据线SDA输出低电平,3-4之间的间隔须>4.0us
  98. DelayNus(4); //延时4us
  99. CLR_I2C_SCL(); //4#时钟线SCL输出低电平,保持I2C的时钟线SCL为低电平,准备发送或接收数据
  100. }
  101. /*********************************************************************************************************
  102. * 函数名称: GenI2CStopSig
  103. * 函数功能: 产生IIC停止时序
  104. * 输入参数: void
  105. * 输出参数: void
  106. * 返 回 值: void
  107. * 创建日期: 2021年07月01日
  108. * 注 意: SCL为高电平时,SDA由低电平向高电平跳变,结束传输数据
  109. * 生成下图所示的波形图,即为停止时序
  110. * 1 2 3 4
  111. * _______________
  112. * SCL : ______/
  113. * __ ____________
  114. * SDA: \______/
  115. *********************************************************************************************************/
  116. void GenI2CStopSig(void)
  117. {
  118. SetI2CSDAAsOutput(); //将数据线SDA设置为输出
  119. CLR_I2C_SCL(); //1#时钟线SCL输出低电平
  120. CLR_I2C_SDA(); //2#数据线SDA输出低电平
  121. DelayNus(4); //延时4us
  122. SET_I2C_SCL(); //3#时钟线SCL输出高电平,3-4之间的间隔须>4.7us
  123. SET_I2C_SDA(); //4#数据线SDA输出高电平,发送I2C总线结束信号,4之后SDA须保持不小于4.0us的高电平
  124. DelayNus(4); //延时4us
  125. }
  126. /*********************************************************************************************************
  127. * 函数名称: I2CSendByte
  128. * 函数功能: 发送一字节,数据从高位开始发送出去
  129. * 输入参数: txd
  130. * 输出参数: void
  131. * 返 回 值: void
  132. * 创建日期: 2021年07月01日
  133. * 注 意: 下面是具体的时序图
  134. * 1 2 3 4
  135. * ______
  136. * SCL: ________/ \______
  137. * ______________________
  138. * SDA: \\\___________________
  139. *********************************************************************************************************/
  140. void I2CSendByte(unsigned char txd)
  141. {
  142. unsigned char t; //循环计数器
  143. SetI2CSDAAsOutput(); //将数据线SDA设置为输出
  144. CLR_I2C_SCL(); //1#时钟线SCL输出低电平,开始数据传输
  145. for(t = 0; t < 8; t++) //循环8次,从高到低取出字节的8个位
  146. {
  147. if((txd&0x80) >> 7) //2#取出字节最高位,并判断为‘0’还是‘1’,从而做出相应的操作
  148. {
  149. SET_I2C_SDA(); //数据线SDA输出高电平,数据位为‘1’
  150. }
  151. else
  152. {
  153. CLR_I2C_SDA(); //数据线SDA输出低电平,数据位为‘0’
  154. }
  155. txd <<= 1; //左移一位,次高位移到最高位
  156. DelayNus(2); //延时2us
  157. SET_I2C_SCL(); //3#时钟线SCL输出高电平
  158. DelayNus(2); //延时2us
  159. CLR_I2C_SCL(); //4#时钟线SCL输出低电平
  160. DelayNus(2); //延时2us
  161. }
  162. }
  163. /*********************************************************************************************************
  164. * 函数名称: I2CReadByte
  165. * 函数功能: 读取一字节数据,当ack=1时,发送ACK,当ack=0时,发送nACK
  166. * 输入参数: txd
  167. * 输出参数: void
  168. * 返 回 值: void
  169. * 创建日期: 2021年07月01日
  170. * 注 意: 下面是具体的时序图
  171. * ______
  172. * SCL: ______/ \___
  173. * ____________________
  174. * SDA: \\\\______________\\\
  175. *********************************************************************************************************/
  176. unsigned char I2CReadByte(unsigned char ack)
  177. {
  178. unsigned char i = 0; //i为循环计数器
  179. unsigned char receive = 0; //receive用来存放接收的数据
  180. SetI2CSDAAsInput(); //1#将数据线SDA设置为输入
  181. for(i = 0; i < 8; i++ ) //循环8次,从高到低读取字节的8个位
  182. {
  183. CLR_I2C_SCL(); //2#时钟线SCL输出低电平
  184. DelayNus(2); //延时2us
  185. SET_I2C_SCL(); //3#时钟线SCL输出高电平
  186. receive <<= 1; //左移一位,空出新的最低位
  187. if(READ_I2CSDA()) //4#读取数据线SDA的数据位
  188. {
  189. receive++; //在SCL的上升沿后,数据已经稳定,因此可以取该数据,存入最低位
  190. }
  191. DelayNus(1); //延时1us
  192. }
  193. if(NACK == ack) //如果ack为NACK
  194. {
  195. SendI2CNAck(); //发送NACK,无应答
  196. }
  197. else //如果ack为ACK
  198. {
  199. SendI2CAck(); //发送ACK,应答
  200. }
  201. return receive; //返回读取到的数据
  202. }
  203. /*********************************************************************************************************
  204. * 函数名称: I2CWaitAck
  205. * 函数功能: 等待应答信号到来
  206. * 输入参数: void
  207. * 输出参数: void
  208. * 返 回 值: 1,接收应答失败;0,接收应答成功
  209. * 创建日期: 2021年07月01日
  210. * 注 意: 当SDA拉低后,表示接收到ACK信号,然后,拉低SCL,根据实际情况做出是否继续传递信号的判断
  211. * 此处表示发送端收到接收端的ACK
  212. * _______|____
  213. * SCL: | \_________
  214. * _______|
  215. * SDA: \_____________
  216. *********************************************************************************************************/
  217. unsigned char I2CWaitAck(void)
  218. {
  219. unsigned char ucErrTime = 0;
  220. SetI2CSDAAsInput(); //将数据线SDA设置为输出
  221. SET_I2C_SCL(); //时钟线SCL输出高电平
  222. DelayNus(1); //延时1us
  223. while(READ_I2CSDA()) //读回来的数据如果是高电平,即接收端没有应答
  224. {
  225. ucErrTime++; //计数器加1
  226. if(ucErrTime > 250) //如果超过250次,则判断为接收端出现故障,因此发送结束信号
  227. {
  228. GenI2CStopSig(); //产生一个停止信号
  229. return 1; //返回值为1,表示没有收到应答信号
  230. }
  231. }
  232. CLR_I2C_SCL(); //表示已收到应答信号,时钟线SCL输出低电平,钳住I2C总线
  233. return 0; //返回值为0,表示接收应答成功
  234. }
  235. /*********************************************************************************************************
  236. * 函数名称: SendI2CAck
  237. * 函数功能: 发送应答信号
  238. * 输入参数: void
  239. * 输出参数: void
  240. * 返 回 值: void
  241. * 创建日期: 2021年07月01日
  242. * 注 意: 下面是具体的时序图
  243. * 1 2 3 4
  244. * ______
  245. * SCL: ________/ \______
  246. * __
  247. * SDA: \___________________
  248. *********************************************************************************************************/
  249. void SendI2CAck(void)
  250. {
  251. CLR_I2C_SCL(); //1#时钟线SCL输出低电平,开始传送应答信号
  252. SetI2CSDAAsOutput(); //将数据线SDA设置为输出
  253. CLR_I2C_SDA(); //2#数据线SDA输出低电平
  254. DelayNus(2); //延时2us
  255. SET_I2C_SCL(); //3#时钟线SCL输出高电平,在SCL上升沿前就要把SDA拉低,为应答信号
  256. DelayNus(2); //延时2us
  257. CLR_I2C_SCL(); //4#时钟线SCL输出低电平,钳住I2C总线
  258. }
  259. /*********************************************************************************************************
  260. * 函数名称: SendI2CNAck
  261. * 函数功能: 不发送应答信号
  262. * 输入参数: void
  263. * 输出参数: void
  264. * 返 回 值: void
  265. * 创建日期: 2021年07月01日
  266. * 注 意: 下面是具体的时序图
  267. * 1 2 3 4
  268. * ______
  269. * SCL: ________/ \______
  270. * __ ___________________
  271. * SDA: __/
  272. *********************************************************************************************************/
  273. void SendI2CNAck(void)
  274. {
  275. CLR_I2C_SCL(); //1#拉低时钟线,开始传送非应答信号
  276. SetI2CSDAAsOutput(); //将数据线SDA设置为输出
  277. SET_I2C_SDA(); //2#数据线SDA输出高电平
  278. DelayNus(2); //延时2us
  279. SET_I2C_SCL(); //3#时钟线SCL输出高电平,在SCL上升沿前就要把SDA拉高,为非应答信号
  280. DelayNus(2); //延时2us
  281. CLR_I2C_SCL(); //4#时钟线SCL输出低电平,钳住I2C总线
  282. }