基于GD32F307的嵌入式学习之路--IIC通讯

IIC通讯试验

准备资源:
1)GD32F307C-EVL开发板一块
2)232串口线一根
试验目的:
实现GPIO软件模拟IIC通讯,通过IIC将一个字节数据写入EEPROM以及通过IIC读取该字节并通过串口打印出来。

硬件原理图

首先需要的就是查看开发板硬件原理图,可以看到使用的EEPROM为AT24C02,其SCL、SDA管脚分别于MCU的PB6、PB7连接。所以可以使用PB6、PB7这两个GPIO口进行软件模拟IIC通讯。
在这里插入图片描述

实现代码一myi2c.c文件

根据IIC通讯协议,使用GPIO模拟出IIC起始信号、停止信号等一系列信号

1)首先需要对GPIO的PB6、PB7初始化,其中PB6模拟SCL信号线,PB7模拟SDA信号线。初始函数如下:

void myi2c_init(void)
{
	rcu_periph_clock_enable(RCU_GPIOB);
	
	gpio_init(GPIOB,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_6|GPIO_PIN_7);
	
	IIC_SCL(1);
	IIC_SDA(1);
}

2)在myi2c.h头文件中需要添加以下宏定义,以控制PB6、PB7的输入输出方向

#define SDA_IN()  {GPIO_CTL0(GPIOB)&=~GPIO_MODE_MASK(7);GPIO_CTL0(GPIOB)|=GPIO_MODE_SET(7,0x8);}//PB9输入模式
#define SDA_OUT() {GPIO_CTL0(GPIOB)&=~GPIO_MODE_MASK(7);GPIO_CTL0(GPIOB)|=GPIO_MODE_SET(7,0x3);}//PB9输出模式

#define IIC_SCL(n)   (n?gpio_bit_set(GPIOB,GPIO_PIN_6):gpio_bit_reset(GPIOB,GPIO_PIN_6))//n=1,输出高电平;n=0,输出低电平
#define IIC_SDA(n)   (n?gpio_bit_set(GPIOB,GPIO_PIN_7):gpio_bit_reset(GPIOB,GPIO_PIN_7))
#define READ_SDA     gpio_input_bit_get(GPIOB,GPIO_PIN_7)

3)由于GD官方给的库中只有一个ms延时函数,故需要添加一个us延时函数,如下:

/* 描述:us级延时函数
 * 参数nus:需要延时的us数
 * 返回值:无*/		    								   
void delay_us(uint32_t nus)
{		
	uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;		/* 滴答定时器的重装载值 */
    ticks = nus * 120; 						/* 需要的节拍数 */
    told = SysTick->VAL;        			/* 刚进入时的计数器值 */

    while(1)
    {
        tnow = SysTick->VAL;

        if(tnow != told)
        {
            if(tnow < told)tcnt += told - tnow;
            else tcnt += reload - tnow + told;
            if(tcnt >= ticks)break;			 /* 时间超过/等于要延迟的时间,则退出. */
			told = tnow;
        }
    }  
}

4)完成GPIO的初始化之后,便可以进行软件模拟IIC。代码如下:

//IIC起始信号
void i2c_sart(void)
{
	SDA_OUT();//SDA输出模式
	IIC_SCL(1);
	IIC_SDA(1);
	delay_us(4);
	IIC_SDA(0);
	delay_us(4);
	IIC_SCL(0);
}

//IIC停止信号
void i2c_stop(void)
{
	SDA_OUT();
	IIC_SCL(0);
	IIC_SDA(0);
	delay_us(4);
	IIC_SCL(1);
	delay_us(4);
	IIC_SDA(1);
	delay_us(4);
}

/
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
uint8_t i2c_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)
		{
			i2c_stop();
			return 1;
		}
	}
	IIC_SCL(0);//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void i2c_ack(void)
{
	IIC_SCL(0);
	SDA_OUT();
	IIC_SDA(0);
	delay_us(2);
	IIC_SCL(1);
	delay_us(2);
	IIC_SCL(0);
}
//不产生ACK应答		    
void i2c_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,无应答			  
void i2c_send_byte(uint8_t byte)
{                        
    uint8_t t;   
	SDA_OUT(); 	    
    IIC_SCL(0);//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        if(byte&0x80) IIC_SDA(1);
		else IIC_SDA(0);

        byte<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL(1);
		delay_us(2); 
		IIC_SCL(0);	
		delay_us(2);
    }	 
} 

//读1个字节,ack=1时,发送ACK; ack=0,发送nACK   
uint8_t i2c_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)
        i2c_nack();//发送nACK
    else
        i2c_ack(); //发送ACK   
    return receive;
}

实现代码一24cxx.c文件

试验使用的开发板上的EEPROM为AT24C02,是一个2K Bit的串行EEPROM存储器,内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。
下图所示为AT24Cxx系列EEPROM的设备地址,由8位组成,其中A0、A1、A2三位加上高四位构成设备地址,最低一位控制读写方向。具体可查看数据手册。
在这里插入图片描述
这里只实现通过IIC向AT24C02读写一个字节的简单操作,其他多个字节的读写可在实际应用中进行扩展。代码如下:

void AT24CXX_Init(void) //初始化IIC
{
	myi2c_init();
}

//指定地址读取一个字节:
//启动总线-->发送设备地址+写-->等待应答-->发送数据存储地址-->
//等待应答-->发送设备地址+读-->等待应答-->读取数据-->主机发送是否应答……
uint8_t AT24CXX_ReadOneByte(uint16_t ReadAddr)	
{
	uint8_t temp;
	i2c_sart();
	i2c_send_byte(0xA0 + ((ReadAddr/256)<<1));//发送设备地址0xA0 + 写
	i2c_wait_ack();
	i2c_send_byte(ReadAddr%256); //发送低地址
	i2c_wait_ack();
	i2c_sart();
	i2c_send_byte(0xA1);
	i2c_wait_ack();
	temp=i2c_read_byte(0); //发送nACK
	i2c_stop();
	return temp;
}

//指定地址写入一个字节:
//启动总线-->发送设备地址+写-->等待应答-->
//发送数据的储存地址(0x00-0xFF,256个字节-->等待应答-->发送数据-->等待应答-->停止总线
void AT24CXX_WriteOneByte(uint16_t WriteAddr,uint8_t DataToWrite)		
{
	i2c_sart();
	i2c_send_byte(0xA0 + ((WriteAddr/256)<<1));//发送设备地址0xA0 + 写
	i2c_wait_ack();
	i2c_send_byte(WriteAddr%256); //发送低地址
	i2c_wait_ack();
	i2c_send_byte(DataToWrite);
	i2c_wait_ack();
	i2c_stop();
	delay_1ms(10);
}

实现代码一main.c

main函数:通过检测两个按键是否按下,当按键KEY_TAMPER按下则从0x00地址开始写一个字节至AT24C02,当按键KEY_USER按下则从0x00地址读一个字节,并通过串口打印出来。
代码如下:

int main(void)
{ 	
	uint8_t datatemp;	
   
    systick_config();
	
	gd_eval_com_init(EVAL_COM1);
	AT24CXX_Init();
	gd_eval_key_init(KEY_TAMPER, KEY_MODE_GPIO);
	gd_eval_key_init(KEY_USER, KEY_MODE_GPIO);
	
	printf("\r\n IIC test... \r\n");
	
	while(1){
		if(RESET == gd_eval_key_state_get(KEY_TAMPER))
		{  //未进行按键的防抖
			printf("\r\n Start Write 24C02.... \r\n");
			AT24CXX_WriteOneByte(0x00,0x0a);
			printf("\r\n 24C02 Write Finished! \r\n");
		}
		
		if(RESET == gd_eval_key_state_get(KEY_USER))
		{
			printf("\r\n Start read 24C02.... \r\n");
			datatemp = AT24CXX_ReadOneByte(0x00);
			printf("datatemp is : 0x%x \r\n",datatemp);	
		}
	}
}

结果

1)按键KEY_TAMPER按下

2)按键KEY_USER按下

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iFan&iLT123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值