当前位置:   article > 正文

ARM_day8:温湿度数据采集应用_主机发送起始信号

主机发送起始信号

1、IIC通信过程

主机发送起始信号、主机发送8位(7位从机地址+1位传送方向(0W,1R))、从机应答、发数据、应答、数据传输完,主机发送停止信号

2、起始信号和终止信号

SCL时钟线,SDA数据线

SCL高电平,SDA由高到低——起始信号

SCL高电平,SDA由低到高——终止信号

均由主机发出

3、应答信号和非应答信号

8位数据+1位应答位——一帧9位

8位数据传输完,第九个时钟周期,数据线(SDA)低电平,接收方还想接收数据,回复应答信号

                                                       数据线(SDA)高电平,接收方不想接收数据,回复非应信号

4、数据传输时机

时钟信号(SCL)高电平,数据线(SDA)稳定时,读取

时钟信号(SCL)低电平,数据线(SDA)高电平或低电平,写入

5、IIC从机选择及读写选择

传输信号包括地址信号、数据信号

起始信号后必须跟一个8位数据(7位从机地址+1位传送方向位(R/W))(0——W、1——R)

6、IIC读写时序

主机向从机发送:

主机发送起始信号、主机发送8位(7位从机地址+1位写标志)、从机应答、主机发送8位从机寄存器地址、从机应答、主机发送8位数据、从机应答、主机发起终止信号

主机读取从机数据:

主机发送起始信号、主机发送8位(7位从机地址+1位写标志)、从机应答、主机发起重复起始信号、主机发送8位(7位从机地址+1位读标志)、从机应答、从机发送8位数据、主机非应答信号、主机发起终止信号

实验:I2C读取温湿度传感器数据

1、SCL时钟线——PF14,SDA数据线——PF15        引脚连接

        I2C总线引脚初始化:(PF14、PF15共同初始化)使能GPIOF外设时钟、设输出功能、推挽输出、输出速度、上拉下拉、空闲状态下的SCL和SDA状态(拉高)——起始信号要求SCL高,SDA从高到低

2、模拟I2C开始信号时序

        SDA数据线保持输出状态  PF15输出;(PF15管脚设为输出)

        空闲状态SCL、SDA拉高(起始信号要求SCL高,SDA从高到低);(选用ODR(设置)、BSRR(复位)、BRR(清空)使输出高低电平)

        延迟一段时间,保持稳定后,拉低SDA数据线完成起始信号;

        延迟一段时间,拉低SCL时钟线,才能进行写入数据;

3、主机向从机写数据(高位到低位)

        SDA数据线保持输出状态  PF15输出;(PF15管脚设为输出)

        时钟线拉低,才能写数据;

        循环发送数据8位(0-7),延迟时间,保持时钟线稳定,开始发送数据;(要发送的数据 dat & 0x80(10000000),判断其真假,真则拉高数据线为高电平1,否则拉低数据线为低电平0)

        每次发送一位数据,延时后拉高时钟线(SCL),接收器才能读数据;

        延时等待接收器接收数据,再延时后将数据左移一位再&0x80进行循环

4、计算温湿度

        湿度:hem=125*测量值/65536-6

        温度:tem=175.72*测量值/65536-46.85

程序代码:

main.c:

  1. #include "si7006.h"
  2. extern void printf(const char* fmt, ...);
  3. int main()
  4. {
  5. //si7006初始化
  6. si7006_init();
  7. unsigned short hum;
  8. short tem;
  9. while(1)
  10. {
  11. //读取温湿度
  12. hum=si70006_read_hum();
  13. tem=si70006_read_tem();
  14. //计算温湿度数据
  15. hum=hum*125/65536-6;
  16. tem=tem*175.72/65536-46.85;
  17. printf("hum:%d\n",hum);
  18. printf("tem:%d\n",tem);
  19. delay_ms(1000);
  20. //湿度大于65开启马达
  21. if(hum>65)
  22. {
  23. GPIOF->ODR |= (0x1<<6);
  24. }
  25. else if(hum<=60) //湿度小于60关闭马达
  26. {
  27. GPIOF->ODR &= (~(0x1<<6));
  28. }
  29. //温度大于25开启风扇
  30. if(tem>=25)
  31. {
  32. GPIOE->ODR |= (0x1<<9);
  33. }
  34. else if(tem<25) //湿度小于25关闭风扇
  35. {
  36. GPIOE->ODR &= (~(0x1<<9));
  37. }
  38. }
  39. return 0;
  40. }

si7006.h:

  1. #ifndef __SI7006_H__
  2. #define __SI7006_H__
  3. #include "iic.h"
  4. void delay_ms(int ms);
  5. void si7006_init();
  6. unsigned short si70006_read_hum();
  7. short si70006_read_tem();
  8. #endif

si7006.c:

  1. #include "si7006.h"
  2. extern void printf(const char* fmt, ...);
  3. void delay_ms(int ms)
  4. {
  5. int i,j;
  6. for(i=0;i<ms;i++)
  7. {
  8. for(j=0;j<2000;j++)
  9. {
  10. }
  11. }
  12. }
  13. void si7006_init()
  14. {
  15. //发起起始信号
  16. i2c_init();//I2C总线引脚初始化
  17. i2c_start();//模拟i2c开始信号的时序
  18. //发送7bit从机地址和写标志位 0x80
  19. i2c_write_byte(0x40<<1|0);//主机向从机写8bit数据
  20. //等待从机应答
  21. i2c_wait_ack();//等待接收器应答
  22. //发送寄存器地址 0XE6
  23. i2c_write_byte(0xE6);
  24. //等待从机应答
  25. i2c_wait_ack();//等待接收器应答
  26. //向从机发送数据 0x3A
  27. i2c_write_byte(0x3A);
  28. //等待从机应答
  29. i2c_wait_ack();
  30. //发送终止信号
  31. i2c_stop();
  32. }
  33. unsigned short si70006_read_hum()//湿度读取
  34. {
  35. unsigned char hum_l,hum_h;
  36. unsigned short hum;
  37. //主机发送起始信号
  38. i2c_init();
  39. i2c_start();
  40. //主机发送7bit从机地址+1bit写标志
  41. i2c_write_byte(0x40<<1|0);
  42. //等待从机应答
  43. i2c_wait_ack();
  44. //主机发送8bit寄存器地址
  45. i2c_write_byte(0xE5);
  46. //等待从机应答
  47. i2c_wait_ack();
  48. //主机发起重复起始信号
  49. i2c_start();
  50. //主机发送7bit从机地址+1bit 读 0x81
  51. i2c_write_byte(0x40<<1|1);
  52. //等待从机应答
  53. i2c_wait_ack();
  54. //延时等待从机测量数据
  55. delay_ms(100);
  56. //读取湿度的高8bit数据 hum_h
  57. //发送应答信号
  58. hum_h=i2c_read_byte(0);
  59. //读取湿度的低8位数据 hum_l
  60. //发送非应答信号
  61. hum_l=i2c_read_byte(1);
  62. //发送终止信号
  63. //将读取到的数据的低8位和高8位合成一个完整数据
  64. hum=hum_h<<8 | hum_l;
  65. return hum;
  66. }
  67. short si70006_read_tem()//温度读取
  68. {
  69. unsigned char tem_l,tem_h;
  70. unsigned short tem;
  71. //主机发送起始信号
  72. i2c_init();
  73. i2c_start();
  74. //主机发送7bit从机地址+1bit写标志
  75. i2c_write_byte(0x40<<1|0);
  76. //等待从机应答
  77. i2c_wait_ack();
  78. //主机发送8bit寄存器地址
  79. i2c_write_byte(0xE3);
  80. //等待从机应答
  81. i2c_wait_ack();
  82. //主机发起重复起始信号
  83. i2c_start();
  84. //主机发送7bit从机地址+1bit 读 0x81
  85. i2c_write_byte(0x40<<1|1);
  86. //等待从机应答
  87. i2c_wait_ack();
  88. //延时等待从机测量数据
  89. delay_ms(100);
  90. //读取温度的高8bit数据 tem_h
  91. //发送应答信号
  92. tem_h=i2c_read_byte(0);
  93. //读取温度的低8位数据 tem_l
  94. //发送非应答信号
  95. tem_l=i2c_read_byte(1);
  96. //发送终止信号
  97. //将读取到的数据的低8位和高8位合成一个完整数据
  98. tem=tem_h<<8 | tem_l;
  99. return tem;
  100. }

iic.h:

  1. #ifndef __IIC_H__
  2. #define __IIC_H__
  3. #include "stm32mp1xx_gpio.h"
  4. #include "stm32mp1xx_rcc.h"
  5. /* 通过程序模拟实现I2C总线的时序和协议
  6. * GPIOF ---> AHB4
  7. * I2C1_SCL ---> PF14
  8. * I2C1_SDA ---> PF15
  9. *
  10. * */
  11. #define SET_SDA_OUT do{GPIOF->MODER &= (~(0x3 << 30)); \
  12. GPIOF->MODER |= (0x1 << 30);}while(0)
  13. #define SET_SDA_IN do{GPIOF->MODER &= (~(0x3 << 30));}while(0)
  14. #define I2C_SCL_H do{GPIOF->BSRR |= (0x1 << 14);}while(0)
  15. #define I2C_SCL_L do{GPIOF->BRR |= (0x1 << 14);}while(0)
  16. #define I2C_SDA_H do{GPIOF->BSRR |= (0x1 << 15);}while(0)
  17. #define I2C_SDA_L do{GPIOF->BRR |= (0x1 << 15);}while(0)
  18. #define I2C_SDA_READ (GPIOF->IDR & (0x1 << 15))
  19. void delay_us(void);//微秒延时
  20. void delay(int ms);
  21. void i2c_init(void);//初始化
  22. void i2c_start(void);//起始信号
  23. void i2c_stop(void);//终止信号
  24. void i2c_write_byte(unsigned char dat);//写一个字节数据
  25. unsigned char i2c_read_byte(unsigned char ack);//读取一个字节数据
  26. unsigned char i2c_wait_ack(void); //等待应答信号
  27. void i2c_ack(void);//发送应答信号
  28. void i2c_nack(void);//发送非应答信号
  29. #endif

iic.c:

  1. #include "iic.h"
  2. extern void printf(const char* fmt, ...);
  3. /*
  4. * 函数名 : delay_us
  5. * 函数功能:延时函数
  6. * 函数参数:无
  7. * 函数返回值:无
  8. * */
  9. void delay_us(void) //微秒级延时
  10. {
  11. unsigned int i = 2000;
  12. while(i--);
  13. }
  14. /*
  15. * 函数名 : i2c_init
  16. * 函数功能: i2C总线引脚的初始化, 通用输出,推挽输出,输出速度,
  17. * 函数参数:无
  18. * 函数返回值:无
  19. * */
  20. void i2c_init(void)
  21. {
  22. // 使能GPIOF端口的时钟
  23. RCC->MP_AHB4ENSETR |= (0x1 << 5);
  24. //使能风扇的时钟
  25. RCC->MP_APB2ENSETR |= 0x1;
  26. //使能马达的时钟
  27. RCC->MP_APB2ENSETR |= (0x1<<3);
  28. // 设置PF14,PF15引脚为通用的输出功能
  29. GPIOF->MODER &= (~(0xF << 28));
  30. GPIOF->MODER |= (0x5 << 28);
  31. //设置PE9为输出
  32. GPIOE->MODER &= (~(0x3<<18));
  33. GPIOE->MODER |= (0x1<<18);
  34. //设置PF6为输出
  35. GPIOF->MODER &= (~(0x3<<12));
  36. GPIOF->MODER |= (0x1<<12);
  37. // 设置PF14, PF15引脚为推挽输出
  38. GPIOF->OTYPER &= (~(0x3 << 14));
  39. //设置PE9为推挽输出
  40. GPIOE->OTYPER &= (~(0x1<<9));
  41. //设置PF6为推挽输出
  42. GPIOF->OTYPER &= (~(0x1<<6));
  43. // 设置PF14, PF15引脚为高速输出
  44. GPIOF->OSPEEDR |= (0xF << 28);
  45. //设置PE9为低速输出
  46. GPIOE->OSPEEDR &= (~(0x3<<18));
  47. //设置PF6为低速输出
  48. GPIOF->OSPEEDR &= (~(0x3<<12));
  49. // 设置PF14, PF15引脚的禁止上拉和下拉
  50. GPIOF->PUPDR &= (~(0xF << 28));
  51. //设置PE9没有上拉下拉电阻
  52. GPIOE->PUPDR &= (~(0x3<<18));
  53. //设置PF6没有上拉下拉电阻
  54. GPIOF->PUPDR &= (~(0x3<<12));
  55. // 空闲状态SDA和SCL拉高
  56. I2C_SCL_H;
  57. I2C_SDA_H;
  58. }
  59. /*
  60. * 函数名:i2c_start
  61. * 函数功能:模拟i2c开始信号的时序
  62. * 函数参数:无
  63. * 函数返回值:无
  64. * */
  65. void i2c_start(void)
  66. {
  67. /*
  68. * 开始信号:时钟在高电平期间,数据线从高到低的变化
  69. * --------
  70. * SCL \
  71. * --------
  72. * ----
  73. * SDA \
  74. * --------
  75. * */
  76. //确保SDA是输出状态 PF15输出
  77. SET_SDA_OUT;
  78. // 空闲状态SDA和SCL拉高
  79. I2C_SCL_H;
  80. I2C_SDA_H;
  81. delay_us();//延时等待一段时间
  82. I2C_SDA_L;//数据线拉低
  83. delay_us();//延时等待一段时间
  84. I2C_SCL_L;//时钟线拉低,让总线处于占用状态
  85. }
  86. /*
  87. * 函数名:i2c_stop
  88. * 函数功能:模拟i2c停止信号的时序
  89. * 函数参数:无
  90. * 函数返回值:无
  91. * */
  92. void i2c_stop(void)
  93. {
  94. /*
  95. * 停止信号 : 时钟在高电平期间,数据线从低到高的变化
  96. * ----------
  97. * SCL /
  98. * --------
  99. * --- -------
  100. * SDA X /
  101. * --- -------
  102. * */
  103. //确保SDA是输出状态 PF15输出
  104. SET_SDA_OUT;
  105. //时钟线拉低
  106. I2C_SCL_L;//为了修改数据线的电平
  107. delay_us();//延时等待一段时间
  108. I2C_SDA_L;//数据线拉低
  109. delay_us();//延时等待一段时间
  110. //时钟线拉高
  111. I2C_SCL_H;
  112. delay_us();//延时等待一段时间
  113. I2C_SDA_H;//数据线拉高
  114. }
  115. /*
  116. * 函数名: i2c_write_byte
  117. * 函数功能:主机向i2c总线上的从设备写8bits数据
  118. * 函数参数:dat : 等待发送的字节数据
  119. * 函数返回值: 无
  120. * */
  121. void i2c_write_byte(unsigned char dat)
  122. {
  123. /*
  124. * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
  125. * 时钟在高电平期间,接收器从数据线上读取数据
  126. * ---- --------
  127. * SCL \ / \
  128. * -------- --------
  129. * -------- ------------------ ---
  130. * SDA X X
  131. * -------- ------------------ ---
  132. *
  133. * 先发送高位在发送低位
  134. * */
  135. //确保SDA是输出状态 PF15输出
  136. SET_SDA_OUT;
  137. unsigned int i;
  138. for(i=0;i<8;i++)
  139. {
  140. //时钟线拉低
  141. I2C_SCL_L;
  142. delay_us();//延时
  143. //0X3A->0011 1010 0X80->10000000
  144. if(dat&0X80)//最高位为1
  145. {
  146. //发送1
  147. I2C_SDA_H;
  148. }
  149. else //最高位为0
  150. {
  151. I2C_SDA_L;//发送0
  152. }
  153. delay_us();//延时
  154. //时钟线拉高,接收器接收
  155. I2C_SCL_H;
  156. delay_us();//延时,用于等待接收器接收数据
  157. delay_us();//延时
  158. //将数据左移一位,让原来第6位变为第7位
  159. dat = dat<<1;
  160. }
  161. }
  162. /*
  163. * 函数名:i2c_read_byte
  164. * 函数功能: 主机从i2c总线上的从设备读8bits数据,
  165. * 主机发送一个应答或者非应答信号
  166. * 函数参数: 0 : 应答信号 1 : 非应答信号
  167. * 函数返回值:读到的有效数据
  168. *
  169. * */
  170. unsigned char i2c_read_byte(unsigned char ack)
  171. {
  172. /*
  173. * 数据信号:时钟在低电平期间,发送器向数据线上写入数据
  174. * 时钟在高电平期间,接收器从数据线上读取数据
  175. * ---- --------
  176. * SCL \ / \
  177. * -------- --------
  178. * -------- ------------------ ---
  179. * SDA X X
  180. * -------- ------------------ ---
  181. *
  182. * 先接收高位, 在接收低位
  183. * */
  184. unsigned int i;
  185. unsigned char dat;//保存接受的数据
  186. //将数据线设置为输入
  187. SET_SDA_IN;
  188. for(i=0;i<8;i++)
  189. {
  190. //先把时钟线拉低,等一段时间,保证发送器发送完毕数据
  191. I2C_SCL_L;
  192. delay_us();
  193. delay_us();//保证发送器发送完数据
  194. //时钟线拉高,读取数据
  195. I2C_SCL_H;
  196. delay_us();
  197. dat=dat<<1;//数值左移 一定要先左移在赋值,不然数据会溢出
  198. if(I2C_SDA_READ)//pf15管脚得到了一个高电平输入
  199. {
  200. dat |=1; //0000 0110
  201. }
  202. else
  203. {
  204. dat &=(~0X1);
  205. }
  206. delay_us();
  207. }
  208. if(ack)
  209. {
  210. i2c_nack();//发送非应答信号,不再接收下一次数据
  211. }
  212. else
  213. {
  214. i2c_ack();//发送应答信号
  215. }
  216. return dat;//将读取到的数据返回
  217. }
  218. /*
  219. * 函数名: i2c_wait_ack
  220. * 函数功能: 主机作为发送器时,等待接收器返回的应答信号
  221. * 函数参数:无
  222. * 函数返回值:
  223. * 0:接收到的应答信号
  224. * 1:接收到的非应答信号
  225. * */
  226. unsigned char i2c_wait_ack(void)
  227. {
  228. /*
  229. * 主机发送一个字节之后,从机给主机返回一个应答信号
  230. *
  231. * -----------
  232. * SCL / M:读 \
  233. * ------------- --------
  234. * --- ---- --------------------
  235. * SDA X X
  236. * --- --------------------
  237. * 主 释 从机 主机
  238. * 机 放 向数据 读数据线
  239. * 总 线写 上的数据
  240. * 线 数据
  241. * */
  242. //时钟线拉低,接收器可以发送信号
  243. I2C_SCL_L;
  244. I2C_SDA_H;//先把数据线拉高,当接收器回应应答信号时,数据线会拉低
  245. delay_us();
  246. SET_SDA_IN;//设置数据线为输入
  247. delay_us();//等待从机响应
  248. delay_us();
  249. I2C_SCL_H;//用于读取数据线数据
  250. if(I2C_SDA_READ)//PF15得到一个高电平输入,收到非应答信号
  251. return 1;
  252. I2C_SCL_L;//时钟线拉低,让数据线处于占用状态
  253. return 0;
  254. }
  255. /*
  256. * 函数名: iic_ack
  257. * 函数功能: 主机作为接收器时,给发送器发送应答信号
  258. * 函数参数:无
  259. * 函数返回值:无
  260. * */
  261. void i2c_ack(void)
  262. {
  263. /* --------
  264. * SCL / \
  265. * ------- ------
  266. * ---
  267. * SDA X
  268. * --- -------------
  269. * */
  270. //保证数据线是输出
  271. SET_SDA_OUT;
  272. I2C_SCL_L;//拉低时钟线
  273. delay_us();
  274. I2C_SDA_L;//数据线拉低,表示应答信号
  275. delay_us();
  276. I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号
  277. delay_us();//让从机读取我们当前的回应
  278. delay_us();
  279. I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据
  280. }
  281. /*
  282. * 函数名: iic_nack
  283. * 函数功能: 主机作为接收器时,给发送器发送非应答信号
  284. * 函数参数:无
  285. * 函数返回值:无
  286. * */
  287. void i2c_nack(void)
  288. {
  289. /* --------
  290. * SCL / \
  291. * ------- ------
  292. * --- ---------------
  293. * SDA X
  294. * ---
  295. * */
  296. //保证数据线是输出
  297. SET_SDA_OUT;
  298. I2C_SCL_L;//拉低时钟线
  299. delay_us();
  300. I2C_SDA_H;//数据线拉高,表示非应答信号
  301. delay_us();
  302. I2C_SCL_H;//时钟线拉高,等待发送器读取应答信号
  303. delay_us();
  304. delay_us();
  305. I2C_SCL_L;//数据线处于占用状态,发送器发送下一次数据
  306. }

运行结果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/609546
推荐阅读
相关标签
  

闽ICP备14008679号