- 数据的有效性
- 起始和停止条件
- 应答信号
页编程
连续读写时序图
AT24C02读写数据代码
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "string.h"
//注意事项:设置成全局变量,不然局部变量影响while(1)循环输出 (只能按复位键,按一下输出一下)
static GPIO_InitTypeDef GPIO_InitStructure;
void MY_AT24C02_Init(void);
void MY_SDA_MODE( GPIOMode_TypeDef gpio_Mode);
void IIC_START(void);
int8_t IIC_WAIT_ACK(void);
void IIC_Master_ACK(uint8_t ack);
void IIC_STOP(void);
void IIC_SEND_Byte(uint8_t data);
int8_t AT24C02_WRITE(uint8_t addr,uint8_t *pbuf,uint32_t len);
int32_t AT24C02_READ(uint8_t addr,uint8_t *pbuf,uint32_t len);
void MY_USART1_Init(uint32_t baud);
void delay_us(uint32_t n);
void delay_ms(uint32_t ms);
#define SCL_W PBout(8)
#define SDA_W PBout(9)
#define SDA_R PBin(9)
#define PAin(x) *((volatile uint32_t*)(0x42000000+(GPIOA_BASE+0x10-0x40000000)*32+(x)*4))
#define PBin(x) *((volatile uint32_t*)(0x42000000+(GPIOB_BASE+0x10-0x40000000)*32+(x)*4))
#define PFout(x) *((volatile uint32_t*)(0x42000000+(GPIOF_BASE+0x14-0x40000000)*32+(x)*4))
#define PBout(x) *((volatile uint32_t*)(0x42000000+(GPIOB_BASE+0x14-0x40000000)*32+(x)*4))
//输出重定向
#pragma import(__use_no_semihosting_swi)//这个声明,使程序遇到文件操作函数不会停在中断处
struct __FILE { int handle; /* Add whatever you need here */ };//标准库需要的支持函数
FILE __stdout;
FILE __stdin;
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);//检测上一次是否发送完毕
USART_SendData(USART1,ch);//用哪一个串口打印,直接更改USARTx
return (ch);
}
//避免使用半主机模式
void _sys_exit(int return_code) {
label: goto label; /* endless loop */
}
int main(void)
{
int32_t rt=0,i;
uint8_t buf[8]={0};
uint8_t buf1[8]={0};
MY_USART1_Init(9600);
MY_AT24C02_Init();
memset(buf,0x67,8);
rt=AT24C02_WRITE(0,buf,8);
printf("rt=%d\n",rt);
for(i=0;i<8;i++)
{
printf("buf[%d]=%x\t",i,buf[i]);
}
printf("\r\n");
printf("写入数据成功\r\n");
rt= AT24C02_READ(0,buf1,8);
if(rt==0)
{
for(i=0;i<8;i++)
{
printf("buf1[%d]=%x\t",i,buf1[i]);
}
printf("\r\n");
}
//主程序的循环
while(1)
{
}
}
void MY_AT24C02_Init(void)
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//配置PB8~PB9为输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8| GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出功能模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,&GPIO_InitStructure);
//初始状态都是拉高
SCL_W=1;
SDA_W=1;
}
void MY_SDA_MODE( GPIOMode_TypeDef gpio_Mode)
{
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = gpio_Mode;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
//启动信号
void IIC_START(void)
{
//SDA引脚为输出模式
MY_SDA_MODE(GPIO_Mode_OUT);
SCL_W=1;
SDA_W=1;
delay_us(5);
SDA_W=0;
delay_us(5);
//为了保持占有时钟线,把高电平拉低(钳住总线)
SCL_W=0;
delay_us(5);
}
//停止信号
void IIC_STOP(void)
{
//SDA引脚为输出模式
MY_SDA_MODE(GPIO_Mode_OUT);
SCL_W=1;
SDA_W=0;
delay_us(5);
SDA_W=1;
delay_us(5);
}
//从机应答信号
int8_t IIC_WAIT_ACK(void)
{
int8_t ack;
//SDA引脚为输入模式
MY_SDA_MODE(GPIO_Mode_IN);
//在高电平的时候获取SDA数据
SCL_W=1;
delay_us(5);
//检测从机应答信号的高低电平
if(SDA_R)
ack=1; //无应答
else
ack=0; //有应答
SCL_W=0;
delay_us(5);
return ack;
}
void IIC_SEND_Byte(uint8_t data)
{
int8_t i=0;
//SDA引脚为输出模式
MY_SDA_MODE(GPIO_Mode_OUT);
SCL_W=0;
SDA_W=0;
delay_us(5);
for(i=7;i>=0;i--)
{
if(data&(1<<i))
SDA_W=1;
else
SDA_W=0;
delay_us(5);
//在时钟线为高电平的时候,SDA获取数据
SCL_W=1;
delay_us(5);
//在时钟线为低电平的时候,SDA切换电平
SCL_W=0;
delay_us(5);
}
}
int8_t AT24C02_WRITE(uint8_t addr,uint8_t *pbuf,uint32_t len)
{
int8_t ack=0;
uint8_t *p=pbuf;
//发送启动信号
IIC_START();
//设备开始寻址
IIC_SEND_Byte(0xA0);//写地址
//检测从机是否应答
ack=IIC_WAIT_ACK();
printf("ack=%d",ack);
if(ack)
{
printf("device address error\n");
return -1;
}
printf("device address success\n");
//发送你要写入数据的地址
IIC_SEND_Byte(addr);
//检测从机是否应答
ack=IIC_WAIT_ACK();
if(ack)
{
printf("word address error\n");
return -2;
}
printf("word address success\n");
//连续写入数据
while(len--)
{
IIC_SEND_Byte(*p++);
ack=IIC_WAIT_ACK();
if(ack)
{
printf("write error\n");
return -3;
}
}
//停止信号
IIC_STOP();
return 0;
}
//主机应答
void IIC_Master_ACK(uint8_t ack) //由主机读数据,主机应答(发数据0,1)
{
//SDA引脚为输出模式
MY_SDA_MODE(GPIO_Mode_OUT);
SCL_W=0;
SDA_W=0;
delay_us(5);
if(ack)
SDA_W=1;
else
SDA_W=0;
delay_us(5);
SCL_W=1; //时钟线在高电平的时候,SDA获取数据
delay_us(5);
SCL_W=0; //时钟线在低电平的时候,SDA切换电平
delay_us(5);
}
//接收数据
uint8_t I2c_Recv_Byte(void)
{
int32_t i=0;
uint8_t data=0;
//SDA引脚为输入模式
MY_SDA_MODE(GPIO_Mode_IN);
for(i=7;i>=0;i--)
{
SCL_W=1; //时钟线在高电平的时候,SDA获取数据
delay_us(5);
//检测从机应答
if(SDA_R)
data|=1<<i;
SCL_W=0; //时钟线在低电平的时候,SDA切换电平
delay_us(5);
}
return data;
}
//读数据
int32_t AT24C02_READ(uint8_t addr,uint8_t *pbuf,uint32_t len)
{
uint8_t ack=0;
uint8_t *p=pbuf;
//发送起始信号
IIC_START();
//设备寻址
IIC_SEND_Byte(0xA0);//写地址
//检测从机是否应答
ack=IIC_WAIT_ACK();
if(ack)
{
printf("device address error\r\n");
return -1;
}
//发送要读取数据的地址
IIC_SEND_Byte(addr);
ack=IIC_WAIT_ACK();
if(ack)
{
printf("word address error\r\n");
return -2;
}
//发送起始信号
IIC_START();
//设备寻址
IIC_SEND_Byte(0xA1);//读地址
ack=IIC_WAIT_ACK();
if(ack)
{
printf("advice address error\r\n");
return -3;
}
len=len-1;
while(len--)
{
*p++=I2c_Recv_Byte();
//printf("rcv:%x",*(p-1));
IIC_Master_ACK(0);
}
//获取最后一个数据
*p=I2c_Recv_Byte();
IIC_Master_ACK(1);
//停止信号
IIC_STOP();
return 0;
}
void MY_USART1_Init(uint32_t baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//1.使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//2.将PA9,PA10引脚复用映射为串口1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);//USART1_TX
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);//USART1_RX
//3.配置引脚
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed= GPIO_High_Speed;
GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//4.串口初始化
USART_InitStructure.USART_BaudRate=baud;//波特率
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//8位数据位
USART_InitStructure.USART_StopBits=USART_StopBits_1;//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);
//5、使能串口1的接收中断--触发方式
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//6、配置抢占优先级和响应优先级,打开中断请求通道给NVIC
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
//7、使能串口
USART_Cmd(USART1,ENABLE);
}
void delay_us(uint32_t n)
{
SysTick->CTRL = 0; //关闭系统定时器才能进行配置
//时钟源21MHz
//实现21000 000次计数就是1s的时间到达
//实现21次计数就是1us的时间到达
SysTick->LOAD = (21*n)-1; // 1us的定时
SysTick->VAL = 0; // 清空当前计数值也会清空count_flag(计数标志位)
SysTick->CTRL = 1; //使能系统定时器工作,并且时钟源选择21MHz的参考时钟001
while ((SysTick->CTRL & 0x00010000)==0);//等待count flag标志位为1
SysTick->CTRL = 0; //关闭系统定时器,不再进行定时
}
void delay_ms(uint32_t ms)
{
#if 0
while(ms --)
{
SysTick->CTRL = 0; // 关闭系统定时器后才能配置寄存器
SysTick->LOAD = 21000-1; // 设置计数值,用于设置定时的时间
SysTick->VAL = 0; // 清空当前值还有计数标志位
SysTick->CTRL = 1; // 使能系统定时器工作,且时钟源为系统时钟的8分频(168MHz/8=21MHz)
while ((SysTick->CTRL & (1<<16))==0); // 等待系统定时器计数完毕
SysTick->CTRL = 0; // 关闭系统定时器
}
#else
while(ms --)
{
SysTick->CTRL = 0; // 关闭系统定时器后才能配置寄存器
SysTick->LOAD = 168000-1; // 设置计数值,用于设置定时的时间
SysTick->VAL = 0; // 清空当前值还有计数标志位
SysTick->CTRL = 5; // 使能系统定时器工作,且时钟源为系统时钟168MHz
while ((SysTick->CTRL & (1<<16))==0); // 等待系统定时器计数完毕
SysTick->CTRL = 0; // 关闭系统定时器
}
#endif
}