文章目录

  • 前言
  • 一、配置GPIO
  • 1.GPIO方向设置
  • 2.GPIO输出高低电平
  • 3.GPIO读取输入电平
  • 4.GPIO相关宏定义
  • 二、实现IIC协议
  • 1.起始/停止信号
  • 2.等待应答
  • 3.产生应答/不产生应答
  • 4.写一个字节
  • 5.读一个字节
  • 三、mpu6500
  • 1.寄存器定义
  • 2.读mpu6500操作
  • 3.写mpu6500操作
  • 4.写操作
  • 5.主函数
  • 四、调试结果
  • 总结



前言

在“”的博文中详细的介绍了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"中,官方的编程指南介绍如下图:

esp32模拟值跳动 esp32模拟口_iot


这里了进行封装一下,方便后面调用,如下图:

esp32模拟值跳动 esp32模拟口_API_02

2.GPIO输出高低电平

因为ESP32C3没有位带功能,所以无法直接配置别名操作GPIO输出高低电平(如led = 1;led= 0)。在之前对GPIO的学习中知道,要改变GPIO的输出电平那可以对寄存器(GPIO_OUT_W1TS_REG、GPIO_OUT_W1TC_REG)的相应位置位即可,所以还可以通过操作寄存器来实现相对好用的GPIO控制,如下图:

esp32模拟值跳动 esp32模拟口_API_03


直接操作指定GPIO引脚的寄存器实现高低电平的输出,再借用三目运算‘:’、‘?’来选择输出电平的高低,这样就实现了低配的别名操作。

volatile是不让编译器进行优化,并每次都是取地址上的数据。

3.GPIO读取输入电平

SDA引脚需要读取从机发送的数据,ESP32C3的源码中有读取GPIO输入电平的API,该API为int gpio_get_level(gpio_num_t gpio_num)在头文件#include "driver/gpio.h"中,官方的编程指南介绍如下图:

esp32模拟值跳动 esp32模拟口_esp32模拟值跳动_04


也对这个API进行封装一下,如下图,是不是有熟悉的味道!!!

esp32模拟值跳动 esp32模拟口_iot_05

4.GPIO相关宏定义

esp32模拟值跳动 esp32模拟口_引脚_06

二、实现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文件中声明,如图:

esp32模拟值跳动 esp32模拟口_iot_07

三、mpu6500

1.寄存器定义

esp32模拟值跳动 esp32模拟口_物联网_08

2.读mpu6500操作

esp32模拟值跳动 esp32模拟口_API_09

3.写mpu6500操作

esp32模拟值跳动 esp32模拟口_esp32模拟值跳动_10

4.写操作

编写测试任务函数

esp32模拟值跳动 esp32模拟口_引脚_11

5.主函数

初始化led和iic接口GPIO,创建led和mpu6500的任务函数

esp32模拟值跳动 esp32模拟口_API_12

四、调试结果

有两个任务:led任务和mpu6500任务

mpu6500任务调试现象:

esp32模拟值跳动 esp32模拟口_esp32模拟值跳动_13


上一篇博客的调试现象

esp32模拟值跳动 esp32模拟口_esp32模拟值跳动_14


读取同一个寄存器的值都是:0x70,说明GPIO模拟IIC协议成功。

led任务调试现象:

esp32模拟值跳动 esp32模拟口_物联网_15


总结

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