【N32L40X】学习笔记13-软件IIC读写EEPROM AT24C02

news2024/9/22 11:29:00

AT24C02

  1. 8个字节每页,累计32个页

  2. 通讯频率MAX = 400K

  3. AT24C02大小 2K

在这里插入图片描述

  1. 芯片地址

在这里插入图片描述
对于at24c02 A2A1A0 这三个引脚没有使用
在这里插入图片描述

在这里插入图片描述

  1. 写时序在这里插入图片描述

由于设备在写周期中不会产生ACK恢复,因此这可用于确定周期何时完成(此特性可用于最大限度地提高总线吞吐量)。一旦从主服务器发出了写命令的停止条件,设备就会启动内部定时的写周期,然后就可以立即启动ACK轮询。这涉及到主服务器发送一个开始条件,然后是一个写命令的控制字节(R/W =0)。如果设备仍忙于写入周期,则不会返回ACK。如果循环完成,设备将返回ACK,然后主命令可以继续下一个读或写命令。该操作的流程图见图5-1。

在这里插入图片描述

  1. 读时序
    在这里插入图片描述
    在这里插入图片描述

at24c2.h

#ifndef _24CXX_H
#define _24CXX_H
#include <stdint.h>


#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

//定义EE_TYPE为AT24C16
#define EE_TYPE AT24C16

uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr);							//指定地址读取一个字节
void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite);		//指定地址写入一个字节

void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len);//指定地址开始写入指定长度的数据
uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len);					//指定地址开始读取指定长度数据

void AT24CXX_Write(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite);	//从指定地址开始写入指定长度的数据
void AT24CXX_Read(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead);   	//从指定地址开始读出指定长度的数据
void AT24CXX_Erasure(uint16_t NumToWrite);  /*从地址0开始将指定个数的地址清0*/
uint8_t AT24CXX_Check(void);  //检查器件
void AT24CXX_Init(void); //初始化IIC

#endif


at24c2.c

#include "at24c02.h"
#include "n32l40x.h"
//#include "delay.h"
//#include "debug.h"
//IO方向设置
//#define SDA_OUT {GPIOC->MODER |= 0x00040000;}  // 设置SDA为输出方向,对于双向I/O需切换为输出
//#define SDA_IN  {GPIOC->MODER &= 0xFFF3FFFF;}  // 设置SDA为输入方向,对于双向I/O需切换为输入
IO操作
//#define IIC_SCL   PAout(8) //SCL
//#define IIC_SDA   PCout(9) //SDA
//#define READ_SDA  PCin(9)  //输入SDA

#define  SDA_OUT do{\
	 GPIO_InitType GPIO_InitStructure;\
    GPIO_InitStruct(&GPIO_InitStructure);\
    GPIO_InitStructure.Pin            = GPIO_PIN_11;\
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Out_PP;\
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);}while(0);

#define  SDA_IN do{\
	 GPIO_InitType GPIO_InitStructure;\
    GPIO_InitStruct(&GPIO_InitStructure);\
    GPIO_InitStructure.Pin            = GPIO_PIN_11;\
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Input;\
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);}while(0);
//产生IIC起始信号
#define 	READ_SDA 	GPIO_ReadInputDataBit(GPIOB,GPIO_PIN_11)
#define 	IIC_SDA(x) 	GPIO_WriteBit(GPIOB,GPIO_PIN_11,x)
#define 	IIC_SCL(x) 	GPIO_WriteBit(GPIOB,GPIO_PIN_10,x)



static void IIC_Init(void);
static void IIC_Start(void);
static void IIC_Stop(void);
static uint8_t IIC_Wait_Ack(void);
static void IIC_Ack(void);
static void IIC_NAck(void);
static void IIC_Send_Byte(uint8_t txd);
static uint8_t IIC_Read_Byte(unsigned char ack);

void delay_us(int x)
{
    for(int i=0; i<x*60; i++);
}

//初始化IIC接口
void AT24CXX_Init(void)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    IIC_Init();//IIC初始化
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值  :读到的数据
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)
{
#if HW_WARE_24CXX >1
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    uint8_t temp=0;
    IIC_Start();
    if(EE_TYPE>AT24C16)
    {
        IIC_Send_Byte(0XA0);	   //发送写命令
        IIC_Wait_Ack();
        IIC_Send_Byte(ReadAddr>>8);//发送高地址
    } else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据
    IIC_Wait_Ack();
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
    IIC_Wait_Ack();
    IIC_Start();
    IIC_Send_Byte(0XA1);           //进入接收模式
    IIC_Wait_Ack();
    temp=IIC_Read_Byte(0);
    IIC_Stop();//产生一个停止条件
    return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)
{
    int page=0;
#if HW_WARE_24CXX >1
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    IIC_Start();
    if(EE_TYPE>AT24C16)
    {
        IIC_Send_Byte(0XA0);	    //发送写命令10100000
        IIC_Wait_Ack();
        IIC_Send_Byte(WriteAddr>>8);//发送高地址
    } else
    {
        page = WriteAddr/256;
#if HW_WARE_24CXX >0
        printf("%s %s %d page = %02xH\r\n",__FILE__,__FUNCTION__,__LINE__,page);
#endif

        IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据
    }
    IIC_Wait_Ack();
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
    IIC_Wait_Ack();
    IIC_Send_Byte(DataToWrite);     //发送字节
    IIC_Wait_Ack();
    IIC_Stop();//产生一个停止条件
    delay_us(100);
}
//在AT24CXX里面的指定地址开始写入长度为Len的数据
//该函数用于写入16bit或者32bit的数据.
//WriteAddr  :开始写入的地址
//DataToWrite:数据数组首地址
//Len        :要写入数据的长度2,4
void AT24CXX_WriteLenByte(uint16_t WriteAddr,uint32_t DataToWrite,uint8_t Len)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    uint8_t t;
    for(t=0; t<Len; t++)
    {
        AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
    }
}

//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr   :开始读出的地址
//返回值     :数据
//Len        :要读出数据的长度2,4
uint32_t AT24CXX_ReadLenByte(uint16_t ReadAddr,uint8_t Len)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    uint8_t t;
    uint32_t temp=0;
    for(t=0; t<Len; t++)
    {
        temp<<=8;
        temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
    }
    return temp;
}
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
uint8_t AT24CXX_Check(void)
{
#if HW_WARE_24CXX > 0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    uint8_t 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;
}

//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer  :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(uint16_t ReadAddr,uint8_t *pBuffer,uint16_t NumToRead)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    while(NumToRead)
    {
        *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
        NumToRead--;
    }
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(uint16_t WriteAddr,uint8_t *pBuffer,uint16_t NumToWrite)
{
#if HW_WARE_24CXX >1
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    while(NumToWrite--)
    {
        AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
        WriteAddr++;
        pBuffer++;
    }
}

//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Erasure(uint16_t NumToWrite)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif

    uint16_t  WriteAddr=0;
    while(NumToWrite--)
    {
        AT24CXX_WriteOneByte(WriteAddr,0);
        WriteAddr++;
    }
}


/*软件模拟IIC*/
//IIC初始化
static void IIC_Init(void)
{
#if HW_WARE_24CXX >0
    printf("%s %s %d\r\n",__FILE__,__FUNCTION__,__LINE__);
#endif


    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB,ENABLE);
    GPIO_InitType GPIO_InitStructure;

    /* Initialize GPIO_InitStructure */
    GPIO_InitStruct(&GPIO_InitStructure);

    /* Configure USARTx Tx as alternate function push-pull */
    GPIO_InitStructure.Pin            = GPIO_PIN_10;
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);
    GPIO_InitStructure.Pin            = GPIO_PIN_11;
    GPIO_InitStructure.GPIO_Mode      = GPIO_Mode_Out_PP;
    GPIO_InitPeripheral(GPIOB, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOB,GPIO_PIN_10,Bit_SET);
    GPIO_WriteBit(GPIOB,GPIO_PIN_11,Bit_SET);

}

static void IIC_Start(void)
{
    SDA_OUT;     //sda线输出
    IIC_SDA(1);
    IIC_SCL(1);
    delay_us(4);
    IIC_SDA(0);//START:when CLK is high,DATA change form high to low
    delay_us(4);
    IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}

//产生IIC停止信号
static void IIC_Stop(void)
{
    SDA_OUT;//sda线输出
    IIC_SCL(0);
    IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
    delay_us(4);
    IIC_SCL(1);
    delay_us(4);
    IIC_SDA(1);//发送I2C总线结束信号
}

//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
static uint8_t IIC_Wait_Ack(void)
{
    uint8_t ucErrTime=0;
    SDA_IN;      //SDA设置为输入
    IIC_SDA(1);
    delay_us(1);
    IIC_SCL(1);
    delay_us(1);
    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL(0);//时钟输出0
    return 0;
}

//产生ACK应答
static void IIC_Ack(void)
{
    IIC_SCL(0);
    SDA_OUT;
    IIC_SDA(0);
    delay_us(2);
    IIC_SCL(1);
    delay_us(2);
    IIC_SCL(0);
}
//不产生ACK应答
static void IIC_NAck(void)
{
    IIC_SCL(0);
    SDA_OUT;
    IIC_SDA(1);
    delay_us(2);
    IIC_SCL(1);
    delay_us(2);
    IIC_SCL(0);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
static void IIC_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SDA_OUT;
    IIC_SCL(0);//拉低时钟开始数据传输
    for(t=0; t<8; t++)
    {
        IIC_SDA((txd&0x80)>>7);
        txd<<=1;
        delay_us(2);   //对TEA5767这三个延时都是必须的
        IIC_SCL(1);
        delay_us(2);
        IIC_SCL(0);
        delay_us(2);
    }
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
static uint8_t IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN;//SDA设置为输入
    for(i=0; i<8; i++ )
    {
        IIC_SCL(0);
        delay_us(2);
        IIC_SCL(1);
        receive<<=1;
        if(READ_SDA)receive++;
        delay_us(1);
    }
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK
    return receive;
}


测试代码

    AT24CXX_Init();
    if(!AT24CXX_Check())
    {
        printf("AT24CXX_Check ok\r\n");
    }
    else
    {
        printf("AT24CXX_Check fail\r\n");
    }

    uint8_t buff[100];

    uint8_t buff1[100];
    for(int i=0; i<100; i++)
    {
        buff[i]=i;
    }

    AT24CXX_Write(0,buff,100);
    AT24CXX_Read(0,buff1,100);
    for(int i=0; i<100; i++)
    {
        printf("%02d ",buff1[i]);
    }
    printf("\r\n");

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

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

相关文章

Jmeter 压测工具使用手册[详细]

1. jemter 简介 jmeter 是 apache 公司基于 java 开发的一款开源压力测试工具&#xff0c;体积小&#xff0c;功能全&#xff0c;使用方便&#xff0c;是一个比较轻量级的测试工具&#xff0c;使用起来非常简 单。因为 jmeter 是 java 开发的&#xff0c;所以运行的时候必须先…

gitbook编译报错gitbook\gitbook-plugin-fontsettings\fontsettings.js‘

找到本地电脑gitbook安装的目录&#xff1a;cd ~/.gitbook/versions/版本/lib/output/website/ 可以用 vim copyPluginAssets.js 也可以用记事本打开注释掉或者改为false

Redis 双写一致性实践及案例

面试问题&#xff1a; 你只要用缓存&#xff0c;就可能会涉及到redis缓存与数据库双存储双写&#xff0c;你只要是双写&#xff0c;就一定会有数据一致性的问题&#xff0c;那么你如何解决一致性问题&#xff1f;双写一致性&#xff0c;你先动缓存redis还是数据库mysql哪一个&…

MySQL建表和增添改查

1.创建一个名为mydb的数据库 mysql> show database mydb; 查询 mysql> show database mydb; 2.创建一个学生信息表 mysql> create table mydb.student_informtion( -> student_id int UNSIGNED NOT NULL PRIMARY KEY, //非空&#xff08;不允许为空&#xff0…

Axure RP9中使用Echarts示例

目录 在Axure中拖入一个矩形框&#xff0c;并命名tes 进入Echarts官网示例页面https://echarts.apache.org/examples/zh/index.html 选择自己需要的图表&#xff0c;修改数据&#xff0c;并复制左侧js代码 把上面复制的代码替换下方的option{}; javascript: var script docum…

扩散模型实战(二):扩散模型的发展

推荐阅读列表&#xff1a; 扩散模型实战&#xff08;一&#xff09;&#xff1a;基本原理介绍 扩散模型从最初的简单图像生成模型&#xff0c;逐步发展到替代原有的图像生成模型&#xff0c;直到如今开启 AI 作画的时代&#xff0c;发展速度可谓惊人。下面介绍一下2D图像生成相…

浅析 C 语言的共用体、枚举和位域

前言 最近在尝试阅读一些系统库的源码&#xff0c;但是其中存在很多让我感到既熟悉又陌生的语法。经过资料查阅&#xff0c;发现是 C 语言中的共用体和位域。于是&#xff0c;趁着课本还没有扔掉&#xff0c;将一些相关的知识点记录在本文。 文章目录 前言共用体 (union)枚举…

Python冷知识-为什么切片和区间会忽略最后一个元素

在 Python 里&#xff0c;像列表&#xff08;list&#xff09;、元组&#xff08;tuple&#xff09;和字符串&#xff08;str&#xff09;这类序列类型都支持切片操作&#xff0c;但是实际上切片操作比人们所想象的要强大很多。 在切片和区间操作里不包含区间范围的最后一个元…

0805hw

1. #include <myhead.h> void Bub_sort(int *arr,int n)//冒泡排序 {for(int i1;i<n;i){int count0;for(int j0;j<n-i;j){if(arr[j]>arr[j1]){int temparr[j];arr[j]arr[j1];arr[j1]temp;count;}}if(count0){break;}}printf("冒泡排序后输出结果:\n"…

OA会议管理系统之会议通知(送审成功可查看人员是否参与会议)

一、前言 1.导读 在上一次的博文中我们实现了会议审批&#xff0c;会议通过审批后&#xff0c;状态就会由原来的待审核变为待开&#xff0c;这时候我们就可以在我的会议下的反馈详情查看人员是否参与会议的反馈情况。这篇博客就是来了解&#xff0c;人员是怎么接收会议通知并反…

介绍Keithley2602A双通道系统数字电源

吉时利Keithley2602A双通道系统数字电源 主要特点及优点 &#xff08;131///4587//6435&#xff09; 一个紧凑的单元中综合了如下功能&#xff1a;精密电压源、高精度电流源、数字多用表、任意波形发生器、电压或电流脉冲发生器、电子负载以及触发控制器 代码后向兼容2600系列…

如何在 Android 上恢复已删除的视频|快速找回丢失的记忆

想知道是否有任何成功的方法可以从 Android 手机中检索已删除的视频&#xff1f;好吧&#xff0c;本指南将向您展示分步说明&#xff0c;让您轻松从手机中找回丢失的视频文件&#xff01; 您是否不小心从 Android 智能手机中删除了珍贵的生日视频&#xff1f;难道是无处可寻吗…

PyTorch中加载模型权重 A匹配B|A不匹配B

在做深度学习项目时&#xff0c;从头训练一个模型是需要大量时间和算力的&#xff0c;我们通常采用加载预训练权重的方法&#xff0c;而我们往往面临以下几种情况&#xff1a; 未修改网络&#xff0c;A与B一致 很简单&#xff0c;直接.load_state_dict() net ANet(num_cla…

Vector - CAPL - 诊断模块函数(发送及流控制帧)

目录 CanTpSendData - 诊断数据的发送 代码示例 CanTpGetHWSTmin & CanTpSetHWSTmin - 获取和设置硬件STMin的值 代码示例 CanTpSetSTminReduction - 将STmin设置需要的值 代码示例 CanTpGetBlockSize & CanTpSetBlockSize 代码示例 CanTpGetSTmin & Can…

一文学透设计模式

设计模式是什么&#xff1f; 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案&#xff0c;代表了解决一些问题的最佳实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。 说白了&#xff0c;设计模式对于软件开发人员来说就…

一百四十四、Kettle——Linux上安装的kettle8.2连接MySQL数据库

一、目的 在Linux上安装好kettle&#xff0c;然后用kettle连接MySQL数据库 注意&#xff1a;kettle版本是8.2 二、实施步骤 &#xff08;一&#xff09;到kettle安装目录下启动Linux的kettle服务 # cd /opt/install/data-integration/ # ./spoon.sh &#xff08;二&#x…

【前端】搭建Vue3框架

目录 一、搭建准备二、node.js安装1、下载并安装2、配置默认安装目录和缓存日志目录①、创建默认安装目录和缓存日志目录&#xff08;我的node.js目录在D盘&#xff0c;所以直接在node.js文件夹下创建&#xff09;②、执行命令&#xff0c;配置默认安装目录和缓存日志目录到刚才…

OpenMesh 网格简化算法(基于边长度)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 网格简化的算法有很多种,基于边结构进行简化的方法便是其中一种方式。此类算法主要关注于它们如何选择要收缩的边,并且似乎都是为流形表面设计的,尽管边缘收缩也可以用于非流形表面,但往往会存在变形较大的问题…

2023-08-05 LeetCode每日一题(合并两个有序链表)

2023-08-05每日一题 一、题目编号 21. 合并两个有序链表二、题目链接 点击跳转到题目位置 三、题目描述 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1&#xff1a; 示例2&#xff1a; 示例3&#xff1a; …

AcWing 372. 棋盘覆盖(二分图匈牙利算法)

输入样例&#xff1a; 8 0输出样例&#xff1a; 32 解析&#xff1a; n为100&#xff0c;状压肯定爆。 将每个骨牌看成二分图的一个匹配&#xff0c;即查找二分图的一个最大匹配&#xff0c;匈牙利算法。 #include<bits/stdc.h> using namespace std; const int N105…