使用platformio平台的libopencm3开发框架来开发stm32g0,以下使用软件模拟i2c总线时序,并用它来读取gxht30温湿度数据。
1 新建项目建立gxht30项目在pio的home页面新建项目,项目名称gxht30,选择开发板为 monkeypi_stm32_g070rb,开发框架选择libopencm3;
项目建立完成后在src目录下新建main.c主程序文件;修改下载和调试方式,这里开发板使用的是daplink仿真器,因此修改platformio.ini文件如下:1upload_protocol = cmsis-dap2debug_tool = cmsis-dap2 i2c软件模拟2.1 文件结构在lib目录新建 sw_i2c 文件夹,并新建如下文件:
2.2 sw_i2c_port.h 与底层io读写相关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 */1112#ifndef _sw_i2c_port_head_h_13#define _sw_i2c_port_head_h_1415#include 16#include 1718#define sw_i2c_scl_clock rcc_gpiob19#define sw_i2c_scl_port gpiob20#define sw_i2c_scl_pin gpio132122#define sw_i2c_sda_clock rcc_gpiob23#define sw_i2c_sda_port gpiob24#define sw_i2c_sda_pin gpio142526#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)3031#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)3334// #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} 4445static void sw_i2c_port_init()46{47 /* 打开gpio时钟 */48 rcc_periph_clock_enable(sw_i2c_scl_clock);49 rcc_periph_clock_enable(sw_i2c_sda_clock);5051 /* 禁用默认上拉,使scl, sda保持高阻状态, 设置为 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);5657 /* 空闲: 拉高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}6162#endif //!_sw_i2c_port_head_h_i2c时序中的延时这里使用软件延时,模拟的是 100khz的频率;
2.3 sw_i2c_private.h 实现i2c的基本时序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总线启动信号 27 */ 28static void i2c_start(void) 29{ 30 /* 当scl高电平时,sda出现一个下跳沿表示i2c总线启动信号 */ 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 /* 当scl高电平时,sda出现一个上跳沿表示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总线设备发送1个字节 55 * @param data 等待发送的字节 56 */ 57static void i2c_send_byte(uint8_t data) 58{ 59 uint8_t i; 60 61 /* 先发送字节的高位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; /* 左移一个bit */ 76 } 77} 78 79/** 80 * @brief 产生一个时钟,并读取器件的ack应答信号 81 * @return 返回true表示正确应答,false表示无器件响应 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(); /* 驱动scl = 1, 此时器件会返回ack应答 */ 94 sw_i2c_delay(); 95 if (sw_i2c_sda_get() == false) { /* 读取sda口线状态 */ 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();104105 return res;106}107108/**109 * @brief 产生一个ack信号110 */111static void i2c_send_ack(void)112{113 sw_i2c_sda_low(); /* cpu驱动sda = 0 */114 sw_i2c_delay();115 sw_i2c_scl_high(); /* cpu产生1个时钟 */116 sw_i2c_delay();117 sw_i2c_scl_low();118 sw_i2c_delay();119 sw_i2c_sda_high(); /* cpu释放sda总线 */120}121122/**123 * @brief cpu产生1个nack信号124 */125static void i2c_send_nack(void)126{127 sw_i2c_sda_high(); /* cpu驱动sda = 1 */128 sw_i2c_delay();129 sw_i2c_scl_high(); /* cpu产生1个时钟 */130 sw_i2c_delay();131 sw_i2c_scl_low();132 sw_i2c_delay();133}134135/**136 * @brief cpu从i2c总线设备读取8bit数据137 * 读1个字节,ack=1时,发送ack,ack=0,发送nack138 * @return139 */140static uint8_t i2c_recv_byte(bool ack)141{142 uint8_t i;143 uint8_t value;144145 /* 读到第1个bit为数据的bit7 */146 value = 0;147 for (i = 0; i < 8; i++) {148 value < 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; i 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; i
1; 90 91 callback(dev_addr_7bit, rc); 92 93 //dealy for sometime, 5 clk 94 for(char i=0; i to_addr){102 break;103 }104105 }106}实现了数据传输 transfer 接口,包含了发送和接收;实现总线设备扫描功能,可以用于辅助调试;3 gxht30使用i2c3.1 扫描设备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 } 910}1112int main(void)13{14 ...1516 printf(init i2c bus\\r\\n);1718 sw_i2c_init();1920 printf(scan device on i2c bus...\\r\\n);2122 scan_i2c_bus(0x02,0xfe, scan_i2c_cb);2324 ...25}3.2 读取温湿度数据根据gxht30芯片手册实现,这里为单次读取:
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);1011 delay_ms(10);1213 //receive data14 sw_i2c_transfer(dev_addr, 0,0, rd_buff, 6);1516 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]));1819 *temp = -45 + (float)(175*temp_int/65535.0000);20 *humi = 100 * (float)(humi_int /65535.0000);21}发送命令的波形也和预期一致;
发送读取命令 0x2c 0x06 的波形:
接收数据的波形,温度+crc+湿度+crc:
4 烧写测试4.1 连线将开发板和温湿度模块的i2c引脚连接:
4.2 测试结果可以看到读取到0x44的设备地址,即温湿度模块的i2c地址,温湿度读取正确:
钎焊是什么焊接_钎焊工艺方法
亿图AI × 学习,终身学习的效率加速器
8位MTP CMOS触摸单片机YS65F805简介
格灵深瞳致力于利用先进技术提供创新的解决方案和产品赋能各行各业
华为Mate10配置售价全曝光!它居然是四摄,最高有8G运存
STM32G0开发笔记:I2C接口软件模拟与GXHT30温湿度模块
PCB环境下的光标捕获系统使用方法解析
大功率LED调光器该如何使用
插拔力试验机有哪些样式?立式、旋转、电脑式
MPI转以太网连接Labview监控300PLC数据
加速布局无线市场,助力物联网产业创新发展
LCD术语
工业机器人在锻造自动化生产线中的应用
如何使用电容器来降低DC-DC转换器的噪声
OPPO 开发者大会 虚拟人“觉醒”年 见证一场万物的进化
企业的数据中台到底指的是什么?
OPPO在印度正式发布了OPPO Reno 2系列三款新机
精耕细作,打造2019中国南部最具影响力表面处理行业盛会
三星为智能座舱SoC换上AMD的GPU
zigbee_蓝牙_wifi的比较与区别分析