嵌入式软件--DAY8 IIC通讯下 硬件实现
正常写项目,用硬件只会更快。51没有IIC外设,我们需要软件模拟,在STM32中我们可以大胆使用硬件,只需要操作IIC外设对应的寄存器即可。
1.硬件电路设计
1.1配置
可以看到IIC模块挂在APB1系统总线上。
复用引脚PB10 PB11,底层协议硬件实现了,我们自己做好配置外设,他会自动根据协议产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器ODR,就能完成数据收发。
STM32外设的iic外设可用作主机和从机,没有明确规定。支持两种速率:100kbit/s,400kbit/s。
支持7位、10位设备地址,支持DMA数据传输,并具有数据校验功能。
iic外设还支持SMBUS2.0协议,SMBUS与IIC类似。
1.2功能框图
控制寄存器2个 CR1 CR2
状态寄存器2个 SR1 SR2
2.寄存器初始化配置
用PB10 PB11与EEPROM进行通讯。
2.1寄存器详解
2.1.1控制寄存器CR
PE最后一步开
只要产生起始条件了,就可以成为主设备
SWRST:软件复位
2.1.2时钟控制寄存器 I2C_CCR
占空比:高电平占周期比例
TRISE:上升折线所占时钟周期
3.硬件实现IIC,寄存器案例
3.1 初始化配置
配置成复用开漏模式,开启内部上拉电阻。开启IO和IIC时钟使能。配置速率。
void I2C_Init(void)
{// 1. 配置时钟RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;// 2. GPIO配置,PB10、PB11 复用开漏输出,MODE - 11, CNF - 11GPIOB->CRH |= (GPIO_CRH_MODE10 | GPIO_CRH_MODE11 | GPIO_CRH_CNF10 | GPIO_CRH_CNF11);// 3. I2C 配置// 3.1 硬件工作模式配置I2C2->CR1 &= ~I2C_CR1_SMBUS; // I2C模式I2C2->CCR &= ~I2C_CCR_FS; // 标准模式// 3.2 时钟频率 36MHzI2C2->CR2 |= 36 << 0;// 3.3 高电平时间:5us - 180个时钟周期;100kbpsI2C2->CCR |= 180 << 0;// 3.4 上升沿时间 1us - 36个时钟周期I2C2->TRISE |= 37;// 3.5 使能I2C模块I2C2->CR1 |= I2C_CR1_PE;
}
3.2 时序
// 主机发出起始信号
uint8_t I2C_Start(void)
{// 产生起始信号I2C2->CR1 |= I2C_CR1_START;// 等待起始信号发出uint16_t timeout = 0xffff; // 超时时间while ((I2C2->SR1 & I2C_SR1_SB) == 0 && timeout){timeout--;}return timeout ? OK : FAIL;
}// 主机设置收发完数据之后发出停止信号
void I2C_Stop(void)
{I2C2->CR1 |= I2C_CR1_STOP;
}// 主机设置发送应答/非应答信号
void I2C_Ack(void)
{I2C2->CR1 |= I2C_CR1_ACK;
}
void I2C_NAck(void)
{I2C2->CR1 &= ~I2C_CR1_ACK;
}// 主机发送设备地址
uint8_t I2C_SendAddr(uint8_t addr)
{// 将要发送的地址写入DR,自动发送I2C2->DR = addr;// 等待地址成功发出(等待应答)uint16_t timeout = 0xffff; // 超时时间while ((I2C2->SR1 & I2C_SR1_ADDR) == 0 && timeout){timeout--;}I2C2->SR2; // 清除 ADDR 标志位return timeout ? OK : FAIL;
}// 主机发送一个字节
uint8_t I2C_SendByte(uint8_t byte)
{// 将要发送的数据写入DR,自动发送I2C2->DR = byte;// 等待字节发送完成(等待应答)uint16_t timeout = 0xffff; // 超时时间while ((I2C2->SR1 & I2C_SR1_BTF) == 0 && timeout){timeout--;}I2C2->DR; // 清除 BTF 标志位return timeout ? OK : FAIL;
}// 主机从SDA读取一个字节
uint8_t I2C_ReadByte(void)
{// 等待字节接收完成uint16_t timeout = 0xffff; // 超时时间while ((I2C2->SR1 & I2C_SR1_RXNE) == 0 && timeout){timeout--;}return timeout ? I2C2->DR : FAIL;
}
4.硬件实现IIC,HAL库写法
4.1 CUBEMX图形化界面配置
配置IIC引脚
IIC轻松配置完毕。
命名
因为与EEPROM通讯,最后需要对传输的数据进行窗口显示,开启串口
出现打开项目的窗口,一定先打开。会出现KEIL文件工程。
4.2 keil编译器配置
我们不能在图形化界面中生成EEPROM文件,我们需要自己添加。
之后就是老一套。勾勾减减即可。之后我们用VSCode打开。
4.3 代码编写
我们首先要将fputc重新写一下。这是基本操作了。
在usart.c里写fputc
int fputc(int ch, FILE *file)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);return ch;
}
因为M24c02不是cubemx生成,所以与我们生成的文件在函数声明上存在差异,逻辑一致,但只要把声明改改。
/** @Author: wushengran* @Date: 2024-09-23 14:07:07* @Description:** Copyright (c) 2024 by atguigu, All Rights Reserved.*/
#include "m24c02.h"// 初始化
void M24C02_Init(void)
{MX_I2C2_Init();
}// 写入一个字节,指定要写入的内部地址
void M24C02_WriteByte(uint8_t innerAddr, uint8_t byte)
{HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &byte, 1, 1000);// 加入延迟,等待写入EEPROM完成HAL_Delay(5);
}// 读取一个字节(随机地址读)
uint8_t M24C02_ReadByte(uint8_t innerAddr)
{uint8_t byte;HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, &byte, 1, 1000);return byte;
}// 连续写入多个字节(页写)
void M24C02_WriteBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{HAL_I2C_Mem_Write(&hi2c2, W_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, bytes, size, 1000);// 加入延迟,等待写入EEPROM完成HAL_Delay(5);
}// 连续读取多个字节
void M24C02_ReadBytes(uint8_t innerAddr, uint8_t *bytes, uint8_t size)
{HAL_I2C_Mem_Read(&hi2c2, R_ADDR, innerAddr, I2C_MEMADD_SIZE_8BIT, bytes, size, 1000);
}
串口这里就没问题了,如果想验证,可以打印一些数据。
在main里引入
构建并烧写
串口传输没有问题。
然后可以针对性对字节写入,字节读取进行测验。