第十六届蓝桥杯嵌入式赛道备赛---I2C通信+E2PROM(AT24C02)存储+MCP4017(可编程电阻)(日志五)
这一个板块针对开发板上的I2C总线区域的模块,这一条总线同时连接了两个芯片一个是,另外一个是MCP4017(可编程电阻)。同时官方给的资料文件中有软件I2C的源码可以直接给我们使用,所以这里就使用软件I2C,就不用芯片自带的硬件I2C了。关于I2C通信的详细细节这里就不展开讨论,在代码配置的过程中会简单提到一点,详细的内容大家自行去学习。官方的软件I2C的源码放在了文章的最后,有需要自取。
前言
这一个板块针对开发板上的I2C总线区域的模块,这一条总线同时连接了两个芯片一个是E2PROM(AT24C02),另外一个是MCP4017(可编程电阻)。同时官方给的资料文件中有软件I2C的源码可以直接给我们使用,所以这里就使用软件I2C,就不用芯片自带的硬件I2C了。
关于I2C通信的详细细节这里就不展开讨论,在代码配置的过程中会简单提到一点,详细的内容大家自行去学习。
官方的软件I2C的源码放在了文章的最后,有需要自取。

芯片资料
E2PROM(AT24C02)
简单介绍一下这芯片的功能,AT24C02是一种可擦除和可编程只读存储器,可以实现掉电不丢失,用于保存单片机运行时想要永久保存的数据信息。
AT24C02/ 2K 串行 EEPROM:内部组织有 32 页,每页 8 字节,2K 需要一个 8 位数据字地址用于随机字寻址。大概意思就是这个芯片能存256个uint8_t大小的数据,数据地址也要是uint8_t的数据类型。
设备地址
要用I2C通信找到对应的设备就要知道这个设备的地址,这个芯片有多个型号,对应的型号的的设备地址标签对应关系如下,AT24C01/02/04/08/16分别对应1K/2K/4K/8K/16K。A2、A1、A0分别对应电路原理图的E3、E2、E1,在上面的框图中可知这三个引脚都是接地的,所以对应的二进制数就是0。最后一位R/W,就是读写位,分别对应1/0。因此这个芯片的设备地址就是1010000_(即0xA_),最后一位读写位。

通信时序
我们基本只需要掌握指定地址读和指定地址写即可,其它连续写和连续读进行循环操作即可。
指定地址写(0xA1)

指定地址读(0xA0)
这里的时序图里面虽然写的是随机地址,但是并不随机,因为里面有Word Address(数据地址)的信息,所以他就是指定地址读。(应该是翻译问题)

从这个时序图中已经能够很清晰的知道每一段时序的作用是什么,下面在写代码的过程中也是调用官方的软件I2C的函数来完成对时序的拼接。
代码部分
E2prom.h
#ifndef __E2PROM_H
#define __E2PROM_H
//指定地址写
void E2prom_write(uint8_t Address, uint8_t Data);
//指定地址读
uint8_t E2prom_read(uint8_t Address);
#endif
E2prom.c
#include "i2c_hal.h"
#include "stm32g4xx_hal.h"
//指定地址写
void E2prom_write(uint8_t Address, uint8_t Data)
{
I2CStart(); //1.起始信号(START)
I2CSendByte(0xA0); //2.发送设备地址 + 写信号(DEVICE ADDRESS+WRITE)
I2CWaitAck(); //3.等待确认信号(ACK)
I2CSendByte(Address); //4.发送数据存储地址(WORD ADDRESS)(可以为0~255,对应256个)
I2CWaitAck(); //5.等待确认信号(ACK)
I2CSendByte(Data); //6.发送数据(DATA)
I2CWaitAck(); //7.等待确认信号(ACK)
I2CStop(); //8.结束信号(STOP)
}
//指定地址读
uint8_t E2prom_read(uint8_t Address)
{
uint8_t Data;
I2CStart(); //1.起始信号(START)
I2CSendByte(0xA0); //2.发送设备地址+写信号(DEVICE ADDRESS+WRITE)
I2CWaitAck(); //3.等待确认信号(ACK)
I2CSendByte(Address); //4.发送数据存储地址(WORD ADDRESS)(可以为0~255,对应256个)
I2CWaitAck(); //5.等待确认信号(ACK)
I2CStop(); //下一次发送起始信号前先停止,留足缓冲时间,避免时序混乱
I2CStart(); //6.起始信号(START)
I2CSendByte(0xA1); //7.发送设备地址+读信号(DEVICE ADDRESS+READ)
I2CWaitAck(); //8.等待确认信号(ACK)
Data = I2CReceiveByte(); //9.接收数据(DATA)
I2CSendNotAck(); //10.发送非确认信号(NO ACK)
I2CStop(); //11.I2C结束信号(STOP)
return Data;
}
这一部分的代码在有官方的软件I2C的源码下,只要按照这个芯片的I2C通信时序给他拼接起来即可,非常的简单粗暴。
main.c
同样我也设置了一个测试代码,检测芯片存储的准确性,也是确认我们时序的拼接没有任何问题。
最终的效果就是在LCD屏幕上打印出ASCII对应的字符。
#include "lcd.h"
#include "E2prom.h"
#include "i2c_hal.h"
uint8_t str[20];
int main(void)
{
I2CInit();
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
uint8_t i = 0;
uint8_t Address = 0x00; //存储起始地址
uint8_t Data = 0x20; //空格的ASCII码
uint8_t Line = Line0; //起始行
while (1)
{
E2prom_write(Address,Data);
//要进行合适的延时,防止读写两个数据帧靠的太近导致数据错乱
HAL_Delay(200);
str[i] = E2prom_read(Address);
LCD_DisplayStringLine(Line,(unsigned char*)str);
Address++;
i++;
Data++;
if(i >= 19)
{
Line += 24;
i = 0;
}
if(Data == 127)
{
break;
}
}
}
MCP4017(可编程电阻)
内部的框图如图所示,对应引脚的连接参考最上面的原理图。

设备地址
MCP4017对应的设备地址是0x5_(E/F),E是包含了写位,F是包含了读位。

然后这些是对应电阻位置的数据地址从0x00~0x7F,一共是0~127,七位的数据值。

对应的电阻值如下,对应这块开发板的芯片,一般情况下的最大电阻是1e6Ω,步进值是Rs,最后经过程序设置得出的电阻值是 R = Rs * (N+1),N是上面的十六进制对应的十进制数。

通信时序
指定地址写(0x5E)
这里是连续的写入操作,实际上我们只需要写入一次即可。

指定地址读(0x5F)
这里的读时序跟前面的AT芯片有所区别,他没有设置写时序,而是直接写入读时序。

代码部分
Mcp4017.h
#ifndef __MCP4017_H
#define __MCP4017_H
//指定地址写
void MCP4017_Write(uint8_t Data);
//指定地址读
uint8_t MCP4017_Read(void);
#endif
Mcp4017.c
时序的拼接照着芯片的标明的时序来拼接即可。
#include "stm32g4xx_hal.h"
#include "i2c_hal.h"
void MCP4017_Write(uint8_t Data)
{
I2CStart();
I2CSendByte(0X5E);
I2CWaitAck();
I2CSendByte(Data);
I2CWaitAck();
I2CStop();
}
uint8_t MCP4017_Read(void)
{
uint8_t Data;
I2CStart();
I2CSendByte(0X5F);
I2CWaitAck();
Data = I2CReceiveByte();
I2CSendNotAck();
I2CStop();
return Data;
}
main.c
测试的代码是跟ADC模块混用的,不清楚这个模块的可以看看我之前的博客,里面有详细的介绍。
#include "lcd.h"
#include "ADC_read.h"
#include "i2c_hal.h"
#include "Mcp4017.h"
int main(void)
{
I2CInit();
ADC_Init();
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
MCP4017_Write(0x7f);
while (1)
{
ADC_Show();
}
}
官方I2C源码
i2c_hal.h
#ifndef __I2C_HAL_H
#define __I2C_HAL_H
#include "stm32g4xx_hal.h"
void I2CStart(void);
void I2CStop(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(void);
void I2CSendNotAck(void);
void I2CSendByte(unsigned char cSendByte);
unsigned char I2CReceiveByte(void);
void I2CInit(void);
#endif
i2c_hal.c
/*
程序说明: CT117E-M4嵌入式竞赛板GPIO模拟I2C总线驱动程序
软件环境: MDK-ARM HAL库
硬件环境: CT117E-M4嵌入式竞赛板
日 期: 2020-3-1
*/
#include "i2c_hal.h"
#define DELAY_TIME 20
/**
* @brief SDA线输入模式配置
* @param None
* @retval None
*/
void SDA_Input_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出模式配置
* @param None
* @retval None
*/
void SDA_Output_Mode()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief SDA线输出一个位
* @param val 输出的数据
* @retval None
*/
void SDA_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_7;
}
else
{
GPIOB->BRR |= GPIO_PIN_7;
}
}
/**
* @brief SCL线输出一个位
* @param val 输出的数据
* @retval None
*/
void SCL_Output( uint16_t val )
{
if ( val )
{
GPIOB->BSRR |= GPIO_PIN_6;
}
else
{
GPIOB->BRR |= GPIO_PIN_6;
}
}
/**
* @brief SDA输入一位
* @param None
* @retval GPIO读入一位
*/
uint8_t SDA_Input(void)
{
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){
return 1;
}else{
return 0;
}
}
/**
* @brief I2C的短暂延时
* @param None
* @retval None
*/
static void delay1(unsigned int n)
{
uint32_t i;
for ( i = 0; i < n; ++i);
}
/**
* @brief I2C起始信号
* @param None
* @retval None
*/
void I2CStart(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C结束信号
* @param None
* @retval None
*/
void I2CStop(void)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(0);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SDA_Output(1);
delay1(DELAY_TIME);
}
/**
* @brief I2C等待确认信号
* @param None
* @retval None
*/
unsigned char I2CWaitAck(void)
{
unsigned short cErrTime = 5;
SDA_Input_Mode();
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
while(SDA_Input())
{
cErrTime--;
delay1(DELAY_TIME);
if (0 == cErrTime)
{
SDA_Output_Mode();
I2CStop();
return ERROR;
}
}
SDA_Output_Mode();
SCL_Output(0);
delay1(DELAY_TIME);
return SUCCESS;
}
/**
* @brief I2C发送确认信号
* @param None
* @retval None
*/
void I2CSendAck(void)
{
SDA_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送非确认信号
* @param None
* @retval None
*/
void I2CSendNotAck(void)
{
SDA_Output(1);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C发送一个字节
* @param cSendByte 需要发送的字节
* @retval None
*/
void I2CSendByte(unsigned char cSendByte)
{
unsigned char i = 8;
while (i--)
{
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output(cSendByte & 0x80);
delay1(DELAY_TIME);
cSendByte += cSendByte;
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
}
SCL_Output(0);
delay1(DELAY_TIME);
}
/**
* @brief I2C接收一个字节
* @param None
* @retval 接收到的字节
*/
unsigned char I2CReceiveByte(void)
{
unsigned char i = 8;
unsigned char cR_Byte = 0;
SDA_Input_Mode();
while (i--)
{
cR_Byte += cR_Byte;
SCL_Output(0);
delay1(DELAY_TIME);
delay1(DELAY_TIME);
SCL_Output(1);
delay1(DELAY_TIME);
cR_Byte |= SDA_Input();
}
SCL_Output(0);
delay1(DELAY_TIME);
SDA_Output_Mode();
return cR_Byte;
}
//
void I2CInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}
到这里只是MCP4017的基本应用,后续会更新它的一些详细参数以及应用。主要是通过读取的电位值来估算大概的电阻值,这个在芯片手册内也有写,感兴趣的可以去看看。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)