【STM32】标准库与HAL库对照学习教程十三--软件IIC控制AT24C02
一、前言
本篇介绍如何使用软件IIC对AT24C02进行数据的读写操作,通过标准库与HAL库的对比,希望能让您了解使用IIC控制外设的一般步骤,希望本篇文章对您能有所帮助。 |
工程配置在前面已经配了很多次了,本章不会再详细讲解工程配置。 有关IIC的原理您可以看这篇文章: 【STM32】标准库与HAL库对照学习教程特别篇–IIC通信原理讲解 本篇不会再介绍IIC的通信原理。
二、准备工作
- STM32开发板(我用的是普中的STM32F103ZE的Z200系列)
- STM32cubemx软件、keil5(MDK)
- 开发板原理图
三、AT24C02(EEPROM)介绍
1、AT24C02简介
AT24C02是EEPROM储存器的一种,其内部可存储256个字节(0xff)的数据。
在其内部有一个8字节的页面缓存区,当对AT24C02进行写数据的操作时,会将数据先放到缓存区里,再存入存储器里,因此要注意控制写入数据的间隔时间。
结构图:
2、引脚功能
3、设备地址
AT24C02器件地址为7位,高4位固定为1010,低3位由 A0/A1/A2信号线的电平决定。传输地址数据时,第八位是读写位。
四、硬件电路图
看自己开发板的原理图。
五、数据传输过程
1、写入数据
- 1、MCU先发送一个起始信号启动总线
- 2、接着,发送设备写操作地址(0xA0)。
- 3、等待应答信号(ACK)。
- 4、发送写入数据的存储地址。AT24C02一共有256个字节的存储空间,地址从0x00~0xFF。
- 5、每写一个字节的数据,AT24C02都会回应一个应答信号。
- 6、当AT24C02回应非应答信号或者主设备不在写入数据时,主设备发送结束信号停止总线。
2、读入数据
- 1、MCU先发送一个开始信号启动总线
- 2、接着,发送设备写操作地址(0xA0)。(这里的写操作是为了要先把要读的数据的存储地址先写进去,告诉AT24C02要读取哪个地址的数据。)
- 3、发送要读取内存的地址。
- 4、重新发送开始信号
- 5、发送设备读操作地址 (0xA1)。
- 6、AT24C02自动向主机发送数据,主机每接收一个字节的数据都要回应一个应答信号。
- 当主设备在读数据时,发送一个非应答信号。在发送结束信号停止总线
六、标准库控制AT24C02
1、实验程序
iic.h
#ifndef IIC_H_
#define IIC_H_
#include "stm32f10x.h"
#define DEBUG_IIC 1
#define IIC_SCL_RCC RCC_APB2Periph_GPIOB
#define IIC_SCL_Pin GPIO_Pin_6
#define IIC_SCL_Port GPIOB
#define IIC_SDA_RCC RCC_APB2Periph_GPIOB
#define IIC_SDA_Pin GPIO_Pin_7
#define IIC_SDA_Port GPIOB
#define SCL PBout(6)
#define SDA_out PBout(7)
#define SDA_in PBin(7)
void IIC_Init(void);
void IIC_start(void);
void IIC_end(void);
void IIC_ACK(void);
void IIC_NACK(void);
u8 IIC_Wait_ACK(void);
void IIC_Write_byte(u8 bit);
u8 IIC_Read_byte(u8 ACK);
#endif
iic.c
#include "iic.h"
#include "System.h"
#include "Delay.h"
#if DEBUG_IIC
#include "usart.h"
#include<stdio.h>
#endif
void IIC_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(IIC_SCL_RCC|IIC_SDA_RCC, ENABLE);
GPIO_InitStruct.GPIO_Pin = IIC_SCL_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SCL_Port, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = IIC_SDA_Pin;
GPIO_Init(IIC_SDA_Port,&GPIO_InitStruct);
SCL = 1;
SDA_out = 1;
}
void IIC_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = IIC_SDA_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_SDA_Port,&GPIO_InitStruct);
}
void IIC_IN()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = IIC_SDA_Pin;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(IIC_SDA_Port,&GPIO_InitStruct);
}
void IIC_start()
{
IIC_OUT();
SCL = 1;
SDA_out = 1;
Delay_us(5);
SDA_out = 0;
Delay_us(5);
}
void IIC_end()
{
IIC_OUT();
SCL = 0;
SDA_out = 0;
SCL = 1;
Delay_us(5);
SDA_out = 1;
Delay_us(5);
}
void IIC_ACK()
{
IIC_OUT();
SCL = 0;
SDA_out = 0;
Delay_us(2);
SCL = 1;
Delay_us(5);
SCL = 0;
}
void IIC_NACK()
{
IIC_OUT();
SCL = 0;
SDA_out = 1;
Delay_us(2);
SCL = 1;
Delay_us(5);
SCL = 0;
}
u8 IIC_Wait_ACK()
{
u8 i;
IIC_IN();
SCL = 1;
while(SDA_in)
{
i++;
if(i > 250)
{
IIC_end();
#if DEBUG_IIC
printf("未收到应答信号,IIC有误\r\n");
#endif
return 0;
}
}
SCL=0;
return 1;
}
void IIC_Write_byte(u8 byte)
{
u8 i;
IIC_OUT();
SCL = 0;
for(i = 0;i < 8;i++)
{
if(byte & 0x80)
SDA_out = 1;
else
SDA_out = 0;
byte <<= 1;
Delay_us(2);
SCL = 1;
Delay_us(2);
SCL = 0;
Delay_us(2);
}
}
u8 IIC_Read_byte(u8 ACK)
{
u8 i,Bit;
IIC_IN();
for(i = 0;i < 8;i++)
{
SCL = 0;
Delay_us(2);
SCL = 1;
Bit <<= 1;
if(SDA_in)
Bit++;
}
if(ACK)
IIC_ACK();
else
IIC_NACK();
return Bit;
}
AT24C02.h
#ifndef AT24C02_H_
#define AT24C02_H_
#include "stm32f10x.h"
#define AT24C02_Address 0xA0
#define AT24C02_Write_Address 0xA0
#define AT24C02_Read_Address 0xA1
void AT24C02_Init(void);
void AT24C02_Write_Byte(u8 Address,u8 Byte);
u8 AT24C02_Read_Byte(u8 Address);
void AT24C02_Write_Continue(u8 Address,u8 *data,u8 number);
void AT24C02_Read_Continue(u8 Address,u8 *data,u8 number);
u8 AT24C02_check(void);
void AT24C02_clear(void);
#endif
AT24C02.c
#include "AT24C02.h"
#include "iic.h"
#include "Delay.h"
void AT24C02_Init()
{
IIC_Init();
}
void AT24C02_Write_Byte(u8 Address,u8 Byte)
{
IIC_start();
IIC_Write_byte(AT24C02_Write_Address);
IIC_Wait_ACK();
IIC_Write_byte(Address%256);
IIC_Wait_ACK();
IIC_Write_byte(Byte);
IIC_Wait_ACK();
IIC_end();
Delay_ms(10);
}
u8 AT24C02_Read_Byte(u8 Address)
{
u8 Byte;
IIC_start();
IIC_Write_byte(AT24C02_Address);
IIC_Wait_ACK();
IIC_Write_byte(Address%256);
IIC_Wait_ACK();
IIC_start();
IIC_Write_byte(AT24C02_Read_Address);
IIC_Wait_ACK();
Byte = IIC_Read_byte(0);
IIC_end();
return Byte;
}
void AT24C02_Write_Continue(u8 Address,u8 *data,u8 number)
{
u8 i;
for(i = 0;i < number;i++)
{
AT24C02_Write_Byte(Address+i,*(data+i));
}
}
void AT24C02_Read_Continue(u8 Address,u8 *data,u8 number)
{
u8 i;
for(i = 0;i < number;i++)
{
*(data+i) = AT24C02_Read_Byte(Address+i);
}
}
u8 AT24C02_check()
{
u8 temp = AT24C02_Read_Byte(0xff);
if(temp == 0x38) return 1;
else
{
AT24C02_Write_Byte(0xff,0x38);
temp = AT24C02_Read_Byte(0xff);
if(temp == 0x38) return 1;
}
return 0;
}
void AT24C02_clear()
{
u8 a[256];
u32 i;
for(i = 0;i < 256;i++)
{
a[i] = 0xff;
}
AT24C02_Write_Continue(0,a,0xff);
}
2、实验效果
七、HAL库控制AT24C02
1、cubemx配置工程主要步骤
① ② ③ ④串口 ⑤引脚 ⑥
2、实验程序
程序是根据标准库移植过来的。 iic.c
#include "iic.h"
#include "System.h"
#include "Delay.h"
#if DEBUG_IIC
#include "usart.h"
#include<stdio.h>
#endif
void IIC_Init()
{
GPIO_InitTypeDef GPIO_InitStruct;
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOB, IIC_SCL_Pin|IIC_SDA_Pin, GPIO_PIN_SET);
GPIO_InitStruct.Pin = IIC_SCL_Pin|IIC_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
SCL = 1;
SDA_out = 1;
}
void IIC_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = IIC_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void IIC_IN()
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = IIC_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void IIC_start()
{
IIC_OUT();
SCL = 1;
SDA_out = 1;
Delay_us(5);
SDA_out = 0;
Delay_us(5);
}
void IIC_end()
{
IIC_OUT();
SCL = 0;
SDA_out = 0;
SCL = 1;
Delay_us(5);
SDA_out = 1;
Delay_us(5);
}
void IIC_ACK()
{
IIC_OUT();
SCL = 0;
SDA_out = 0;
Delay_us(2);
SCL = 1;
Delay_us(5);
SCL = 0;
}
void IIC_NACK()
{
IIC_OUT();
SCL = 0;
SDA_out = 1;
Delay_us(2);
SCL = 1;
Delay_us(5);
SCL = 0;
}
uint8_t IIC_Wait_ACK()
{
uint8_t i;
IIC_IN();
SCL = 1;
while(SDA_in)
{
i++;
if(i > 250)
{
IIC_end();
#if DEBUG_IIC
printf("未收到应答信号,IIC有误\r\n");
#endif
return 0;
}
}
SCL=0;
return 1;
}
void IIC_Write_byte(uint8_t byte)
{
uint8_t i;
IIC_OUT();
SCL = 0;
for(i = 0;i < 8;i++)
{
if(byte & 0x80)
SDA_out = 1;
else
SDA_out = 0;
byte <<= 1;
Delay_us(2);
SCL = 1;
Delay_us(2);
SCL = 0;
Delay_us(2);
}
}
uint8_t IIC_Read_byte(uint8_t ACK)
{
uint8_t i,Bit;
IIC_IN();
for(i = 0;i < 8;i++)
{
SCL = 0;
Delay_us(2);
SCL = 1;
Bit <<= 1;
if(SDA_in)
Bit++;
}
if(ACK)
IIC_ACK();
else
IIC_NACK();
return Bit;
}
iic.h
#ifndef IIC_H_
#define IIC_H_
#include "main.h"
#define DEBUG_IIC 0
#define SCL PBout(6)
#define SDA_out PBout(7)
#define SDA_in PBin(7)
void IIC_Init(void);
void IIC_start(void);
void IIC_end(void);
void IIC_ACK(void);
void IIC_NACK(void);
uint8_t IIC_Wait_ACK(void);
void IIC_Write_byte(uint8_t bit);
uint8_t IIC_Read_byte(uint8_t ACK);
#endif
AT24C02.c
#include "AT24C02.h"
#include "iic.h"
#include "Delay.h"
void AT24C02_Init()
{
IIC_Init();
}
void AT24C02_Write_Byte(uint8_t Address,uint8_t Byte)
{
IIC_start();
IIC_Write_byte(AT24C02_Write_Address);
IIC_Wait_ACK();
IIC_Write_byte(Address%256);
IIC_Wait_ACK();
IIC_Write_byte(Byte);
IIC_Wait_ACK();
IIC_end();
Delay_ms(10);
}
uint8_t AT24C02_Read_Byte(uint8_t Address)
{
uint8_t Byte;
IIC_start();
IIC_Write_byte(AT24C02_Address);
IIC_Wait_ACK();
IIC_Write_byte(Address%256);
IIC_Wait_ACK();
IIC_start();
IIC_Write_byte(AT24C02_Read_Address);
IIC_Wait_ACK();
Byte = IIC_Read_byte(0);
IIC_end();
return Byte;
}
void AT24C02_Write_Continue(uint8_t Address,uint8_t *data,uint8_t number)
{
uint8_t i;
for(i = 0;i < number;i++)
{
AT24C02_Write_Byte(Address+i,*(data+i));
}
}
void AT24C02_Read_Continue(uint8_t Address,uint8_t *data,uint8_t number)
{
uint8_t i;
for(i = 0;i < number;i++)
{
*(data+i) = AT24C02_Read_Byte(Address+i);
}
}
uint8_t AT24C02_check()
{
uint8_t temp = AT24C02_Read_Byte(0xff);
if(temp == 0x38) return 1;
else
{
AT24C02_Write_Byte(0xff,0x38);
temp = AT24C02_Read_Byte(0xff);
if(temp == 0x38) return 1;
}
return 0;
}
void AT24C02_clear()
{
uint8_t a[256];
uint32_t i;
for(i = 0;i < 256;i++)
{
a[i] = 0xff;
}
AT24C02_Write_Continue(0,a,0xff);
}
AT24C02.h
#ifndef AT24C02_H_
#define AT24C02_H_
#include "stm32f1xx_hal.h"
#define AT24C02_Address 0xA0
#define AT24C02_Write_Address 0xA0
#define AT24C02_Read_Address 0xA1
void AT24C02_Init(void);
void AT24C02_Write_Byte(uint8_t Address,uint8_t Byte);
uint8_t AT24C02_Read_Byte(uint8_t Address);
void AT24C02_Write_Continue(uint8_t Address,uint8_t *data,uint8_t number);
void AT24C02_Read_Continue(uint8_t Address,uint8_t *data,uint8_t number);
uint8_t AT24C02_check(void);
void AT24C02_clear(void);
#endif
usart.c
#include<stdio.h>
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
main.c
#include "AT24C02.h"
#include "Delay.h"
#include<stdio.h>
SysTick_Init(72);
AT24C02_Init();
while(!AT24C02_check())
{
printf("AT24C02检测不正常!\r\n");
Delay_ms(500);
}
printf("AT24C02检测正常!\r\n");
AT24C02_clear();
static uint8_t i = 0,a[5] = {7,8,3,4,5},b[5];
AT24C02_Write_Continue(0,a,5);
AT24C02_Read_Continue(0,b,5);
for(i = 0;i < 5;i++)
{
printf("读出的数据为 %d\r\n",b[i]);
}
break;
3、实验效果
到这里就结束啦!
|