最近看了江科大的软件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发送的数据的最后一位就是当前所需要发送的位数据。