使用Platformio平臺的libopencm3開發(fā)框架來開發(fā)STM32G0,以下使用軟件模擬I2C總線時(shí)序,并用它來讀取GXHT30溫濕度數(shù)據(jù)。
1 新建項(xiàng)目
- 建立gxht30項(xiàng)目
在PIO的Home頁面新建項(xiàng)目,項(xiàng)目名稱gxht30,選擇開發(fā)板為 MonkeyPi_STM32_G070RB,開發(fā)框架選擇libopencm3;
1upload_protocol = cmsis-dap
2debug_tool = cmsis-dap
2 I2C軟件模擬
2.1 文件結(jié)構(gòu)
在lib目錄新建 sw_i2c 文件夾,并新建如下文件:
2.2 sw_i2c_port.h 與底層IO讀寫相關(guān)
1/**
2 * @file sw_i2c_port.h
3 * @author MakerInChina (makerinchina.cn)
4 * @brief
5 * @version 0.01
6 * @date 2022-09-25
7 *
8 * @copyright Copyright (c) 2022
9 *
10 */
11
12#ifndef _SW_I2C_PORT_HEAD_H_
13#define _SW_I2C_PORT_HEAD_H_
14
15#include 3/stm32/rcc.h>
16#include 3/stm32/gpio.h>
17
18#define SW_I2C_SCL_CLOCK RCC_GPIOB
19#define SW_I2C_SCL_PORT GPIOB
20#define SW_I2C_SCL_PIN GPIO13
21
22#define SW_I2C_SDA_CLOCK RCC_GPIOB
23#define SW_I2C_SDA_PORT GPIOB
24#define SW_I2C_SDA_PIN GPIO14
25
26#define sw_i2c_scl_high() gpio_set(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN)
27#define sw_i2c_scl_low() gpio_clear(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN)
28#define sw_i2c_sda_high() gpio_set(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN)
29#define sw_i2c_sda_low() gpio_clear(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN)
30
31#define sw_i2c_sda_input() gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN)
32#define sw_i2c_sda_output() gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN)
33
34// #define sw_i2c_delay() delay_us(5)
35#define sw_i2c_delay() do{ \\
36 for (int i=0; i<58; i++) { \\
37 __asm__ volatile ("nop"); \\
38 } \\
39 }while(0)
40static bool sw_i2c_sda_get(void)
41{
42 return (gpio_get(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN) != 0) ? true:false;
43}
44
45static void sw_i2c_port_init()
46{
47 /* 打開GPIO時(shí)鐘 */
48 rcc_periph_clock_enable(SW_I2C_SCL_CLOCK);
49 rcc_periph_clock_enable(SW_I2C_SDA_CLOCK);
50
51 /* 禁用默認(rèn)上拉,使SCL, SDA保持高阻狀態(tài), 設(shè)置為 OD 模式 */
52 gpio_mode_setup(SW_I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SCL_PIN);
53 gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN);
54 gpio_set_output_options(SW_I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, SW_I2C_SCL_PIN);
55 gpio_set_output_options(SW_I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, SW_I2C_SDA_PIN);
56
57 /* 空閑: 拉高SCL和SDA */
58 gpio_set(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN);
59 gpio_set(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN);
60}
61
62#endif //!_SW_I2C_PORT_HEAD_H_
i2c時(shí)序中的延時(shí)這里使用軟件延時(shí),模擬的是 100KHz的頻率;
2.3 sw_i2c_private.h 實(shí)現(xiàn)i2c的基本時(shí)序
1/**
2 * @file sw_i2c_private.h
3 * @author MakerInChina (makerinchina.cn)
4 * @brief
5 * @version 0.01
6 * @date 2022-09-25
7 *
8 * @copyright Copyright (c) 2022
9 *
10 */
11
12#ifndef _SW_I2C_PRIVATE_HEAD_H_
13#define _SW_I2C_PRIVATE_HEAD_H_
14
15#include "sw_i2c_port.h"
16
17static void i2c_start(void);
18static void i2c_stop(void);
19static bool i2c_wait_ack(void);
20static void i2c_send_ack(void);
21static void i2c_send_nack(void);
22static void i2c_send_byte(uint8_t data);
23static uint8_t i2c_recv_byte(bool ack);
24
25/**
26 * @brief I2C總線啟動(dòng)信號
27 */
28static void i2c_start(void)
29{
30 /* 當(dāng)SCL高電平時(shí),SDA出現(xiàn)一個(gè)下跳沿表示I2C總線啟動(dòng)信號 */
31 sw_i2c_sda_high();
32 sw_i2c_scl_high();
33 sw_i2c_delay();
34 sw_i2c_sda_low();
35 sw_i2c_delay();
36 sw_i2c_scl_low();
37 sw_i2c_delay();
38}
39
40/**
41 * @brief I2C總線停止信號
42 */
43static void i2c_stop(void)
44{
45 /* 當(dāng)SCL高電平時(shí),SDA出現(xiàn)一個(gè)上跳沿表示I2C總線停止信號 */
46 sw_i2c_sda_low();
47 sw_i2c_delay();
48 sw_i2c_scl_high();
49 sw_i2c_delay();
50 sw_i2c_sda_high();
51}
52
53/**
54 * @brief 向I2C總線設(shè)備發(fā)送1個(gè)字節(jié)
55 * @param data 等待發(fā)送的字節(jié)
56 */
57static void i2c_send_byte(uint8_t data)
58{
59 uint8_t i;
60
61 /* 先發(fā)送字節(jié)的高位bit7 */
62 for (i = 0; i < 8; i++) {
63 sw_i2c_delay();
64 sw_i2c_scl_low();
65
66 if (data & 0x80) {
67 sw_i2c_sda_high();
68 } else {
69 sw_i2c_sda_low();
70 }
71
72 sw_i2c_delay();
73 sw_i2c_scl_high();
74
75 data <<= 1; /* 左移一個(gè)bit */
76 }
77}
78
79/**
80 * @brief 產(chǎn)生一個(gè)時(shí)鐘,并讀取器件的ACK應(yīng)答信號
81 * @return 返回true表示正確應(yīng)答,false表示無器件響應(yīng)
82 */
83static bool i2c_wait_ack(void)
84{
85 bool res;
86
87 sw_i2c_delay();
88 sw_i2c_scl_low();
89
90 sw_i2c_sda_input();
91
92 sw_i2c_delay();
93 sw_i2c_scl_high(); /* 驅(qū)動(dòng)SCL = 1, 此時(shí)器件會(huì)返回ACK應(yīng)答 */
94 sw_i2c_delay();
95 if (sw_i2c_sda_get() == false) { /* 讀取SDA口線狀態(tài) */
96 res = true;
97 } else {
98 res = false;
99 }
100 sw_i2c_scl_low();
101 sw_i2c_sda_high(); /* 釋放SDA總線 */
102 sw_i2c_sda_output();
103 sw_i2c_delay();
104
105 return res;
106}
107
108/**
109 * @brief 產(chǎn)生一個(gè)ACK信號
110 */
111static void i2c_send_ack(void)
112{
113 sw_i2c_sda_low(); /* CPU驅(qū)動(dòng)SDA = 0 */
114 sw_i2c_delay();
115 sw_i2c_scl_high(); /* CPU產(chǎn)生1個(gè)時(shí)鐘 */
116 sw_i2c_delay();
117 sw_i2c_scl_low();
118 sw_i2c_delay();
119 sw_i2c_sda_high(); /* CPU釋放SDA總線 */
120}
121
122/**
123 * @brief CPU產(chǎn)生1個(gè)NACK信號
124 */
125static void i2c_send_nack(void)
126{
127 sw_i2c_sda_high(); /* CPU驅(qū)動(dòng)SDA = 1 */
128 sw_i2c_delay();
129 sw_i2c_scl_high(); /* CPU產(chǎn)生1個(gè)時(shí)鐘 */
130 sw_i2c_delay();
131 sw_i2c_scl_low();
132 sw_i2c_delay();
133}
134
135/**
136 * @brief CPU從I2C總線設(shè)備讀取8bit數(shù)據(jù)
137 * 讀1個(gè)字節(jié),ack=1時(shí),發(fā)送ACK,ack=0,發(fā)送nACK
138 * @return
139 */
140static uint8_t i2c_recv_byte(bool ack)
141{
142 uint8_t i;
143 uint8_t value;
144
145 /* 讀到第1個(gè)bit為數(shù)據(jù)的bit7 */
146 value = 0;
147 for (i = 0; i < 8; i++) {
148 value <<= 1;
149 sw_i2c_scl_high();
150 sw_i2c_delay();
151 if (sw_i2c_sda_get()==true) {
152 value++;
153 }
154 sw_i2c_scl_low();
155 sw_i2c_delay();
156 }
157
158 if (ack) {
159 i2c_send_ack(); //發(fā)送ACK
160 } else {
161 i2c_send_nack();//發(fā)送nACK
162 }
163
164 return value;
165}
166
167#endif //!_SW_I2C_PRIVATE_HEAD_H_
2.4 sw_i2c 讀寫實(shí)現(xiàn)
1/**
2 * @file sw_i2c.c
3 * @author MakerInChina (makerinchina.cn)
4 * @brief
5 * @version 0.01
6 * @date 2022-09-25
7 *
8 * @copyright Copyright (c) 2022
9 *
10 */
11
12#include "sw_i2c.h"
13#include "sw_i2c_port.h"
14#include "sw_i2c_private.h"
15
16void sw_i2c_init()
17{
18 sw_i2c_port_init();
19}
20
21/* Function to setup and execute I2C transfer request */
22bool sw_i2c_transfer(uint8_t dev_addr, uint8_t *tx_buffer,uint16_t tx_size,uint8_t *rx_buffer,uint16_t rx_size)
23{
24 uint16_t i;
25
26 if (tx_size > 0) {
27 /* start */
28 i2c_start();
29 /* address + write */
30 i2c_send_byte(dev_addr<<1);
31 if (i2c_wait_ack() == false) {
32 goto error_device_nack;
33 }
34 /* write data */
35 for (i=0; i36 i2c_send_byte(tx_buffer[i]);
37 if (i2c_wait_ack() == false) {
38 goto error_device_nack;
39 }
40 }
41 }
42 if (rx_size > 0) {
43 /* start */
44 i2c_start();
45 /* address + read */
46 i2c_send_byte(dev_addr<<1 | 1);
47 if (i2c_wait_ack() == false) {
48 goto error_device_nack;
49 }
50 /* read data */
51 for (i=0; i52 rx_buffer[i] = i2c_recv_byte(i+153 }
54 }
55 i2c_stop();
56 return true;
57
58error_device_nack:
59 i2c_stop();
60 return false;
61}
62
63// Scan the I2C bus between addresses from_addr and to_addr.
64// On each address, call the callback function with the address and result.
65// If result==0, address was found, otherwise, address wasn't found
66// (can use result to potentially get other status on the I2C bus, see twi.c)
67// Assumes Wire.begin() has already been called
68void scan_i2c_bus(uint8_t from_addr, uint8_t to_addr, void(*callback)(uint8_t address, uint8_t result))
69{
70 bool rc;
71 uint8_t dev_addr_7bit = 0;
72
73 for( uint8_t addr = from_addr; addr <= to_addr; addr++) {
74
75 /* start */
76 i2c_start();
77
78 /* address + write */
79 i2c_send_byte(addr);
80
81 if (i2c_wait_ack() == false) {
82 rc = false;
83 }else{
84 rc = true;
85 }
86
87 i2c_stop();
88
89 dev_addr_7bit = addr>>1;
90
91 callback(dev_addr_7bit, rc);
92
93 //dealy for sometime, 5 clk
94 for(char i=0; i<5; i++){
95 sw_i2c_delay();
96 }
97
98 //ignore add+1, read
99
100 addr++;
101 if(addr > to_addr){
102 break;
103 }
104
105 }
106}
- 實(shí)現(xiàn)了數(shù)據(jù)傳輸 transfer 接口,包含了發(fā)送和接收;
- 實(shí)現(xiàn)總線設(shè)備掃描功能,可以用于輔助調(diào)試;
3 GXHT30使用I2C
3.1 掃描設(shè)備
1void scan_i2c_cb( uint8_t addr, uint8_t result )
2{
3
4 if(result == 1){
5 printf(" scan addr[7bit]: 0x%x found!\\r\\n",addr);
6 }else{
7 // printf("scan addr: %x not found\\r\\n",addr); //not found
8 }
9
10}
11
12int main(void)
13{
14 ...
15
16 printf("init i2c bus\\r\\n");
17
18 sw_i2c_init();
19
20 printf("scan device on i2c bus...\\r\\n");
21
22 scan_i2c_bus(0x02,0xfe, scan_i2c_cb);
23
24 ...
25}
3.2 讀取溫濕度數(shù)據(jù)
根據(jù)GXHT30芯片手冊實(shí)現(xiàn),這里為單次讀取:
1void gxht30_sample(float *temp, float *humi)
2{
3 uint8_t rd_buff[6] = {0};
4 uint8_t cmd[2] = {0x2c, 0x06};
5
6 uint8_t dev_addr = 0x44;
7
8 //send read cmd
9 sw_i2c_transfer(dev_addr, cmd, 2, 0, 0);
10
11 delay_ms(10);
12
13 //receive data
14 sw_i2c_transfer(dev_addr, 0,0, rd_buff, 6);
15
16 uint16_t temp_int = (uint16_t)((rd_buff[0] << 8)|(rd_buff[1]));
17 uint16_t humi_int = (uint16_t)((rd_buff[3] << 8)|(rd_buff[4]));
18
19 *temp = -45 + (float)(175*temp_int/65535.0000);
20 *humi = 100 * (float)(humi_int /65535.0000);
21}
發(fā)送命令的波形也和預(yù)期一致;
- 發(fā)送讀取命令 0x2c 0x06 的波形:
- 接收數(shù)據(jù)的波形,溫度+CRC+濕度+CRC:
4 燒寫測試
4.1 連線
將開發(fā)板和溫濕度模塊的I2C引腳連接:
4.2 測試結(jié)果
可以看到讀取到0x44的設(shè)備地址,即溫濕度模塊的I2C地址,溫濕度讀取正確:
-
接口
+關(guān)注
關(guān)注
33文章
8447瀏覽量
150723 -
STM32
+關(guān)注
關(guān)注
2264文章
10854瀏覽量
354299 -
I2C
+關(guān)注
關(guān)注
28文章
1477瀏覽量
123058 -
I2C總線
+關(guān)注
關(guān)注
8文章
386瀏覽量
60791 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
4896瀏覽量
97059
發(fā)布評論請先 登錄
相關(guān)推薦
評論