通用同步异步收发器(USART)

USART介绍

  • 全双工的异步通信。
  • 接口通过三个引脚与其他设备连接在一起(接收RX、发送TX、时钟输出CK)(任何USART双向通信至少需要RX和TX)
  • 发送方为同步传输提供时钟。
  • 发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。
  • 波特率

    • 使用分数波特率发生器系统
    • 发送和接收共用的可编程波特率,最高达4.5Mbits/s
    • 12位整数和4位小数的表示方法。
    • 只有USART1使用PCLK2(即APB2最高72MHz)。其它USART使用PCLK1(即APB1最高36MHz)。
  • 可编程数据字长度(8位或9位)(8数据+n停止位/9数据+n停止位)(数据位最后一个可能是奇偶校验位)(数据最低有效位在前)
  • 可配置的停止位-支持1或2个停止位。
  • 发送和接收数据流程:(数据寄存器USART_DR包含发送寄存器TDR和接收寄存器RDR)

    • 发送数据:系统进行写操作将数据写入发送寄存器TDR,然后TDR将数据移送到发送移位寄存器中,再由发送移位寄存器送入编解码模块中编码后从TX输出。
    • 接收数据;数据从RX进入,先到编解码模块中解码后送入接收移位寄存器,然后再到接收寄存器RDR中供系统进行读操作。
  • 当发送使能位(TE)被设置时,发送移位寄存器中的数据在TX脚上输出,相应的时钟脉冲在CK脚上输出。
  • 注意:在数据传输期间不能复位TE位,否则将破坏TX脚上的数据,因为波特率计数器停止计数。正在传输的当前数据将丢失。

分数波特率的计算

  • 波特率 = PCK /(16*USARTDIV)(USARTDIV是波特比率寄存器中的值换算得到,即分频系数)
  • 已知USARTDIV = 25.62;求波特比率寄存器USART_BRR该写入的值。

    • DIV_Fraction = 16*0.62 = 9.92。最接近的整数是:10 = 0xA
    • DIV_Mantissa = 取整(25.620) = 25 = 0x019。(整数部分是12位所以是0x019,写0x19也行,这里是为了好辨别)、
    • 所以USART_BRR = 0x019A(写作0x19A也行)

USART的中断请求

  • USART的各种中断事件被连接到同一个中断向量
  • 以下各种情况会触发中断:(如果设置了对应的使能控制位,这些事件就可以产生各自的中断)

    • 发送期间:发送完成、清除发送、发送数据寄存器为空。
    • 接收期间:空闲总线检测、溢出错误、接收数据寄存器非空、校验错误、LIN断开符号检测、噪音标志(仅在多缓冲器通信)和帧错误(仅在多缓冲器通信)。

USART寄存器

状态寄存器(USART_SR)

  • 复位值:0x00C0
  • TXE:发送寄存器DTR是否为空标志位。

    • 当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。
    • 如果控制寄存器USART_CR1中的TXEIE为1,则产生中断。
    • 对数据寄存器USART_DR的写操作,硬件会将该位清零。
    • 1:数据已经被转移到移位寄存器。
  • TC:发送完成标志位。

    • 当一帧数据发送完成后,并且TXE=1时,该位被硬件置一。
    • 如果控制寄存器USART_CR1中的TXEIE为1,则产生中断。
    • 由软件序列清除该位(先读USART_SR,然后写入USART_DR)
    • 1:发送完成。
  • RXNE:检查接收移位寄存器是否为空标志位。

    • 当接收移位寄存器中的数据被转移到接收数据寄存器RDR中时,该位被硬件置位(接收移位寄存器中为空)
    • 如果控制寄存器USART_CR1中的RXNEIE为1,则产生中断。
    • 对数据寄存器USART_DR的读操作可以将该位清零。
    • 1:收到数据,可以读出。
  • IDLE:监测到总线空闲标志位。

    • 当检测到总线空闲时,该位被硬件置位。
    • 如果USART_CR1中的IDLEIE为1,则产生中断。
    • 由软件序列清除该位(先读USART_SR,然后读USART_DR)。
    • 1:检测到空闲总线。

数据寄存器(USART_DR)

  • 复位值:不确定
  • DR:数据值,包含了发送或接收的数据。

波特比率寄存器(USART_BRR)

  • 复位值:0x0000
  • DIV_Mantissa:USARTDIV的整数部分
  • DIV_Fraction:USARTDIV的小数部分

控制寄存器 1(USART_CR1)

  • 复位值:0x0000
  • UE:USART使能位。该位由软件设置和清零。(0:USART分频器和输出被禁止)
  • M:设置传输字长,由软件对其设置和清零(0:8个数据位。1:9个数据位)
  • PCE:检验控制使能(1:使能校验控制)
  • PS:校验选择(0:偶校验)
  • PEIE:PE中断使能(1:当USART_SR中的TXE为1时,产生USART中断)
  • TCIE:发送完成中断使能(1:当USART_SR中的TC为1时,产生USART中断)
  • RXNEIE:接收移位寄存器为空的中断使能(1:当USART_SR中的ORE或者RXNE为1时,产生USART中断)
  • IDLEIE:总线空闲IDLE中断使能(该位由软件设置或清除)(1:当USART_SR中的IDLE为1时,产生USART中断)
  • TE:发送使能(1:使能发送)
  • RE:接收使能(1:使能接收,并开始搜寻RX引脚上的起始位)

控制寄存器 2(USART_CR2)

  • 复位值:0x0000
  • STOP:停止位(UART4和UART5不能用0.5停止位和1.5停止位)

    • 00:1个停止位。
    • 01:0.5个停止位。
    • 10:2个停止位。
    • 11:1.5个停止位
  • CLKEN:时钟使能(0:禁止CK引脚)

代码示例

  • USART初始化设置

/*******************************************************************************
* 函 数 名         : USART1_Init
* 函数功能           : USART1初始化函数
* 输    入         : bound:波特率
* 输    出         : 无
*******************************************************************************/
void USART1_Init(u32 bound)
{
   //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;    //声明GPIO对象
    USART_InitTypeDef USART_InitStructure;    //声明串口对象
    NVIC_InitTypeDef NVIC_InitStructure;    //声明嵌套断的对象

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);


    /*  配置GPIO的模式和IO口 */
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX               //串口输出PA9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;        //复用推挽输出
    GPIO_Init(GPIOA,&GPIO_InitStructure);  /* 初始化串口输出PA9*/

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX             //串口输入PA10
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;          //模拟输入
    GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入PA10*/


   //USART1 初始化设置
    USART_InitStructure.USART_BaudRate = bound;//波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;    //收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口1

    USART_Cmd(USART1, ENABLE);  //使能串口1

    USART_ClearFlag(USART1, USART_FLAG_TC);  //清除串口的挂起标志

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;        //串口1中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //抢占优先级3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);    //根据指定的参数初始化VIC寄存器、
}

  • 串口的中断函数


/*******************************************************************************
* 函 数 名         : USART1_IRQHandler
* 函数功能           : USART1中断函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void USART1_IRQHandler(void)                    //串口1中断服务程序
{
    u8 r;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
    {
        r =USART_ReceiveData(USART1);//(USART1->DR);    //读取接收到的数据
        USART_SendData(USART1,r);          //    将接收到的数据再发送回去
        while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
    }
    USART_ClearFlag(USART1,USART_FLAG_TC);
}

I2C通信之EEPROM

I2C特点

  • 遵循着与51单片机相同的I2C协议。
  • 接口通过数据引脚(SDA)和时钟引脚(SCL)连接到I2C总线(数据引脚SDA用于数据传输,时钟引脚SCL用于收发数据的同步)
  • I2C模块接收和发送数据,并将数据从串行转换成并行,或并行转换成串行。
  • I2C主设备功能:

    • 产生时钟。
    • 产生起始和停止信号。
  • I2C从设备功能:

    • 可编程的I2C地址检测。
    • 可响应2个从地址的双地址能力。
    • 停止位检测。
  • 支持不同的通讯速度:

    • 标准速度(高达100 kHz)
    • 快速(高达400 kHz)
  • 状态标志:

    • 发送器/接收器模式标志。
    • 字节发送结束标志。
    • I2C总线忙标志。
  • 错误标志:

    • 主模式时的仲裁丢失。
    • 地址/数据传输后的应答(ACK)错误。
    • 检测到错位的起始或停止条件。
    • 禁止拉长时钟功能时的上溢或下溢。
  • 拥有2个中断向量:

    • 1个中断用于地址/数据通讯成功。
    • 1个中断用于错误。

    I2C通信流程

    1.主机发送I2C起始信号(通过SDA线)并产生时钟信号,告诉从机要开始传输信号了(所有从机都会收到这个起始信号)
    2.此时主机会广播从机地址(每个从机地址唯一)(跟在起始条件之后的1个或2个字节就是地址),匹配到后与之对应的从机地址会给出一个应答信号。
    3.在发送从地址时,设备也会告知从设备是发送数据还是接收数据(TRA位标志主设备是在接收器模式还是发送器模式)
    4.在7位地址模式时:

    • 进入发送数据模式:主设备发送从地址时最低位为’0’。
    • 进入接收数据模式:主设备发送从地址时最低位为’1’。

    5.在10位地址模式时:

    • 进入发送数据模式:主设备先送头字节(11110xx0),然后送最低位为’0’的从地址(这里xx代表10位地址中的最高2位)
    • 进入接收数据模式:主设备先送头字节(11110xx0),然后送最低位为’1’的从地址。然后再重新发送一个开始条件,后面跟着头字节(11110xx1)(这里xx代表10位地址中的最高2位)

    6.从器件地址约定:

    • 8位从器件地址的高4位固定为1010,接下来的3位表示要读写存储器的地址,最后一位为读写控制位(1表示读)

    7.如果从机答应的话就可以进行通信了。
    8.在停止通信时,主机会发送停止信号,告诉从机到此结束。

I2C模式选择

  • 四种运行模式:(该模块默认地工作于从模式)

    • 从发送器模式
    • 从接收器模式
    • 主发送器模式
    • 主接收器模式
    • 注意:接口在生成起始条件后自动地从从模式切换到主模式;当仲裁丢失或产生停止信号时,则从主模式切换到从模式。
  • 数据传输流程:串行数据传输总是以起始条件开始并以停止条件结束,并且在一个字节传输的8个时钟后的第九个时钟,接收器必须回应一个应答信号(ACK)给发送器。当通过START位在总线上产生了起始条件,设备就进入了主模式。(设备默认从模式)

I2C中断请求

  • SB、ADDR、ADD10、STOPF、BTF、RxNE和TxE通过逻辑或使用同一个中断通道(it_event事件中断)
  • BERR、ARLO、AF、OVR、PECERR、TIMEOUT和SMBALERT通过逻辑或使用同一个中断通道(it_error错误中断)

I2C寄存器描述

控制寄存器1 (I2C_CR1)

  • 复位值:0x0000
  • SWRST:软件复位

    • 当被置位时,I2C处于复位状态。在复位该位前确信I2C的引脚被释放,总线是空的
    • 0:I2C模块不处于复位状态
    • 1: I2C模块处于复位状态
  • ACK:应答使能

    • 软件可以设置或清除该位,或当PE=0时,由硬件清除
    • 0:无应答返回
    • 1:在接收到一个字节后返回一个应答(匹配的地址或数据)
  • STOP:停止条件产生

    • 软件可以设置或清除该位
    • 当检测到停止条件时,由硬件清除
    • 当检测到超时错误时,硬件将其置位
    • 在主模式下:1:在当前字节传输或在当前起始条件发出后产生停止条件
    • 在从模式下:1:在当前字节传输或释放SCL和SDA线后产生停止条件
    • 注意:当设置了STOP、START或PEC位,在硬件清除这个位之前,软件不要执行任何对I2C_CR1的写操作;否则有可能会第2次设置STOP、START或PEC位
  • START:起始条件产生

    • 软件可以设置或清除该位
    • 当起始条件发出后或PE=0时,由硬件清除
    • 在主模式下:1:重复产生起始条件
    • 在从模式下:1:当总线空闲时,产生起始条件
  • ENGC:广播呼叫使能(1:允许广播呼叫. 以应答响应地址00h)
  • ENPEC:PEC使能(ENPEC:PEC使能)
  • ENARP:ARP使能

    • 1:使能ARP
    • 如果SMBTYPE=0,使用SMBus设备的默认地址
    • 如果SMBTYPE=1,使用SMBus的主地址
  • SMBTYPE:SMBUS类型(0:SMBus设备;1:SMBUS主机)
  • SMBUS:SMBUS模式(0:I2C模式;1:SMBUS模式)
  • PE:I2C模块使能(1:启用I2C模块,根据SMBUS位的设置,相应的I/O口需配置为复用功能)

控制寄存器 2(I2C_CR2)

  • 复位值:0x0000
  • LAST: DMA最后一次传输(1:下一次DMA的EOT是最后的传输)
  • DMAEN:DMA请求使能(1:当TxE=1或RxNE =1时,允许DMA请求)
  • ITBUFEN:缓冲器中断使能(1:当TxE=1或RxNE=1时,产生事件中断,不管DMAEN是何种状态)
  • ITEVTEN:事件中断使能(1:允许事件中断)
  • ITERREN:出错中断使能(1:允许出错中断)
  • FREQ[5:0]:I2C模块时钟频率(必须设置正确的输入时钟频率产生正确的时序,范围在2~36MHz之间)

    • 000000:禁用
    • 000001:禁用
    • 000010:2MHz
    • .......
    • 100100:36MHz
    • 大于100100:禁用

自身地址寄存器 1(I2C_OAR1)

  • 复位值:0x0000
  • ADDMODE:寻址模式(在从模式中)

    • 0:7位从地址(不响应10位地址)
    • 1:10位从地址(不响应7位地址)
  • 位14:必须始终由软件保持为1。
  • ADD[9:8]:接口地址

    • 7位地址模式时不用关心
    • 10位地址模式时为地址的9~8位
  • ADD[7:1]:接口地址(地址的7~1位)
  • ADD0:接口地址

    • 7位地址模式时不用关心
    • 10位地址模式时为地址第0位

数据寄存器(I2C_DR)

  • 复位值:0x0000
  • DR:8位数据寄存器

    • 用于存放接收到的数据或放置用于发送到总线的数据

状态寄存器 1(I2C_SR1)

  • 复位值:0x0000
  • SMBALERT:SMBus提醒
  • TIMEOUT:超时或Tlow错误
  • PECERR:在接收时发生PEC错误
  • OVR:过载/欠载
  • AF:应答失败
  • ARLO:仲裁丢失(主模式)
  • BERR:总线出错
  • TxE:数据寄存器为空(发送时)
  • RxNE:数据寄存器非空(接收时)
  • STOPF:停止条件检测位(从模式)
  • ADD10:10位头序列已发送(主模式)
  • BTF:字节发送结束
  • ADDR:地址已被发送(主模式)/地址匹配(从模式)
  • SB:是否发送起始条件(主模式)

状态寄存器 2 (I2C_SR2)

  • 复位值:0x0000
  • PEC[7:0]:数据包出错检测
  • DUALF:双标志(从模式)
  • SMBHOST: SMBus主机头系列(从模式)
  • SMBDEFAULT:SMBus设备默认地址(从模式)
  • GENCALL:广播呼叫地址(从模式)
  • TRA:数据是否发送/接收
  • BUSY:总线忙
  • MSL:主从模式

时钟控制寄存器(I2C_CCR)

  • 复位值:0x0000
  • 注意:

    • 要求FPCLK1应当是10 MHz的整数倍,这样可以正确地产生400KHz的快速时钟
    • CCR寄存器只有在关闭I2C时(PE=0)才能设置
  • F/S:I2C主模式选项
  • DUTY:快速模式时的占空比
  • CCR:快速/标准模式下的时钟控制分频系数(主模式)

代码示例

  • I2C代码
#include "iic.h"
#include "SysTick.h"

//IO操作函数
#define IIC_SCL    PBout(10) //SCL
#define IIC_SDA    PBout(11) //SDA
#define READ_SDA   PBin(11)  //输入SDA


/*******************************************************************************
* 函 数 名         : IIC_Init
* 函数功能           : IIC初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC,ENABLE);

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;         //指定PB10作为SCL线
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB,&GPIO_InitStructure);                //初始化I/O口

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;            //指定PB11为SDA线
    GPIO_Init(GPIOB,&GPIO_InitStructure);

    IIC_SCL=1;
    IIC_SDA=1;
}

/*******************************************************************************
* 函 数 名         : SDA_OUT
* 函数功能           : SDA输出配置
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_OUT(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : SDA_IN
* 函数功能           : SDA输入配置
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void SDA_IN(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    GPIO_Init(GPIOB,&GPIO_InitStructure);
}

/*******************************************************************************
* 函 数 名         : IIC_Start
* 函数功能           : 产生IIC起始信号
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Start(void)
{
    SDA_OUT();     //SDA线输出
    IIC_SDA=1;     //SDA线拉高
    IIC_SCL=1;        //SCL线拉高
    delay_us(5);
     IIC_SDA=0;    //当CLK值为高时,SDA由高变低表示起始信号
    delay_us(6);
    IIC_SCL=0;    //钳住I2C总线,准备发送或接收数据
}

/*******************************************************************************
* 函 数 名         : IIC_Stop
* 函数功能           : 产生IIC停止信号
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Stop(void)
{
    SDA_OUT();    //sda线输出
    IIC_SCL=0;
    IIC_SDA=0;
     IIC_SCL=1;
    delay_us(6);
    IIC_SDA=1;    //当CLK值为高时,SDDA由低变高表示停止信号
    delay_us(6);
}

/*******************************************************************************
* 函 数 名         : IIC_Wait_Ack
* 函数功能           : 等待应答信号到来
* 输    入         : 无
* 输    出         : 1,接收应答失败
                     0,接收应答成功
*******************************************************************************/
u8 IIC_Wait_Ack(void)
{
    u8 tempTime=0;

    IIC_SDA=1;
    delay_us(1);
    SDA_IN();      //SDA设置为输入
    IIC_SCL=1;
    delay_us(1);
    while(READ_SDA)
    {
        tempTime++;
        if(tempTime>250)
        {
            IIC_Stop();
            return 1;
        }
    }
    IIC_SCL=0;//时钟输出0
    return 0;
}

/*******************************************************************************
* 函 数 名         : IIC_Ack
* 函数功能           : 产生ACK应答
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_Ack(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=0;
    delay_us(2);
    IIC_SCL=1;
    delay_us(5);
    IIC_SCL=0;
}

/*******************************************************************************
* 函 数 名         : IIC_NAck
* 函数功能           : 产生NACK非应答
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(2);
    IIC_SCL=1;
    delay_us(5);
    IIC_SCL=0;
}

/*******************************************************************************
* 函 数 名         : IIC_Send_Byte
* 函数功能           : IIC发送一个字节
* 输    入         : txd:发送一个字节
* 输    出         : 无
*******************************************************************************/
void IIC_Send_Byte(u8 txd)
{
    u8 t;
    SDA_OUT();
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {
        if((txd&0x80)>0) //0x80  1000 0000
            IIC_SDA=1;
        else
            IIC_SDA=0;
        txd<<=1;
        delay_us(2);   //对TEA5767这三个延时都是必须的
        IIC_SCL=1;
        delay_us(2);
        IIC_SCL=0;
        delay_us(2);
    }
}

/*******************************************************************************
* 函 数 名         : IIC_Read_Byte
* 函数功能           : IIC读一个字节
* 输    入         : ack=1时,发送ACK,ack=0,发送nACK
* 输    出         : 应答或非应答
*******************************************************************************/
u8 IIC_Read_Byte(u8 ack)
{
    u8 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;
}

  • AT24C代码
#include "24cxx.h"
#include "SysTick.h"

/*******************************************************************************
* 函 数 名         : AT24CXX_Init
* 函数功能           : AT24CXX初始化
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void AT24CXX_Init(void)
{
    IIC_Init();//IIC初始化
}

/*******************************************************************************
* 函 数 名         : AT24CXX_ReadOneByte
* 函数功能           : 在AT24CXX指定地址读出一个数据
* 输    入         : ReadAddr:开始读数的地址
* 输    出         : 读到的数据
*******************************************************************************/
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
    u8 temp=0;
    IIC_Start();
    if(EE_TYPE>AT24C16)
    {
        IIC_Send_Byte(0XA0);       //发送写命令(最低位为0,对从器件写)
        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);                 //发送器件地址并且最后一位为1,对从器件读
    IIC_Wait_Ack();
  temp=IIC_Read_Byte(0);
  IIC_Stop();//产生一个停止条件
    return temp;
}

/*******************************************************************************
* 函 数 名         : AT24CXX_WriteOneByte
* 函数功能           : 在AT24CXX指定地址写入一个数据
* 输    入         : WriteAddr  :写入数据的目的地址
                     DataToWrite:要写入的数据
* 输    出         : 无
*******************************************************************************/
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
    IIC_Start();
    if(EE_TYPE>AT24C16)
    {
        IIC_Send_Byte(0XA0);        //发送写命令
        IIC_Wait_Ack();
        IIC_Send_Byte(WriteAddr>>8);//发送高地址
    }
    else
    {
        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_ms(10);
}

/*******************************************************************************
* 函 数 名         : AT24CXX_WriteLenByte
* 函数功能           : 在AT24CXX里面的指定地址开始写入长度为Len的数据
                     用于写入16bit或者32bit的数据
* 输    入         : WriteAddr  :写入数据的目的地址
                     DataToWrite:要写入的数据
                     Len        :要写入数据的长度2,4
* 输    出         : 无
*******************************************************************************/
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{
    u8 t;
    for(t=0;t<Len;t++)
    {
        AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);
    }
}

/*******************************************************************************
* 函 数 名         : AT24CXX_ReadLenByte
* 函数功能           : 在AT24CXX里面的指定地址开始读出长度为Len的数据
                     用于读出16bit或者32bit的数据
* 输    入         : ReadAddr   :开始读出的地址
                     Len        :要读出数据的长度2,4
* 输    出         : 读取的数据
*******************************************************************************/
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{
    u8 t;
    u32 temp=0;
    for(t=0;t<Len;t++)
    {
        temp<<=8;
        temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);
    }
    return temp;
}

/*******************************************************************************
* 函 数 名         : AT24CXX_Check
* 函数功能           : 检查AT24CXX是否正常
* 输    入         : 无
* 输    出         : 1:检测失败,0:检测成功
*******************************************************************************/
u8 AT24CXX_Check(void)
{
    u8 temp;
    temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
    if(temp==0x36)return 0;
    else//排除第一次初始化的情况
    {
        AT24CXX_WriteOneByte(255,0X36);
        temp=AT24CXX_ReadOneByte(255);
        if(temp==0X36)return 0;
    }
    return 1;
}

/*******************************************************************************
* 函 数 名         : AT24CXX_Read
* 函数功能           : 在AT24CXX里面的指定地址开始读出指定个数的数据
* 输    入         : ReadAddr :开始读出的地址 对24c02为0~255
                     pBuffer  :数据数组首地址
                     NumToRead:要读出数据的个数
* 输    出         : 无
*******************************************************************************/
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{
    while(NumToRead)
    {
        *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
        NumToRead--;
    }
}

/*******************************************************************************
* 函 数 名         : AT24CXX_Write
* 函数功能           : 在AT24CXX里面的指定地址开始写入指定个数的数据
* 输    入         : WriteAddr :开始写入的地址 对24c02为0~255
                     pBuffer  :数据数组首地址
                     NumToRead:要读出数据的个数
* 输    出         : 无
*******************************************************************************/
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{
    while(NumToWrite--)
    {
        AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
        WriteAddr++;
        pBuffer++;
    }
}


03-05 23:23