点击查看代码
/**
*********************************************************************************
* @file main.c
* @author
* @version
* @date 2024/07/18
* @brief 利用IO口模拟
SCL -- PB8 输出模式
SDA -- PB9 输出模式/输入模式
GND -- 接地
VCC -- 3.3V
*********************************************************************************
**/
#include "stm32f4xx.h" //必须包含
#include <stdio.h>
#include <stdbool.h>
#include "delay.h"
#define SDA_SET(n) (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_9) : GPIO_ResetBits(GPIOB,GPIO_Pin_9)
#define SCL_SET(n) (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_8) : GPIO_ResetBits(GPIOB,GPIO_Pin_8)
#define SDA_READ GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)
//前台程序就是中断服务程序,该程序是不需要手动调用的,当中断触发之后CPU会自动跳转过来执行该函数
void USART1_IRQHandler(void)
{
uint8_t data;
//判断中断是否发生
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//从USART1中接收一个字节
data = USART_ReceiveData(USART1); //一次只能接收一个字节
//把接收到的数据转发出去
USART_SendData(USART1,data);
}
}
void USART1_Config(u32 baud)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//打开了GPIO端口时钟 PA9和PA10
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//打开USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//选择GPIO引脚的复用功能
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//配置GPIO引脚 注意:复用模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口参数+对串口初始化
USART_InitStructure.USART_BaudRate = baud; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位
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);
//配置NVIC参数 + 对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);
//选择USART1的中断源,接收到数据则触发中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//打开串口
USART_Cmd(USART1, ENABLE);
}
//利用串口发送一个字符串
void USART1_SendString(const char *str)
{
while(*str)
{
USART_SendData(USART1,*str++);
while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );
}
}
//SCL引脚设置
void IIC_SCLConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//SDA引脚设置
void IIC_SDAConfig(GPIOMode_TypeDef GPIO_Mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
//打开GPIO端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
//SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//IIC的初始化
void IIC_Config(void)
{
//1.设置SDA和SCL为输出模式
IIC_SCLConfig();
IIC_SDAConfig(GPIO_Mode_OUT);
//2.确保SDA和SCL处于空闲状态
SDA_SET(1);
SCL_SET(1);
delay_us(5);
}
//IIC的开始信号
void IIC_Start(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.确保SDA和SCL处于空闲状态
SDA_SET(1);
SCL_SET(1);
delay_us(5);
//3.把SDA引脚电平拉低
SDA_SET(0);
delay_us(5);
//4.把SCL引脚电平拉低,此时准备数据
SCL_SET(0);
//delay_us(5);
}
//IIC的发送字节
void IIC_SendByte(uint8_t Byte)
{
uint8_t i = 0;
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备数据
SCL_SET(0);
delay_us(5);
//3.循环发送8bit,遵循MSB高位先出
for(i=0;i<8;i++)
{
//4.判断待发送的字节的最高位
if( Byte & 0x80 )
{
SDA_SET(1);
}
else
SDA_SET(0);
Byte <<= 1;
delay_us(5);
//5.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
//6.把SCL引脚电平拉低,此时主机准备下一个bit
SCL_SET(0);
delay_us(5);
}
}
//判断从机是否应答
bool IIC_IsSlaveACK(void)
{
bool ack;
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时为第9个脉冲的低电平,从机准备bit
SCL_SET(0);
delay_us(5);
//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态
SCL_SET(1);
delay_us(5);
//4.主机读取状态 1 表示未应答 0 表示已应答
if(SDA_READ)
ack = false;
else
ack = true;
//5.把SCL引脚电平拉低
SCL_SET(0);
delay_us(5);
return ack;
}
//IIC读取字节
uint8_t IIC_ReadByte(void)
{
uint8_t i = 0,data = 0;
//1.设置SDA引脚为输入模式
IIC_SDAConfig(GPIO_Mode_IN);
//2.把SCL引脚电平拉低,此时从机准备数据
SCL_SET(0);
delay_us(5);
//3.循环读取8bit,遵循MSB高位先出
for (i = 0; i < 8; ++i)
{
//4.把SCL电平拉高,此时主机读取bit
SCL_SET(1);
delay_us(5);
//5.主机读取bit
data <<= 1;
data |= SDA_READ;
//delay_us(5);
//6.把SCL引脚电平拉低,此时从机准备下一个bit数据
SCL_SET(0);
delay_us(5);
}
//7.返回结果
return data;
}
//ack=1 表示不应答 ack=0 表示要应答
void IIC_MasterACK(uint8_t ack)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.把SCL引脚电平拉低,此时主机准备
SCL_SET(0);
delay_us(5);
//3.判断ack的状态,从而对SDA操作
if(ack)
SDA_SET(1);
else
SDA_SET(0);
delay_us(5);
//4.把SCL电平拉高,此时从机读取bit
SCL_SET(1);
delay_us(5);
//5.把SCL引脚电平拉低
SCL_SET(0);
delay_us(5);
}
//IIC的停止信号
void IIC_Stop(void)
{
//1.设置SDA引脚为输出模式
IIC_SDAConfig(GPIO_Mode_OUT);
//2.设置SDA和SCL均为低电平
SDA_SET(0);
SCL_SET(0);
delay_us(5);
//3.把SCL电平拉高
SCL_SET(1);
delay_us(5);
//4.把SDA电平拉高
SDA_SET(1);
delay_us(5);
}
void AT24C02_Config(void)
{
IIC_Config();
}
//AT24C02字节写入的指令
bool AT24C02_ByteWrite(uint8_t Addr,uint8_t Byte)
{
//1.开始信号
IIC_Start();
//2.器件地址
IIC_SendByte(0xA0);
//3.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//4.存储地址
IIC_SendByte(Addr);
//5.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
return false;
}
//6.发送数据
IIC_SendByte(Byte);
//7.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Data Error\r\n");
return false;
}
//8.停止信号
IIC_Stop();
//9.延时5ms
delay_ms(5);
return true;
}
//AT24C02的当前地址读指令
uint8_t AT24C02_CurrentAddressRead(void)
{
uint8_t recv = 0;
//1.开始信号
IIC_Start();
//2.器件地址
IIC_SendByte(0xA1);
//3.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//4.读取字节
recv = IIC_ReadByte();
//5.发送应答
IIC_MasterACK(0x01);
//6.停止信号
IIC_Stop();
return recv;
}
//AT24C02的随机地址读指令
uint8_t AT24C02_RandomAddressRead(uint8_t DestAddr)
{
uint8_t recv = 0;
//1.开始信号
IIC_Start();
//2.器件地址
IIC_SendByte(0xA0);
//3.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//4.存储地址
IIC_SendByte(DestAddr);
//5.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
return false;
}
//6.开始信号
IIC_Start();
//7.器件地址
IIC_SendByte(0xA1);
//8.从机应答
if( IIC_IsSlaveACK() == false)
{
IIC_Stop();
USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
return false;
}
//9.读取字节
recv = IIC_ReadByte();
//10.发送应答
IIC_MasterACK(0x01);
//11.停止信号
IIC_Stop();
return recv;
}
int main()
{
uint8_t data = 0;
uint8_t buf[128] ={0};
//1.硬件的初始化
USART1_Config(9600);
AT24C02_Config();
//2.向AT24C02的0x10地址下写入字符'A'
AT24C02_ByteWrite(0x00,'A');
//3.读取AT4C02的0x00地址下的数据
data = AT24C02_RandomAddressRead(0x00);
sprintf((char *)buf,"read byte is %c\r\n",data);
USART1_SendString((char *)buf);
while(1)
{
}
}