​​​​​​​实验(四):I2C应用:读写串行FLASH 实验

您所在的位置:网站首页 形容充满气概的成语 ​​​​​​​实验(四):I2C应用:读写串行FLASH 实验

​​​​​​​实验(四):I2C应用:读写串行FLASH 实验

2023-06-16 05:28| 来源: 网络整理| 查看: 265

实验目的与任务

实验目的:

1. 学习对I2C的使用;

2. 掌握KEIL5的仿真与调试。

任务:

1. 根据要求编写程序,并写出原理性注释;

2. 将检查程序运行的结果,分析一下是否正确;

3. 完成所建工程的验证调试。

实验要求

本实验以 EEPROM的读写实验为大家讲解 STM32 的 I2C 使用方法。实验中 STM32 的 I2C 外设采用主模式,分别用作主发送器和主接收器, 通过查询事件的方式来确保正常通讯。

实验内容及步骤

本实验板中的 EEPROM 芯片(型号: AT24C02)的 SCL 及 SDA 引脚连接到了 STM32 对应的 I2C 引脚中,结合上拉电阻,构成了 I2C 通讯总线,它们通过 I2C 总线交互。EEPROM 芯片的设备地址一共有 7 位,其中高 4 位固定为: 1010 b,低 3 位则由 A0/A1/A2信号线的电平决定,见下图,图中的 R/W 是读写方向位,与地址无关。

按照我们此处的连接, A0/A1/A2 均为 0,所以 EEPROM 的 7 位设备地址是: 1010000b ,即 0x50。由于 I2C 通讯时常常是地址跟读写方向连在一起构成一个 8 位数,且当R/W 位为 0 时,表示写方向,所以加上 7 位地址,其值为“0xA0”,常称该值为 I2C 设备的“写地址”;当 R/W 位为 1 时,表示读方向,加上 7 位地址,其值为“0xA1”,常称该值为“读地址”。

EEPROM 芯片中还有一个 WP 引脚,具有写保护功能,当该引脚电平为高时,禁止写入数据,当引脚为低电平时,可写入数据,我们直接接地,不使用写保护功能。

1. 软件设计

① 实验新建文件步骤:

运行Keil 5开发环境。由于没有AT24C02模块,本实验使用基于I2C协议的MPU6050姿态传感器代替。为了使工程更加有条理,我们把I2C通讯底层协议和MPU6050功能函数相关的代码独立分开存储,方便以后移植。新建MyI2C.c、MyI2C.h、MPU6050.c、MPU6050.h、MPU6050_Reg.h文件。

② 编程要点:

配置通讯使用的目标引脚为开漏模式;

使能 I2C 外设的时钟;

配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;

编写基本 I2C 按字节收发的函数;

编写读写 EEPROM 存储内容的函数;

编写测试程序,对读写数据进行校验。

2. 实验步骤

(1)运行Keil uVision5开发环境,建立一个项目工程。

(2)在工程中添加main.c文件,因需要用到OLED显示屏,所以将之前实验写好的OLED文件移植到该工程中,然后在main.c中调用,如图1所示。

图1 移植程序

(3)在工程中添加I2C底层驱动文件,因此需要创建MyI2C.c文件,编写I2C通讯时序,如图2所示。

图2 编写MyI2C.c代码

(4)编写MyI2C.h程序,方便以后工程文件以移植,使项目工程工具有移植性,如图3所示。

图3 MyI2C.h程序

(5)在工程中添加MPU6050使用的功能函数文件,因此需要先创建W25Q64_Ins.h文件,存放指令码,如图4所示。

图4 编写MPU6050指令码

(6)创建MPU6050.c文件,编写需要使用的功能函数,如图5所示。

图5 编写MPU6050功能函数

(7)编写MPU6050.h程序,方便以后工程文件以移植,使项目工程工具有移植性,如图6所示。

图6 编写MPU6050.h程序

(8)编写main.c程序,读取设备ID,读取六个方向的加速度计数据,如图7所示。

图7 main.c程序

运行并调试成功并无错误和警告。

3. 调试验证及结果

(1)将开发板连接到电脑上,使用跳线在面包板上将MPU6050和单片机连接,使用STLINK将程序烧录到STM32中,如图8所示。

图8 线路连接

(2)程序烧录后,实验现象如图9所示:

图9 实验现象

实验代码分析

(1)MyI2C.c:

#include "stm32f10x.h" // Device header #include "Delay.h" /** * @brief 封装写SCL * @param * @retval */ void MyI2C_W_SCL(uint8_t BitValue) { GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue); Delay_us(10); } /** * @brief 封装写SDA * @param * @retval */ void MyI2C_W_SDA(uint8_t BitValue) { GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue); Delay_us(10); } /** * @brief 封装读SDA * @param * @retval */ uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); Delay_us(10); return BitValue; } /** * @brief 初始化 * @param * @retval */ void MyI2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); } /** * @brief I2C起始条件 * @param * @retval */ void MyI2C_Start(void) { MyI2C_W_SDA(1);//拉高,释放 MyI2C_W_SCL(1); MyI2C_W_SDA(0);//拉低 MyI2C_W_SCL(0); } void MyI2C_Stop(void) { MyI2C_W_SDA(0);//拉低 MyI2C_W_SCL(1);//释放SCL MyI2C_W_SDA(1);//释放SDA } /** * @brief 发送一个字节 * @param * @retval */ void MyI2C_SendByte(uint8_t Byte) { uint8_t i; for (i = 0; i < 8; i ++) { MyI2C_W_SDA(Byte & (0x80 >> i));//从最高位开始取 MyI2C_W_SCL(1);//释放SCL MyI2C_W_SCL(0);//拉低SCL } } /** * @brief 读一个字节 * @param * @retval */ uint8_t MyI2C_ReceiveByte(void) { uint8_t i, Byte = 0x00; MyI2C_W_SDA(1); for (i = 0; i < 8; i ++) { MyI2C_W_SCL(1); if (MyI2C_R_SDA()) {Byte |= (0x80 >> i);} MyI2C_W_SCL(0); } return Byte; } /** * @brief 发送应答 * @param * @retval */ void MyI2C_SendAck(uint8_t AckBit) { MyI2C_W_SDA(AckBit); MyI2C_W_SCL(1); MyI2C_W_SCL(0); } /** * @brief 接受应答 * @param * @retval */ uint8_t MyI2C_ReceiveAck(void) { uint8_t AckBit; MyI2C_W_SDA(1); MyI2C_W_SCL(1); AckBit = MyI2C_R_SDA(); MyI2C_W_SCL(0); return AckBit; }

(2)MPU6050.c:

#include "stm32f10x.h" // Device header #include "MyI2C.h" #include "MPU6050_Reg.h" #include "Delay.h" #define MPU6050_ADDRESS 0xD0 /** * @brief 指定地址写 * @param * @retval */ void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); MyI2C_SendByte(RegAddress);//指定寄存器地址 MyI2C_ReceiveAck(); MyI2C_SendByte(Data);//指定写入数据 MyI2C_ReceiveAck(); MyI2C_Stop(); } /** * @brief 指定地址读 * @param * @retval */ uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck(); MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS | 0x01);//发送 读 指令 MyI2C_ReceiveAck(); Data = MyI2C_ReceiveByte();//接受数据 MyI2C_SendAck(1);//发送应答 MyI2C_Stop(); return Data; } /** * @brief 初始化 * @param * @retval */ void MPU6050_Init(void) { MyI2C_Init(); //解除睡眠,选择陀螺仪时钟,6个轴不待机,采样分频为10,滤波参数最大,陀螺仪和加速度计选最大量程 MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);//电源管理寄存器1,选择陀螺仪时钟 //Delay_ms(100); MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);//电源管理寄存器2 //Delay_ms(100); MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09);//采样率分频,10分频 MPU6050_WriteReg(MPU6050_CONFIG, 0x06);//配置寄存器,0000 0110 MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);//陀螺仪配置寄存器, 0001 1000 MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计配置寄存器 } /** * @brief * @param * @retval */ uint8_t MPU6050_ReadID(void) { return MPU6050_ReadReg(MPU6050_WHO_AM_I); } /** * @brief 获取数据寄存器数据 * @param * @retval */ void MPU6050_ReadData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ) { //加速度 *AccX = (MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3