STM32G0开发笔记:I2C接口软件模拟与GXHT30温湿度模块

发布时间:
来源: 电子工程世界

使用Platformio平台的libopencm3开发框架来开发STM32G0,以下使用软件模拟I2C总线时序,并用它来读取GXHT30温湿度数据。


1 新建项目

建立gxht30项目

在PIO的Home页面新建项目,项目名称gxht30,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;


项目建立完成后在src目录下新建main.c主程序文件;

修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:

1upload_protocol = cmsis-dap

2debug_tool = cmsis-dap


2 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 */

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时钟 */

48    rcc_periph_clock_enable(SW_I2C_SCL_CLOCK);

49    rcc_periph_clock_enable(SW_I2C_SDA_CLOCK);

50

51    /* 禁用默认上拉,使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);

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时序中的延时这里使用软件延时,模拟的是 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();

104

105    return res;

106}

107

108/**

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}

121

122/**

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}

134

135/**

136 * @brief CPU从I2C总线设备读取8bit数据

137 * 读1个字节,ack=1时,发送ACK,ack=0,发送nACK

138 * @return

139 */

140static uint8_t i2c_recv_byte(bool ack)

141{

142    uint8_t i;

143    uint8_t value;

144

145    /* 读到第1个bit为数据的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(); //发送ACK

160    } else {

161        i2c_send_nack();//发送nACK

162    }

163

164    return value;

165}

166

167#endif //!_SW_I2C_PRIVATE_HEAD_H_

2.4 sw_i2c 读写实现

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        }

文章来源于: 电子工程世界 原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。