I2c.c 14 KB

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