使用Platformio平台的libopencm3开发框架来开发STM32G0,以下使用软件模拟I2C总线时序,并用它来读取GXHT30温湿度数据。
1 新建项目
在PIO的Home页面新建项目,项目名称gxht30,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
项目建立完成后在src目录下新建main.c主程序文件;
修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:
1 2 upload_protocol = cmsis-dap debug_tool = cmsis-dap
2 I2C软件模拟 2.1 文件结构 在lib目录新建 sw_i2c 文件夹,并新建如下文件:
2.2 sw_i2c_port.h 与底层IO读写相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #ifndef _SW_I2C_PORT_HEAD_H_ #define _SW_I2C_PORT_HEAD_H_ #include <libopencm3/stm32/rcc.h> #include <libopencm3/stm32/gpio.h> #define SW_I2C_SCL_CLOCK RCC_GPIOB #define SW_I2C_SCL_PORT GPIOB #define SW_I2C_SCL_PIN GPIO13 #define SW_I2C_SDA_CLOCK RCC_GPIOB #define SW_I2C_SDA_PORT GPIOB #define SW_I2C_SDA_PIN GPIO14 #define sw_i2c_scl_high() gpio_set(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN) #define sw_i2c_scl_low() gpio_clear(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN) #define sw_i2c_sda_high() gpio_set(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN) #define sw_i2c_sda_low() gpio_clear(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN) #define sw_i2c_sda_input() gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_INPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN) #define sw_i2c_sda_output() gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN) #define sw_i2c_delay() do{ \ for (int i=0; i<58; i++) { \ __asm__ volatile ("nop" ); \ } \ }while(0) static bool sw_i2c_sda_get (void ) { return (gpio_get(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN) != 0 ) ? true :false ; } static void sw_i2c_port_init () { rcc_periph_clock_enable(SW_I2C_SCL_CLOCK); rcc_periph_clock_enable(SW_I2C_SDA_CLOCK); gpio_mode_setup(SW_I2C_SCL_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SCL_PIN); gpio_mode_setup(SW_I2C_SDA_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, SW_I2C_SDA_PIN); gpio_set_output_options(SW_I2C_SCL_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, SW_I2C_SCL_PIN); gpio_set_output_options(SW_I2C_SDA_PORT, GPIO_OTYPE_OD, GPIO_OSPEED_25MHZ, SW_I2C_SDA_PIN); gpio_set(SW_I2C_SCL_PORT, SW_I2C_SCL_PIN); gpio_set(SW_I2C_SDA_PORT, SW_I2C_SDA_PIN); } #endif
i2c时序中的延时这里使用软件延时,模拟的是 100KHz的频率;
2.3 sw_i2c_private.h 实现i2c的基本时序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 #ifndef _SW_I2C_PRIVATE_HEAD_H_ #define _SW_I2C_PRIVATE_HEAD_H_ #include "sw_i2c_port.h" static void i2c_start (void ) ;static void i2c_stop (void ) ;static bool i2c_wait_ack (void ) ;static void i2c_send_ack (void ) ;static void i2c_send_nack (void ) ;static void i2c_send_byte (uint8_t data) ;static uint8_t i2c_recv_byte (bool ack) ;static void i2c_start (void ) { sw_i2c_sda_high(); sw_i2c_scl_high(); sw_i2c_delay(); sw_i2c_sda_low(); sw_i2c_delay(); sw_i2c_scl_low(); sw_i2c_delay(); } static void i2c_stop (void ) { sw_i2c_sda_low(); sw_i2c_delay(); sw_i2c_scl_high(); sw_i2c_delay(); sw_i2c_sda_high(); } static void i2c_send_byte (uint8_t data) { uint8_t i; for (i = 0 ; i < 8 ; i++) { sw_i2c_delay(); sw_i2c_scl_low(); if (data & 0x80 ) { sw_i2c_sda_high(); } else { sw_i2c_sda_low(); } sw_i2c_delay(); sw_i2c_scl_high(); data <<= 1 ; } } static bool i2c_wait_ack (void ) { bool res; sw_i2c_delay(); sw_i2c_scl_low(); sw_i2c_sda_input(); sw_i2c_delay(); sw_i2c_scl_high(); sw_i2c_delay(); if (sw_i2c_sda_get() == false ) { res = true ; } else { res = false ; } sw_i2c_scl_low(); sw_i2c_sda_high(); sw_i2c_sda_output(); sw_i2c_delay(); return res; } static void i2c_send_ack (void ) { sw_i2c_sda_low(); sw_i2c_delay(); sw_i2c_scl_high(); sw_i2c_delay(); sw_i2c_scl_low(); sw_i2c_delay(); sw_i2c_sda_high(); } static void i2c_send_nack (void ) { sw_i2c_sda_high(); sw_i2c_delay(); sw_i2c_scl_high(); sw_i2c_delay(); sw_i2c_scl_low(); sw_i2c_delay(); } static uint8_t i2c_recv_byte (bool ack) { uint8_t i; uint8_t value; value = 0 ; for (i = 0 ; i < 8 ; i++) { value <<= 1 ; sw_i2c_scl_high(); sw_i2c_delay(); if (sw_i2c_sda_get()==true ) { value++; } sw_i2c_scl_low(); sw_i2c_delay(); } if (ack) { i2c_send_ack(); } else { i2c_send_nack(); } return value; } #endif
2.4 sw_i2c 读写实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 #include "sw_i2c.h" #include "sw_i2c_port.h" #include "sw_i2c_private.h" void sw_i2c_init () { sw_i2c_port_init(); } bool sw_i2c_transfer (uint8_t dev_addr, uint8_t *tx_buffer,uint16_t tx_size,uint8_t *rx_buffer,uint16_t rx_size) { uint16_t i; if (tx_size > 0 ) { i2c_start(); i2c_send_byte(dev_addr<<1 ); if (i2c_wait_ack() == false ) { goto error_device_nack; } for (i=0 ; i<tx_size; i++) { i2c_send_byte(tx_buffer[i]); if (i2c_wait_ack() == false ) { goto error_device_nack; } } } if (rx_size > 0 ) { i2c_start(); i2c_send_byte(dev_addr<<1 | 1 ); if (i2c_wait_ack() == false ) { goto error_device_nack; } for (i=0 ; i<rx_size; i++) { rx_buffer[i] = i2c_recv_byte(i+1 <rx_size); } } i2c_stop(); return true ; error_device_nack: i2c_stop(); return false ; } void scan_i2c_bus (uint8_t from_addr, uint8_t to_addr, void (*callback)(uint8_t address, uint8_t result)) { bool rc; uint8_t dev_addr_7bit = 0 ; for ( uint8_t addr = from_addr; addr <= to_addr; addr++) { i2c_start(); i2c_send_byte(addr); if (i2c_wait_ack() == false ) { rc = false ; }else { rc = true ; } i2c_stop(); dev_addr_7bit = addr>>1 ; callback(dev_addr_7bit, rc); for (char i=0 ; i<5 ; i++){ sw_i2c_delay(); } addr++; if (addr > to_addr){ break ; } } }
实现了数据传输 transfer 接口,包含了发送和接收;
实现总线设备扫描功能,可以用于辅助调试;
3 GXHT30使用I2C 3.1 扫描设备 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void scan_i2c_cb ( uint8_t addr, uint8_t result ) { if (result == 1 ){ printf (" scan addr[7bit]: 0x%x found!\r\n" ,addr); }else { } } int main(void ){ ... printf ("init i2c bus\r\n" ); sw_i2c_init(); printf ("scan device on i2c bus...\r\n" ); scan_i2c_bus(0x02 ,0xfe , scan_i2c_cb); ... }
3.2 读取温湿度数据 根据GXHT30芯片手册实现,这里为单次读取:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void gxht30_sample (float *temp, float *humi) { uint8_t rd_buff[6 ] = {0 }; uint8_t cmd[2 ] = {0x2c , 0x06 }; uint8_t dev_addr = 0x44 ; sw_i2c_transfer(dev_addr, cmd, 2 , 0 , 0 ); delay_ms(10 ); sw_i2c_transfer(dev_addr, 0 ,0 , rd_buff, 6 ); uint16_t temp_int = (uint16_t )((rd_buff[0 ] << 8 )|(rd_buff[1 ])); uint16_t humi_int = (uint16_t )((rd_buff[3 ] << 8 )|(rd_buff[4 ])); *temp = -45 + (float )(175 *temp_int/65535.0000 ); *humi = 100 * (float )(humi_int /65535.0000 ); }
发送命令的波形也和预期一致;
4 烧写测试 4.1 连线 将开发板和温湿度模块的I2C引脚连接:
4.2 测试结果 可以看到读取到0x44的设备地址,即温湿度模块的I2C地址,温湿度读取正确: