I2c.c 14 KB

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