ESP32C3学习&开发之路——GPIO模拟IIC驱动mpu6500


前言

在“https://blog.csdn.net/RMDYBW/article/details/131761502?spm=1001.2014.3001.5501”的博文中详细的介绍了IIC协议,并使用ESP32C3模组自带的IIC外设驱动了mpu6500。本博文将介绍不使用ESP32C3的IIC外设,而是通过GPIO来模拟IIC协议,就像以前使用STM32F103x系列MCU时那样将GPIO模拟成IIC的标准协议来驱动IIC器件,并记录我的开发过程。


一、配置GPIO

IIC协议需要用到2个GPIO引脚,一个是时钟引脚SCL,一个是数据收发引脚SDA。SCL引脚需要输出时钟信号;SDA引脚需要读写数据。

1.GPIO方向设置

SDA引脚因为需要收发数据所以会存在GPIO方向的设置,在发送数据时需要将引脚设置成“GPIO_MODE_OUTPUT”模式,在读取数据时需要将引脚设置成“GPIO_MODE_INPUT”模式。
在ESP32C3的源码中有实现GPIO方向设置的API,该API为esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode)在头文件#include "driver/gpio.h"中,官方的编程指南介绍如下图:
在这里插入图片描述
这里了进行封装一下,方便后面调用,如下图:
在这里插入图片描述

2.GPIO输出高低电平

因为ESP32C3没有位带功能,所以无法直接配置别名操作GPIO输出高低电平(如led = 1;led= 0)。在之前对GPIO的学习中知道,要改变GPIO的输出电平那可以对寄存器(GPIO_OUT_W1TS_REG、GPIO_OUT_W1TC_REG)的相应位置位即可,所以还可以通过操作寄存器来实现相对好用的GPIO控制,如下图:
在这里插入图片描述
直接操作指定GPIO引脚的寄存器实现高低电平的输出,再借用三目运算‘:’、‘?’来选择输出电平的高低,这样就实现了低配的别名操作。
volatile是不让编译器进行优化,并每次都是取地址上的数据。

3.GPIO读取输入电平

SDA引脚需要读取从机发送的数据,ESP32C3的源码中有读取GPIO输入电平的API,该API为int gpio_get_level(gpio_num_t gpio_num)在头文件#include "driver/gpio.h"中,官方的编程指南介绍如下图:
在这里插入图片描述
也对这个API进行封装一下,如下图,是不是有熟悉的味道!!!
在这里插入图片描述

4.GPIO相关宏定义

在这里插入图片描述

二、实现IIC协议

1.起始/停止信号

ESP32C3官方微秒级延时API:ets_delay_us(uint32_t us),要包含头文件"esp32c3/rom/ets_sys.h"

/*****
 * @Edit 2023.7
 * @brief 产生IIC起始信号
 * @param[in] Null
 * @return Null
******/
void iic_start(void)
{
	SDA_OUT();     //sda线输出
	SCL(0);
	ets_delay_us(1);
	SDA(1);
	ets_delay_us(1);	
	SCL(1);
	ets_delay_us(4);
 	SDA(0);//START:when CLK is high,DATA change form high to low 
	ets_delay_us(4);
	SCL(0);//钳住I2C总线,准备发送或接收数据 
}	  

/*****
 * @Edit 2023.7
 * @brief 产生IIC停止信号
 * @param[in] Null
 * @return Null
******/
void iic_stop(void)
{
	SDA_OUT();//sda线输出
	SCL(0);
	ets_delay_us(1);
	SDA(0);//STOP:when CLK is high DATA change form low to high
 	ets_delay_us(4);
	SCL(1); 
	ets_delay_us(4);
	SDA(1);//发送I2C总线结束信号
	ets_delay_us(4);							   	
}

2.等待应答

/*****
 * @Edit 2023.7
 * @brief 等待应答信号到来
 * @param[in] Null
 * @return 0,接收应答成功
 *         1,接收应答失败
******/
uint8_t iic_wait_ack(void)
{
	uint8_t ucErrTime=0;
	SDA_IN();      //SDA设置为输入  
	SDA(1); ets_delay_us(1);	   
	SCL(1); ets_delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			iic_stop();
			return 1;
		}
	}
	SCL(0);//时钟输出0 	   
	return 0;  
} 

3.产生应答/不产生应答

/*****
 * @Edit 2023.7
 * @brief 产生ACK应答
 * @param[in] Null
 * @return Null
******/
void iic_ack(void)
{
	SCL(0);
	SDA_OUT();
	SDA(0);
	ets_delay_us(2);
	SCL(1);
	ets_delay_us(2);
	SCL(0);
}
	 
/*****
 * @Edit 2023.7
 * @brief 不产生ACK应答
 * @param[in] Null
 * @return Null
******/   
void iic_nack(void)
{
	SCL(0);
	SDA_OUT();
	SDA(1);
	ets_delay_us(2);
	SCL(1);
	ets_delay_us(2);
	SCL(0);
}	

4.写一个字节

/*****
 * @Edit 2023.7
 * @brief IIC发送一个字节
 * @param[in] txd:要发送的一个字节
 * @return Null
******/  
void iic_send_byte(uint8_t txd)
{                        
    uint8_t t;   
	SDA_OUT(); 	    
    SCL(0);//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
		SDA((txd&0x80)>>7);
		txd<<=1; 	  
		ets_delay_us(2);   //对TEA5767这三个延时都是必须的
		SCL(1);
		ets_delay_us(2); 
		SCL(0);	
		ets_delay_us(2);
    }	 
}

5.读一个字节

/*****
 * @Edit 2023.7
 * @brief 读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
 * @param[in] ack:0,不发送ACK;1,发送ACK
 * @return 1个字节
******/  
uint8_t iic_read_byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
	{
        SCL(0);	
        ets_delay_us(2);
		SCL(1);
        receive<<=1;
        if(READ_SDA)receive++;   
		ets_delay_us(1); 
    }					 
    if (!ack)
        iic_nack();//发送nACK
    else
        iic_ack(); //发送ACK   
    return receive;
}

如果要在外部调用,需要把这些函数在myiic.h文件中声明,如图:
在这里插入图片描述

三、mpu6500

1.寄存器定义

在这里插入图片描述

2.读mpu6500操作

在这里插入图片描述

3.写mpu6500操作

在这里插入图片描述

4.写操作

编写测试任务函数
在这里插入图片描述

5.主函数

初始化led和iic接口GPIO,创建led和mpu6500的任务函数
在这里插入图片描述

四、调试结果

有两个任务:led任务和mpu6500任务
mpu6500任务调试现象:
在这里插入图片描述
上一篇博客的调试现象
在这里插入图片描述
读取同一个寄存器的值都是:0x70,说明GPIO模拟IIC协议成功。
led任务调试现象:
在这里插入图片描述
源码会放到CSDN的资源免费下载。

总结

通过使用GPIO模拟IIC协议,加深了对ESP32C3 GPIO的认识和相关API的应用,还加深了对IIC协议原理的理解,巩固了相关的知识。行到此处也只是在物联网开发这条路上的一小步而已,长路漫漫,共勉。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是基于 ESP32-C3GPIO 模拟 SPI 的示例代码: ```c #include <stdio.h> #include "driver/gpio.h" #include "esp_system.h" #define MOSI_PIN 16 #define MISO_PIN 17 #define SCLK_PIN 18 #define CS_PIN 19 #define DELAY_US 1 // 初始化 GPIO void gpio_init() { gpio_pad_select_gpio(MOSI_PIN); gpio_pad_select_gpio(MISO_PIN); gpio_pad_select_gpio(SCLK_PIN); gpio_pad_select_gpio(CS_PIN); gpio_set_direction(MOSI_PIN, GPIO_MODE_OUTPUT); gpio_set_direction(MISO_PIN, GPIO_MODE_INPUT); gpio_set_direction(SCLK_PIN, GPIO_MODE_OUTPUT); gpio_set_direction(CS_PIN, GPIO_MODE_OUTPUT); } // 定义 SPI 读写函数 void spi_transfer(uint8_t address, uint8_t *data, uint8_t len) { // 选中设备 gpio_set_level(CS_PIN, 0); // 发送地址 for (int i = 7; i >= 0; i--) { gpio_set_level(MOSI_PIN, (address >> i) & 1); gpio_set_level(SCLK_PIN, 1); ets_delay_us(DELAY_US); gpio_set_level(SCLK_PIN, 0); ets_delay_us(DELAY_US); } // 发送数据 for (int j = 0; j < len; j++) { for (int i = 7; i >= 0; i--) { gpio_set_level(MOSI_PIN, (data[j] >> i) & 1); gpio_set_level(SCLK_PIN, 1); ets_delay_us(DELAY_US); gpio_set_level(SCLK_PIN, 0); ets_delay_us(DELAY_US); } } // 读取数据 for (int j = 0; j < len; j++) { uint8_t value = 0; for (int i = 7; i >= 0; i--) { gpio_set_level(SCLK_PIN, 1); ets_delay_us(DELAY_US); value |= gpio_get_level(MISO_PIN) << i; gpio_set_level(SCLK_PIN, 0); ets_delay_us(DELAY_US); } data[j] = value; } // 取消选中设备 gpio_set_level(CS_PIN, 1); } ``` 在这个示例中,我们定义了四个 GPIO 引脚:MOSI、MISO、SCLK 和 CS。然后,我们使用 `gpio_init` 函数初始化 GPIO,并将这些引脚分别设置为输出或输入。接下来,我们定义了一个名为 `spi_transfer` 的函数,它接受一个地址和要写入的数据,并返回从 SPI 设备读取的数据。 在 `spi_transfer` 函数中,我们首先选中设备(将 `CS` 设置为低电平),然后发送地址。我们使用 `for` 循环和 `gpio_set_level` 函数将地址的每个位发送到 `MOSI` 引脚上,然后在每个位发送完毕后,我们将 `SCLK` 引脚置高一段时间,然后再置低,以向设备发送时钟信号。接下来,我们发送要写入的数据,方法与发送地址类似。 最后,我们使用 `gpio_get_level` 函数读取从 `MISO` 引脚接收到的数据,并将其存储在 `data` 数组中。注意,在读取数据时,我们需要先将 `SCLK` 引脚置高,然后读取 `MISO` 引脚上的数据,最后再将 `SCLK` 引脚置低。读取完数据后,我们取消选中设备(将 `CS` 设置为高电平),并将读取到的数据存储在 `data` 数组中。 这是一个基本的 GPIO 模拟 SPI 的示例代码,在实际应用中,你可能需要根据自己的需求进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值