TMS320F28P550SJ9学习笔记13: 软件I2C_驱动AT24Cxx存储芯片

news2025/3/16 15:11:26

今日尝试配置软件I2C通信,我的目标通信芯片是AT24C64,相较于AT24C02这样的8位寻址,它是16位寻址的,所以有些不同

文章提供测试代码讲解、完整工程下载、测试效果图

目录

软件I2C引脚初始化:

C内联函数改变SCL与SDA的输出:

#include "AT24C64.h"

主函数调用:

测试效果截图:

遇到的问题:

完整测试工程下载:


软件I2C引脚初始化:

I2C SCL = GPIO26

I2C SDA = GPIO16

C内联函数改变SCL与SDA的输出:

除了能够联系起SDA与SCL对应引脚寄存器输出0与1

还能改变SDA引脚寄存器的输入输出模式

#include "AT24C64.h"

移植的正点原子的代码,稍作修改

当时有个冗余等待ACK,影响了我实际的读取,因此被我删去

/*
 * AT24C64.c
 *
 *  Created on: 2025年3月14日
 *      Author: 30313
 */


#include "AT24C64.h"


//初始化 AT24C64
void Init_AT24_I2C_software(void)
{
    //设置GPIO16和GPIO26为GPIO模式
    EALLOW;
    GpioCtrlRegs.GPAMUX2.bit.GPIO16 = 0;// 将A17/GPIO16设置为GPIO模式
    GpioCtrlRegs.GPAMUX2.bit.GPIO26 = 0;  // 将B17/GPIO26设置为GPIO模式
    //禁用模拟
    //GpioCtrlRegs.GPAAMSEL.bit.GPIO26 = 0; // 禁用GPIO26的模拟功能
    GpioCtrlRegs.GPAAMSEL.bit.GPIO16 = 0; // 禁用GPIO16的模拟功能
    //设置GPIO10和GPIO11为输出
    GpioCtrlRegs.GPADIR.bit.GPIO16 = 1;   // 设置GPIO16为输出
    GpioCtrlRegs.GPADIR.bit.GPIO26 = 1;   // 设置GPIO26为输出
    //设置GPIO10和GPIO11为标准输入/输出模式
    GpioCtrlRegs.GPAPUD.bit.GPIO16 = 1;   // 启用GPIO16的上拉电阻
    GpioCtrlRegs.GPAPUD.bit.GPIO26 = 1;   // 启用GPIO26的上拉电阻
    //设置GPIO10和GPIO11为同步模式(可选)
    GpioCtrlRegs.GPAQSEL2.bit.GPIO16 = 0; // 设置GPIO16为同步模式
    GpioCtrlRegs.GPAQSEL2.bit.GPIO26 = 0; // 设置GPIO26为同步模式
    //GpioCtrlRegs.GPACTRL.bit.QUALPRD2=0x01 // 设置GPIO16~23 的 采样周期为 PLLSYSCLK/2
    EDIS;

}


//产生IIC起始信号
void AT24_IIC_Start(void)
{
    AT24_SDA_OUT();     //sda线输出
    AT24_IIC_SDA(1);
    AT24_IIC_SCL(1);
    DEVICE_DELAY_US(4);
    AT24_IIC_SDA(0);//START:when CLK is high,DATA change form high to low
    DEVICE_DELAY_US(4);
    AT24_IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}

//产生IIC停止信号
void AT24_IIC_Stop(void)
{
    AT24_SDA_OUT();//sda线输出
    AT24_IIC_SCL(0);
    AT24_IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
    DEVICE_DELAY_US(4);
    AT24_IIC_SCL(1);
    AT24_IIC_SDA(1);//发送I2C总线结束信号
    DEVICE_DELAY_US(4);
}

//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
INT8U AT24_IIC_WaitAck(void)
{
    INT8U ucErrTime=0;
    AT24_SDA_IN();      //SDA设置为输入
    AT24_IIC_SDA(1);DEVICE_DELAY_US(1);
    AT24_IIC_SCL(1);DEVICE_DELAY_US(1);
    while(AT24_READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            AT24_IIC_Stop();
            return 1;
        }
    }
    AT24_IIC_SCL(0);//时钟输出0
    return 0;
}
//产生ACK应答
void AT24_IIC_Ack(void)
{
    AT24_IIC_SCL(0);
    AT24_SDA_OUT();
    AT24_IIC_SDA(0);
    DEVICE_DELAY_US(2);
    AT24_IIC_SCL(1);
    DEVICE_DELAY_US(2);
    AT24_IIC_SCL(0);
}
//不产生ACK应答
void AT24_IIC_NAck(void)
{
    AT24_IIC_SCL(0);
    AT24_SDA_OUT();
    AT24_IIC_SDA(1);
    DEVICE_DELAY_US(2);
    AT24_IIC_SCL(1);
    DEVICE_DELAY_US(2);
    AT24_IIC_SCL(0);
}

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
INT8U AT24_IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    AT24_SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        AT24_IIC_SCL(0);
        DEVICE_DELAY_US(2);
        AT24_IIC_SCL(1);
        receive<<=1;
        if(AT24_READ_SDA == 1)receive++;
        DEVICE_DELAY_US(1);
    }
    if (!ack)
        AT24_IIC_NAck();//发送nACK
    else
        AT24_IIC_Ack(); //发送ACK
    return receive;
}


void AT24_Write_IIC_Byte(unsigned char IIC_Byte)
{
    unsigned char i;
    unsigned char da;
    da=IIC_Byte;
    AT24_SDA_OUT();
    AT24_setIIC_SCL(0);
    //DEVICE_DELAY_US(2);
    for(i=0;i<8;i++)
    {
       if((da & 0x80)>>7)
       {
           AT24_IIC_SDA(1);
           //DEVICE_DELAY_US(2);
       }
       else
       {
           AT24_IIC_SDA(0);
           //DEVICE_DELAY_US(2);
       }
         da <<= 1;
         DEVICE_DELAY_US(2);
         AT24_setIIC_SCL(1);
         DEVICE_DELAY_US(2);
         AT24_setIIC_SCL(0);
         DEVICE_DELAY_US(2);
    }
}

/*******************************************************************************
  * 函数名:x24Cxx_WriteByte
  * 功  能:写一个字节
  * 参  数:u16Addr要写入的地址
            u8Data要写入的数据
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void AT24CXX_WriteOneByte(uint16_t u16Addr, uint8_t u8Data)
{
    //x24Cxx_WriteEnable();//使能写入
    AT24_IIC_Start();//起始信号
    if(EE_TYPE>AT24C16)
    {
    AT24_Write_IIC_Byte(DEV_ADDR);
    AT24_IIC_WaitAck();//等待应答
    AT24_Write_IIC_Byte(u16Addr>>8);//发送高地址
    }else
    {
        AT24_Write_IIC_Byte(DEV_ADDR+((u16Addr/256)<<1));   //发送器件地址0XA0,写数据
    }
    AT24_IIC_WaitAck();//等待应答
    AT24_Write_IIC_Byte(u16Addr % 256);  //发送低地址
    AT24_IIC_WaitAck();//等待应答
    AT24_Write_IIC_Byte(u8Data);  //发送字节
    AT24_IIC_WaitAck();//等待应答
    AT24_IIC_Stop();//停止信号
    //x24Cxx_WriteDisble();//禁止写入
}

/*******************************************************************************
  * 函数名:x24Cxx_ReadByte
  * 功  能:读一个字节
  * 参  数:u16Addr要读取的地址
  * 返回值:u8Data读出的数据
  * 说  明:无
*******************************************************************************/
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)
{
    uint8_t u8Data = 0;
    AT24_IIC_Start();//起始信号
    if(EE_TYPE>AT24C16)
    {
        AT24_Write_IIC_Byte(DEV_ADDR);
        AT24_IIC_WaitAck();//等待应答
        AT24_Write_IIC_Byte(ReadAddr>>8); //发送高地址
        //AT24_IIC_WaitAck();//等待应答
    }
    else AT24_Write_IIC_Byte(DEV_ADDR+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据

    AT24_IIC_WaitAck();//等待应答
    AT24_Write_IIC_Byte(ReadAddr%256);   //发送低地址
    AT24_IIC_WaitAck();//等待应答
    AT24_IIC_Start();//起始信号
    AT24_Write_IIC_Byte(0xa1);//器件寻址+读
    AT24_IIC_WaitAck();//等待应答
    u8Data = AT24_IIC_Read_Byte(1);
    AT24_IIC_Stop();//停止信号
    return u8Data;
}


检查AT24CXX是否正常
这里用了24XX的最后一个地址(255)来存储标志字.
如果用其他24C系列,这个地址要修改
返回1:检测失败
返回0:检测成功
//INT8U AT24CXX_Check(void)
//{
//    INT8U temp;
//    temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
//    if(temp==0X55)return 0;
//    else//排除第一次初始化的情况
//    {
//        AT24CXX_WriteOneByte(255,0X55);
//        temp=AT24CXX_ReadOneByte(255);
//        if(temp==0X55)return 0;
//    }
//    return 1;
//}



#ifndef USER_AT24C64_H_
#define USER_AT24C64_H_

#include "f28x_project.h"
#include "driverlib.h"
#include "device.h"

#include "Typedef.h"

#define READ_CMD                1
#define WRITE_CMD               0
#define DEV_ADDR                0xA0                    //设备硬件地址

#define AT24C01     127
#define AT24C02     255
#define AT24C04     511
#define AT24C08     1023
#define AT24C16     2047
#define AT24C32     4095
#define AT24C64     8191
#define AT24C128    16383
#define AT24C256    32767

#define EE_TYPE AT24C64

#define u8 unsigned char
#define u32 unsigned int
#define u16 unsigned short

//宏定义 设定寄存器来改变SDA线的输入输出状态:
#define AT24_SDA_IN()  {EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO16 = 0;  EDIS;} //SDA输入模式
#define AT24_SDA_OUT() {EALLOW; GpioCtrlRegs.GPADIR.bit.GPIO16 = 1;  EDIS;} //SDA输出模式


static inline void AT24_setIIC_SDA(Uint16 value) {
    if (value) { GpioDataRegs.GPASET.bit.GPIO16 = 1;   }// GPIO16 输出1
    else { GpioDataRegs.GPACLEAR.bit.GPIO16 = 1; }      // GPIO16 输出0
}

static inline void AT24_setIIC_SCL(Uint16 value) {
    if (value) { GpioDataRegs.GPASET.bit.GPIO26 = 1;  } // GPIO26 输出1
    else { GpioDataRegs.GPACLEAR.bit.GPIO26 = 1; }      // GPIO26 输出0
}

//IO操作函数
#define AT24_IIC_SCL    AT24_setIIC_SCL
#define AT24_IIC_SDA    AT24_setIIC_SDA
#define AT24_READ_SDA   GpioDataRegs.GPADAT.bit.GPIO16


void Init_AT24_I2C_software(void);
void AT24_IIC_NAck(void);
void AT24_IIC_Ack(void);
INT8U AT24_IIC_WaitAck(void);
void AT24_IIC_Stop(void);
void AT24_IIC_Start(void);

INT8U AT24_IIC_Read_Byte(unsigned char ack);
void AT24_Write_IIC_Byte(unsigned char IIC_Byte);
void AT24CXX_WriteOneByte(uint16_t u16Addr, uint8_t u8Data);        //指定地址写入一个字节
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr);

//u8 AT24CXX_Check(void);  //检查器件


#endif /* USER_AT24C64_H_ */

主函数调用:

每隔 300ms 读写一次EEPROM 0x0002地址的数据,每次数据数值加一,断电会从断电前的数值大小继续往下计数

测试效果截图:

这次运行时正常的,而且我还开起了一个CPUTimer0,100us中断,中断服务函数有80us的阻塞,这样都没影响软件I2C的de正常读写:

TMS320F28P550SJ9软件I2C_驱动AT24Cx

遇到的问题:

当时直接移植的代码,它里面有俩次等待ACK,第一次逻辑分析仪得出正常响应了,第二次是冗余的,会造成器件通信失败,直接进入停止信号:

将额外的这句等待ack删去就好了

还有就是I2C在写器件数据之后,需要等至少10ms再去读,或者写,否则器件也会歇菜不干活:

完整测试工程下载:

这个文件的功能描述有点不对,以这里的描述为准

https://download.csdn.net/download/qq_64257614/90486368?spm=1001.2014.3001.5503

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2316107.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手抖预防方法主要包括以下几个方面

手抖预防方法主要包括以下几个方面&#xff1a; 1. 心理调节&#xff1a;保持心情舒畅&#xff0c;避免过度紧张和焦虑。如有必要&#xff0c;可以寻求心理医生帮助进行心理调适。 2. 充分休息&#xff1a;保证充足的睡眠时间&#xff0c;避免熬夜和过度劳累。合理安排工作和…

SpringBoot MCP 入门使用

随着AI的火爆&#xff0c;最近发现MCP在未来确实大有可为&#xff0c;作为一名javaer怎么可以落后在历史洪流呢&#xff0c;根据官网和cursor也从零开始体验一下自定义mcp server。以后可以根据自己业务场景做出各种适合自身业务的工具。 至于什么是MCP 可以到https://modelcon…

Windows 11 安装Docker Desktop环境

1、确认CPU开启虚拟化 打开任务管理器&#xff0c;切换到“性能”选项卡&#xff0c;查看 CPU 信息。若“虚拟化”状态显示为“已启用”&#xff0c;则表示虚拟化已开启&#xff1b;若显示为“已禁用”&#xff0c;则需要在启动时进入 BIOS 开启虚拟化设置&#xff08;若显示已…

汽车保养记录用什么软件记录,汽车维修记录查询系统,佳易王汽车保养维护服务记录查询管理系统操作教程

一、概述 本实例以佳易王汽车保养维护服务记录查询管理系统为例说明&#xff0c;其他版本可参考本实例。试用版软件资源可到文章最后了解&#xff0c;下载的文件为压缩包文件&#xff0c;请使用免费版的解压工具解压即可试用。 软件特点&#xff1a;1、功能实用&#xff0c;操…

深入理解C/C++堆数据结构:从原理到实战

一、堆的本质与特性 1.1 什么是堆数据结构&#xff1f; 堆&#xff08;Heap&#xff09;是一种特殊的完全二叉树&#xff0c;它满足以下核心性质&#xff1a; 堆序性&#xff1a;每个节点的值都满足特定顺序关系 结构性&#xff1a;完全二叉树的结构特性&#xff08;除最后一…

【MATLAB实战】实现白鲸算法(BWO)优化BP神经网络:提升模型性能的新思路

一、什么是白鲸优化算法&#xff08;BWO&#xff09;&#xff1f; 白鲸优化算法是受自然界中白鲸群体行为和觅食策略启发的一种新型智能优化算法。白鲸在捕食过程中展现出高效的协作能力和适应性&#xff0c;例如通过“回声定位”搜索猎物位置群体间信息共享&#xff0c;这些行…

医疗资源联动,广州长泰医院与海南德雅医院共筑地贫防治新篇章

​ 为贯彻落实"健康中国"战略关于出生缺陷综合防治的部署要求&#xff0c;推动地中海贫血防治体系建设。2025年3月15日&#xff0c;广州长泰医院与海南德雅医院联合主办的“地中海贫血生殖遗传干预大型义诊暨合作签约仪式”在广州正式启动&#xff0c;活动以“爱与希…

DeepSeek在医学领域的应用

DeepSeek作为高性能AI大模型&#xff0c;在医学领域的应用场景广泛&#xff0c;结合其在数据处理、自然语言理解和深度学习方面的优势&#xff0c;显著推动了医疗行业的智能化转型。以下是其核心应用场景及具体案例&#xff1a; 1. 辅助诊断与决策支持 临床辅助诊断&#xff1…

3.数据结构-串、数组和广义表

串、数组和广义表 3.1串3.1.1串的类型定义、存储结构及其运算串的顺序存储串的堆式顺序存储结构串的链式存储 3.1.2 串的模式匹配算法BF算法*KMP算法&#xff08;待更新&#xff09; 3.2数组3.2.1数组的顺序存储3.2.2特殊矩阵的压缩存储对称矩阵三角矩阵对角矩阵 3.3广义表*案例…

苹果电脑杀毒软件CleanMyMac

杀毒软件在苹果家族中是一个小众软件&#xff0c;百度搜索苹果电脑杀毒软件&#xff0c;可能各种杀软良莠不齐&#xff0c;因为在这个市场非常小&#xff0c;绝大多数都是冲着“清理”去的&#xff0c;而不是杀毒。最近测试了一款Mac电脑杀毒软件&#xff0c;杀毒效果也是一般般…

Day16:二叉搜索树和双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表 。 对于双向循环列表&#xff0c;你可以将左右孩子指针作为双向循环链表的前驱和后继指针&#xff0c;第一个节点的前驱是最后一个节点&#xff0c;最后一个节点的后继是第一个节点。 特别地&#xff0c;我们希望可以…

Qt QML实现弹球消砖块小游戏

前言 弹球消砖块游戏想必大家都玩过&#xff0c;很简单的小游戏&#xff0c;通过移动挡板反弹下落的小球&#xff0c;然后撞击砖块将其消除。本文使用QML来简单实现这个小游戏。 效果图&#xff1a; 正文 代码目录结构如下&#xff1a; 首先是小球部分&#xff0c;逻辑比较麻…

如何在实际应用中测量和调整直线导轨的预紧力?

在实际应用中&#xff0c;准确测量和调整直线导轨的预紧力对于保证设备的性能和精度至关重要&#xff0c;但测量和调整直线导轨的预紧力需要根据具体的导轨型号和结构来选择合适的方法。以下是一些常见的测量和调整方法&#xff1a; 1、使用压力传感器&#xff1a;在一些先进的…

YOLO11 使用入门

YOLO12 使用入门 1. 源码下载2. 权重下载3. 环境配置4. 例程测试4.1. 目标检测4.1.1. 源文件 model4.1.2. 结果Results4.1.3. 边界框 Boxes 2.2. 图像分割4.2.1. 推理 model.predict4.2.2. 掩码 Masks 1. 源码下载 之前介绍了《目标检测 YOLOv5 使用入门》 现在是 2024.12.2…

汽车感性负载-智能高边钳位能量计算

随着汽车电子技术的发展&#xff0c;新的电子电气架构下&#xff0c;越来越多的执行部件在车身出现&#xff0c;比如电磁阀、风机、水泵、油泵、雨刮继电器等常用的执行器&#xff0c; 它们一般都表现为感性特点。驱动这些负载的最简单和最常见的方法是将它们连接到高边侧开关(…

基于Python+Flask+MySQL+HTML的爬取豆瓣电影top-250数据并进行可视化的数据可视化平台

FlaskMySQLHTML 项目采用前后端分离技术&#xff0c;包含完整的前端&#xff0c;以flask作为后端 Pyecharts、jieba进行前端图表展示 通过MySQL收集格列数据 通过Pyecharts制作数据图表 这是博主b站发布的详细讲解&#xff0c;感兴趣的可以去观看&#xff1a;【Python爬虫可…

七大常用智能家居协议对比

如果您不知道在项目中使用哪种智能家居通信协议&#xff0c;那么进入智能家居行业可能会很困难。如果没有合适的协议将其集成到智能家居生态系统中&#xff0c;智能家居设备将无法正常工作。否则&#xff0c;您将面临硬件和软件无法满足最终用户期望的风险。协议选择不当可能会…

996引擎-问题处理:缺失特效分割文件 ModelAtlasSplitConfigs

通常我们买的资源都是带会 ModelAtlasSplitConfigs.txt 或 sceneAtlasSplitConfigs.txt 的 但有时确实找不到的话&#xff0c;是可以用996工具生成的&#xff1a;

异步加载错误如何解决

首先是 提供两张图 如果数据过多的情况下我在所内和住家形式频繁的来回切换 导致数据展示的不一样 大家是不是有这样的问题 这个是导致了数据展示有问题的情况 住家的情况本来是没有几层的 下面我帮大家解决一下 // 防止异步延迟 const Noop () > { } const lhl (resDa…

R语言零基础系列教程-01-R语言初识与学习路线

代码、讲义、软件回复【R语言01】获取。 R语言初识 R是一个开放的统计编程环境&#xff0c;是一门用于统计计算和作图的语言。“一切皆是对象”&#xff0c;数据、函数、运算符、环境等等都是对象。易学&#xff0c;代码像伪代码一样简洁&#xff0c;可读性高强大的统计和可视…