MSP432自主开发笔记5:IIC通信移植与驱动AT24Cxx存储芯片

news2024/11/17 5:47:32

今日学习移植MSP432的IIC总线协议,并用此驱动AT24C02芯片实现写入以及读取的功能,然后实现打印开机复位次数的效果。

文章贴出测试工程,测试截图,测试代码~

 其实是实在看不懂MSP432有关于FLASH存储操作相关的英文手册与例程,没法实现掉电保护数据等功能,才想到用AT24C02进行代替~

AT24C02基础知识:

下图就是AT24C02,旁边是比它记性更好,更睿智的大哥:AT24C32:

它们通过IIC总线通信,可以实现数据的写入与读取,

24c02简介

24C02是一个2Kbit(0~255个字节)的串行EEPROM存储芯片,可存储256个字节数据。工作电压范围为1.8V到6.0V,具有低功耗CMOS技术,自定时擦写周期,1000000次编程/擦除周期,可保存数据100年。24C02有一个16字节的页写缓冲器和一个写保护功能。通过I2C总线通讯读写芯片数据,通讯时钟频率可达400KHz。

可以通过存储IC的型号来计算芯片的存储容量是多大;

比如24C02后面的02表示的是可存储2Kbit的数据,转换为字节的存储量为21024/8 = 256byte;

有比如24C04后面的04表示的是可存储4Kbit的数据,转换为字节的储存量为41024/8 = 512byte;以此来类推其它型号的存储空间。

IIC主机轮询设备用从机地址,读写数据用存储区地址24C02共256字节数据,那么存储区域地址就是00H-FFH

24C02有两种工作模式:

字节写入模式:

结合技术文档我认为该模式是这样工作的:首先是可以再任意的地址(0x00~0xFF)写入一个字节,也可以在某一地址连续的写入N字节,而且不需要翻页,从技术手册得知,答题时说字节写入模式下,页指针根写入数据的多少来自动增加实现翻页功能,不用自己在程序里边实现;
页写入模式

页写入模式下,手册上写着,一页可以存8字节,当存储的数据大于8时,则会覆盖先前保存的数据,例如,有16个数据 uchar data[16]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},从24C02的0x00地址开始存放,当存完一页(8个)时,第9个数据会保存在0x00,覆盖掉开始保存的1,后边的数据一次类推,这样的现象叫做“翻转”,如果想写完一页后将剩余的数据保存在下一页,页指针需要自己来设定。

  注意:

在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。

写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!

所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须延时5ms才可以.

AT24C32与AT24C02的关系:

AT24C02和AT24C32都是EEPROM(Electrically Erasable Programmable Read-Only Memory)芯片,用于存储数据。它们的主要区别在于存储容量和寻址能力。

AT24C02是一种2K位的EEPROM,存储容量为256字节(每个字节包含8位)。它可以支持8位地址总线,因此可以寻址256个不同的存储位置。

AT24C32是一种32K位的EEPROM,存储容量为4千字节(每个字节包含8位)。它可以支持16位地址总线,因此可以寻址65,536个不同的存储位置,比AT24C02大了256倍。

因此,AT24C32相对于AT24C02有更大的存储容量和更大的寻址能力。这意味着AT24C32可以存储更多的数据,并且可以更灵活地访问不同的存储位置。然而,AT24C32也更昂贵一些。(差不多贵不到五毛钱哈哈哈)

IIC通信代码移植:

以下为我移植的IIC通信代码,用软件方式模拟:

#include "IIC.h"

/// @brief iic初始化
/// @param iic 
/// @author wyy
void I2c_Init(soft_iic_struct *iic)
{
    gpio_init(iic->scl,GPO,1);
    gpio_init(iic->sda,GPO,1);
}


/// @brief iic起始信号
/// @param iic 
/// @author wyy
void I2c_Start(soft_iic_struct *iic)
{
    gpio_init(iic->scl,GPO,1);
    gpio_init(iic->sda,GPO,1);

    delay_us(2);

    gpio_set(iic->sda,0); // START:when CLK is high,DATA change form high to low
    delay_us(2);
    gpio_set(iic->scl,0);
}

// 产生IIC停止信号
void I2c_Stop(soft_iic_struct *iic)
{
    gpio_set(iic->scl,0);
    gpio_init(iic->sda,GPO,0);// STOP:when CLK is high DATA change form low to high
    delay_us(2);
    gpio_set(iic->scl,1);
    gpio_set(iic->sda,1); // 发送I2C总线结束信号
    delay_us(2);
}

// 等待应答信号到来
// 返回值:1,接收应答失败
//         0,接收应答成功
u8 I2c_WaitAck(soft_iic_struct *iic) // 测数据信号的电平
{
    u8 ucErrTime = 0;
    gpio_init(iic->sda,GPI,0);// 切换为输入模式

    gpio_set(iic->scl,1);
    delay_us(2);
    gpio_set(iic->sda,1);
    delay_us(2);
    while (gpio_get_level(iic->sda))
    {
        ucErrTime++;
        if (ucErrTime > 250)
        {
            I2c_Stop(iic);
            return 1;
        }
    }
    gpio_set(iic->scl,0);
    return 0;
}


/// @brief 产生ACK应答
/// @param iic 
/// @author wyy
void I2C_Ack(soft_iic_struct *iic)
{
    gpio_set(iic->scl,0);
    gpio_init(iic->sda,GPO,0); // sda输出模式
    delay_us(2);
    gpio_set(iic->scl,1);
    delay_us(2);
    gpio_set(iic->scl,0);
}

/// @brief 不产生ACK应答
/// @param iic 
/// @author wyy
void I2C_NAck(soft_iic_struct *iic)
{
    gpio_set(iic->scl,0);
    gpio_init(iic->sda,GPO,1); // sda输出模式
    delay_us(2);
    gpio_set(iic->scl,1);
    delay_us(2);
    gpio_set(iic->scl,0);
}


/// @brief 写入一个字节
/// @param iic 通信对象
/// @param dat 写入的数据
/// @author wyy
void I2C_Send_Byte(soft_iic_struct *iic,uint8_t dat)
{
    u8 i;
    gpio_init(iic->sda,GPO,1);
    gpio_set(iic->scl,0);
    delay_us(2);
    for (i = 0; i < 8; i++)
    {
        if (dat & 0x80) // 将dat的8位从最高位依次写入
        {
            gpio_set(iic->sda,1);
            delay_us(2);
        }
        else
        {
            gpio_set(iic->sda,0);
            delay_us(2);
        }
        dat <<= 1;
        gpio_set(iic->scl,1); // 将时钟信号设置为高电平,sda不允许变化
        delay_us(4);
        gpio_set(iic->scl,0);
        delay_us(2);
    }
    delay_us(2);
}



/// @brief 读1个字节,ack=1时,发送ACK,ack=0,发送nACK
/// @param iic 通信对象
/// @param ack 是否发送ACK
/// @return 读到的字节\
/// @author wyy
u8 I2C_Read_Byte(soft_iic_struct *iic,u8 ack)
{
    u8 i = 0;
    u8 dat = 0;
    gpio_init(iic->sda,GPI,0); // SDA设置为输入
    for (i = 0; i < 8; i++)
    {
        gpio_set(iic->scl,0);
        delay_us(2);
        gpio_set(iic->scl,1);
        dat <<= 1;
        if (gpio_get_level(iic->sda))
            dat++;
        delay_us(2);
    }
    if (!ack)
    {
        I2C_NAck(iic);
    }
    else
    {
        I2C_Ack(iic);
    }
    return dat;
}



/// @brief IIC写指定设备 指定寄存器的一个值
/// @param iic 通信对象
/// @param Addr 目标设备地址
/// @param reg 目标寄存器
/// @param dat 写入的数据
/// @return 
/// @author wyy
u8 I2C_Write_data(soft_iic_struct *iic,u8 Addr, u8 reg, u8 dat)
{
    I2c_Start(iic);
    I2C_Send_Byte(iic,(Addr << 1) | 0); // 发送器件地址+写命令
    if (I2c_WaitAck(iic))
    {
        I2c_Stop(iic);
        return 1;
    }
    I2C_Send_Byte(iic,reg); // 写寄存器地址
    I2c_WaitAck(iic);      // 等待应答
    I2C_Send_Byte(iic,dat); // 发送数据
    if (I2c_WaitAck(iic))  // 等待ACK
    {
        return 1;
    }
    I2c_Stop(iic);
    return 0;
}


/// @brief IIC连续读
/// @param iic 
/// @param addr 器件地址
/// @param reg 要读取的寄存器地址
/// @param len 要读取的长度
/// @param buf 读取到的数据存储区
/// @return 0,正常,其他,错误代码
u8 I2C_Read_Len(soft_iic_struct *iic,u8 addr, u8 reg, u8 len, u8 *buf)
{
    I2c_Start(iic);
    I2C_Send_Byte(iic,(addr << 1) | 0); // 发送器件地址+写命令
    if (I2c_WaitAck(iic))              // 等待应答
    {
        I2c_Stop(iic);
        return 1;
    }
    I2C_Send_Byte(iic,reg); // 写寄存器地址
    I2c_WaitAck(iic);      // 等待应答
    I2c_Start(iic);
    I2C_Send_Byte(iic,(addr << 1) | 1); // 发送器件地址+读命令
    I2c_WaitAck(iic);                  // 等待应答
    while (len)
    {
        if (len == 1)
            *buf = I2C_Read_Byte(iic,0); // 读数据,发送nACK
        else
            *buf = I2C_Read_Byte(iic,1); // 读数据,发送ACK
        len--;
        buf++;
    }
    I2c_Stop(iic); // 产生一个停止条件
    return 0;
}

u8 I2c_Write_Len(soft_iic_struct *iic,u8 addr,u8 reg,u8 len,u8 *buf)
{
	u8 i; 
    I2c_Start(iic);
	I2C_Send_Byte(iic,(addr<<1)|0);//发送器件地址+写命令	iic地址是7位+读写(0/1)操作位
	if(I2c_WaitAck(iic))	//等待应答
	{
		I2c_Stop(iic);	 
		return 1;		
	}
    I2C_Send_Byte(iic,reg);	//写寄存器地址
    I2c_WaitAck(iic);		//等待应答
	for(i=0;i<len;i++)
	{
		I2C_Send_Byte(iic,buf[i]);	//发送数据
		if(I2c_WaitAck(iic))		//等待ACK
		{
			I2c_Stop(iic); 
			return 1;		 
		}		
	}    
    I2c_Stop(iic);
	return 0;	
} 

#ifndef _IIC_h_
#define _IIC_h_

#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
#include "delay.h"

#define sda  BITBAND_PERI(P1OUT,6)
#define scl  BITBAND_PERI(P1OUT,7)


void init_SCL_SDA(void);                    //初始化总线引脚        

//以下为IIC必要的软件模拟:

static void I2C_Delay(unsigned char n);
void I2CStart(void);
void I2CStop(void);
void I2CSendByte(unsigned char byt);
unsigned char I2CReceiveByte(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(unsigned char ackbit);



#endif

AT24C02_EEPROM操作函数的编写:


/*******************************************************************************
* 函 数 名         : at24c02_write_one_byte
* 函数功能		   : 在AT24CXX指定地址写入一个数据
* 输    入         : addr:写入数据的目的地址
                     dat:要写入的数据
* 输    出         : 无
*******************************************************************************/
void at24c02_save_one_byte(u8 addr, u8 dat)
{
    I2c_Start(&at24c02);
    I2C_Send_Byte(&at24c02, 0XA0); // 发送写命令
    I2c_WaitAck(&at24c02);
    I2C_Send_Byte(&at24c02, addr); // 发送写地址
    I2c_WaitAck(&at24c02);
    I2C_Send_Byte(&at24c02, dat); // 发送写地址
    I2c_WaitAck(&at24c02);
    I2c_Stop(&at24c02);
}

/*******************************************************************************
 * 函 数 名         : at24c02_read_one_byte
 * 函数功能		   : 在AT24CXX指定地址读出一个数据
 * 输    入         : addr:开始读数的地址
 * 输    出         : 读到的数据
 *******************************************************************************/
u8 at24c02_read_one_byte(u8 addr)
{
    u8 temp = 0;
    I2c_Start(&at24c02);
    I2C_Send_Byte(&at24c02, 0XA0); // 发送写命令
    I2c_WaitAck(&at24c02);
    I2C_Send_Byte(&at24c02, addr); // 发送写地址
    I2c_WaitAck(&at24c02);
    I2c_Start(&at24c02);
    I2C_Send_Byte(&at24c02, 0XA1); // 进入接收模式
    I2c_WaitAck(&at24c02);
    temp = I2C_Read_Byte(&at24c02, 0); // 读取字节
    I2c_Stop(&at24c02);                // 产生一个停止条件
    return temp;                       // 返回读取的数据
}

主函数测试:

在主函数对移植代码测试,按下复位键储存开机次数:

#include "main.h"

char i=0;

int main(void)
{
	  inint_all();   //初始化所有模块
		i=at24c02_read_one_byte(0x02);
//		i=0;
		printf("i=%d\r\n",i);
		i++;
		at24c02_save_one_byte(0x02,i);
    while (1)
    {

    }
}

//初始化所有模块
void inint_all(void)
{
    SysInit();                                  //时钟配置    
    delay_init();								 								//delay_ms函数配置
		uart_init(115200);	
		At24c02_Init();
	  printf("Hello,MSP432!\r\n");								//串口打印测试字符
		MAP_Interrupt_enableMaster();               // 开启总中断
}

测试效果:

图中我手中的板子是淘宝购买的MSP432P401R红色Launch_pad仿制版,全部功能,全部代码实现与红板子一样,只是便宜以及下载程序不方便:

测试工程下载:

文章只贴出关键代码,全部代码在测试工程提供下载:

https://download.csdn.net/download/qq_64257614/88114613

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

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

相关文章

怎么学习Java并发编程相关技术? - 易智编译EaseEditing

学习Java并发编程可以通过多种方式进行&#xff0c;包括但不限于以下几种&#xff1a; 在线教程和学习平台&#xff1a; 网上有许多免费和付费的Java并发编程教程和学习平台&#xff0c;如Coursera、Udemy、edX、Codecademy等。这些平台提供结构化的课程和练习&#xff0c;适…

Goby 漏洞发布|Metabase JDBC 远程代码执行漏洞(CVE-2023-38646)

漏洞名称&#xff1a;Metabase JDBC 远程代码执行漏洞&#xff08;CVE-2023-38646&#xff09; English Name&#xff1a;Metabase JDBC Remote Code Execution Vulnerability (CVE-2023-38646) CVSS core: 9.8 影响资产数&#xff1a;66604 漏洞描述&#xff1a; Metabas…

初识计算机系统

计算机系统是由硬件和系统软件组成的&#xff0c;它们共同工作来运行应用程序。虽然系统的具体实现方式随着时间不断变化&#xff0c;但是系统内在的概念却没有改变。所有计算机系统都有相似的硬件和软件组件&#xff0c;它们又执行着相似的功能。 一、信息就是位上下文 我们通…

Python基础入门教程(上)

目录 一、你好Python 1.1、Python安装 win版 Linux版 1.2、第一个Python程序 二、Python基本语法 2.1、字面量 2.2、注释 2.3、变量 2.4、数据类型 type()函数 字符串类型的不同定义方式 2.5、数据类型转换 ​编辑 2.6、标识符 2.7、运算符 2.8、字符串扩展 …

教育机构视频播放时观看行为分析有哪些应用?

教育机构视频播放时观看行为分析有哪些应用&#xff1f; 观看行为分析 观看行为分析是指我们平台基于视频大数据分析&#xff0c;能够以秒为粒度展示观众如何观看您的视频。 视频观看热力图是单次观看行为的图形化表示&#xff0c;我们平台云点播视频的每一次播放&#xff0…

脚手架 --- command框架<一>

版本&#xff1a;6.0.0 假设脚手架名称&#xff1a;big-cat-cli 实例化 const commander require(commander) const program new commander.Command()program 基本信息配置 program.name(Object.keys(pkg.bin)[0]) // 赋值name, 显示在useage 前部分.usage(<command>…

产品开发八大模块交流︱奇瑞新能源汽车产品开发院院长荣升格

奇瑞新能源汽车股份有限公司研发中心/产品开发院院长荣升格先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾&#xff0c;演讲议题&#xff1a;产品开发八大模块交流。大会将于8月12-13日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&#xff1a; VUCA时代…

树和二叉树 --- 数据结构

目录 1.树的概念及结构 1.1树的概念 1.2树的表示 1.3树在实际生活中的运用 2.二叉树的概念及结构 2.1概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 1.树的概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n (n>0)个有限结点组成…

路由策略(重发布)

要求&#xff1a; 1、使用双点双向重发布 2、所有路由器进行最佳选路 3、存在备份路径&#xff0c;不得出现环路&#xff0c;和路由回馈 1.更改设备名称配置接口IP地址 R1 <Huawei>system-view [Huawei]sysname R1 [R1]interface GigabitEthernet 0/0/0 [R1-GigabitEt…

Elasticsearch 整合springboot-Elasticsearch文章二

文章目录 官网版本组件版本说明实现代码地址pom.xmlapplication.ymlRepositoryVisitLog模型定义controller使用测试http请求结果kibana结果ID外传 官网 https://www.elastic.co/cn/ 版本 https://docs.spring.io/spring-data/elasticsearch/docs/4.4.10/reference/html/ 我们…

[SSM]GoF之代理模式

目录 十四、GoF之代理模式 14.1对代理模式的理解 14.2静态代理 14.3动态代理 14.3.1JDK动态代理 14.3.2CGLIB动态代理 十四、GoF之代理模式 14.1对代理模式的理解 场景&#xff1a;拍电影的时候&#xff0c;替身演员去代理演员完成表演。这就是一个代理模式。 演员为什…

爬虫005_python类型转换_其他类型转换为整型_转换为Float类型_转换为字符串_转换为布尔值---python工作笔记023

首先来看,字符串转换成int 很简单 float转换成int 会把小数点后面的内容丢掉 boolean转换为int true是1 false 是0 然后字符串转换为int,要注意 不能有特殊字符比如1.23 中有点 就报错 上面字符串12ab,有ab也报错 看上面

向量与流迭代器(istream_iterator和ostream_iterator)

运行代码&#xff1a; //向量与流迭代器 #include"std_lib_facilities.h"struct Item {string name;int iid;double value;friend istream& operator>>(istream& is, Item& ii);friend ostream& operator<<(ostream& os, const Ite…

F5 LTM 知识点和实验 4-持久化

第四章:持久化 持久化: 大多数应用都是有状态的,比如,使用一个购物网站,最重要的是用户在放入一个商品之后,刷新网页要能继续看到购物车里的东西,这就需要请求报文发到同一个后端服务器上,持久化就能完成这个功能。 持久化支持一下几种场景: 源地址目标地址SSLSIPH…

【go-zero】浅析 01

“github.com/google/uuid” uuid.New().String() go-zero 文档 https://www.w3cschool.cn/gozero/ go-zero 官网 https://go-zero.dev/ 快速开始&#xff1a; $ mkdir go-zero-demo $ cd go-zero-demo $ go mod init go-zero-demo $ goctl api new greet $ go mod tidy Done…

揭开神秘的面纱--自己实现MyBatis 底层机制[下]

&#x1f600;前言 本篇博文是MyBatis 底层机制的核心实现&#xff0c;简单的手法让MyBatis 不再神秘&#xff0c;希望能够帮助到你&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的…

JAVA-字符串生成图片

直接上代码 public static void main(String[] args) throws IOException {createFontImage("红色", new Font("宋体", Font.BOLD, 50), 400, 400);}/*** 根据str,font的样式将文字变成图片,然后返回一个流** param str 字符串* param font 字体* pa…

奇富科技引领编制国内首个金融行业大模型标准

7月28日&#xff0c;由中国信息通信研究院&#xff08;以下简称中国信通院&#xff09;组织的“行业大模型高质量发展论坛暨可信AI大模型标准宣贯会”在江苏南京召开。会议发起了行业大模型标准联合推进计划&#xff0c;启动了多个行业的大模型标准编制工作&#xff0c;其中&am…

ES6 - promise.all和race方法的用法详解

文章目录 一、前言二、Promise.all()1&#xff0c;第一句&#xff1a;Promise.all()方法接受一个数组作为参数&#xff0c;且每一个都是 Promise 实例2&#xff0c;第二句&#xff1a;如果不是&#xff0c;就会先调Promise.resolve方法&#xff0c;将参数转为 Promise 实例再进…

Python web实战 | Docker+Nginx部署python Django Web项目详细步骤【干货】

概要 在这篇文章中&#xff0c;我将介绍如何使用 Docker 和 Nginx 部署 Django Web 项目。一步步讲解如何构建 Docker 镜像、如何编写 Docker Compose 文件和如何配置 Nginx。 1. Docker 构建 Django Web 项目 1.1 配置 Django 项目 在开始之前&#xff0c;我们需要有一个 D…