用江科大的IIC发送一个字节的逻辑,用位带操作赋值失败原因总结

本文讲述了作者在使用江科大提供的IIC例程代码时,遇到的问题,即位带操作在给SDA线赋值时只影响最后一位。通过对比位带操作和GPIO_WriteBit方法,作者优化了IIC_Send_Byte函数,以正确发送字节数据。
摘要由CSDN通过智能技术生成

最近看了江科大的软件IIC视频,想要自己根据一个读写mpu6050的例程代码来实现IIC。我与江科大使用GPIO_WriteBit函数来给SDA线赋值不同,我用的是位带操作来给SDA的GPIO口赋值。该部分的.h文件配置如下:

//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))  

//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入 


//IO方向设置
#define SDA_IN()  {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=8<<4;}
#define SDA_OUT() {GPIOB->CRH&=0XFFFFFF0F;GPIOB->CRH|=3<<4;}

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

之后我想使用江科大的IIC写入一个字节代码逻辑,发现无法进行正常读取,代码如下:

//发送一个字节
void IIC_Send_Byte(u8 txd)
{
	u8 i;
	SDA_OUT();
	IIC_SCL=0;
	for(i=0;i<8;i++)
	{
		IIC_SDA = (BitAction)(txd&(0x80>>i));//这句是重点
		delay_us(1); 
		IIC_SCL=1;
		delay_us(1); 
		IIC_SCL=0;
		delay_us(1); 
	}
}

之后我进行尝试,利用位带操作分别给IIC_SDA口赋值0x80、0x01和0x00,然后用oled打印出来看结果,主函数代码:

	 SDA_OUT();
	 IIC_SDA = (BitAction)(0x80)
	 NUM_0X80 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	 IIC_SDA = (BitAction)(0x01)
	 NUM_0X01 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	 IIC_SDA = (BitAction)(0x00)
	 NUM_0X00 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	while(1)
	{
		
		OLED_ShowString(0,10,"NUM_0x80:");
		OLED_ShowNumber(70,10, NUM_0X80,5,12);
		OLED_ShowString(0,30,"NUM_0x01:");
		OLED_ShowNumber(70,30, NUM_0X01,5,12);
		OLED_ShowString(0,50,"NUM_0x00:");
		OLED_ShowNumber(70,50, NUM_0X00,5,12);
		OLED_Refresh_Gram();//刷新
	
		delay_ms(50);//延时
	}

OLED结果: 

验证可知位带操作赋值只有所赋的值的最后一位有效,其它位不管是0是1不会影响赋值结果。而江科大所使用的GPIO_WriteBit方法则是只要有一个位是1,则赋给SDA的IO口的值就会是1

试一下GPIO_WriteBit的方法发送数据的结果:

	 SDA_OUT();
	 GPIO_WriteBit(GPIOB,GPIO_Pin_9,(BitAction)(0x80));
	 NUM_0X80 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	 GPIO_WriteBit(GPIOB,GPIO_Pin_9,(BitAction)(0x01));
	 NUM_0X01 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	 GPIO_WriteBit(GPIOB,GPIO_Pin_9,(BitAction)(0x00));
	 NUM_0X00 = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9);
	while(1)
	{
		
		OLED_ShowString(0,10,"NUM_0x80:");
		OLED_ShowNumber(70,10, NUM_0X80,5,12);
		OLED_ShowString(0,30,"NUM_0x01:");
		OLED_ShowNumber(70,30, NUM_0X01,5,12);
		OLED_ShowString(0,50,"NUM_0x00:");
		OLED_ShowNumber(70,50, NUM_0X00,5,12);
		OLED_Refresh_Gram();//刷新
	
		delay_ms(50);//延时
	}

GPIO_WriteBit方法的结果符合预期。

因此我修改了发送一个字节的逻辑,代码如下:

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;
    for(t=0;t<8;t++)
    {              
			IIC_SDA=(txd&0x80)>>7;//直接位地址赋值只能够取到所赋的值的最后一位,最后一位为0就为0,所以不可以用江科大的那种方式。
			txd<<=1; 	  //GPIO_WriteBit赋值时只要所赋的值有一位是1就赋1
			delay_us(1);   
			IIC_SCL=1;
			delay_us(1); 
			IIC_SCL=0;	
			delay_us(1);
    }	 
}

这样每次SDA发送的数据的最后一位就是当前所需要发送的位数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值