本程序是stm32f103 接pcf8591AD/DA板(YL-PCF8591),I2C通信,I2C通信用的是模拟I2C,不是STM32 自带的硬件I2C外设。实践检验很稳定。SCL接口用PB6,SDA用PB7,与STM32自带的硬件I2C接口针脚是一样的,这是巧合,你也可以随便定义成别Pin针脚。关于GPIO设置,最后有解释。
写比较简单看下面程序:下面是DA(数/模转换主程序)主要用写函数。
u8 DACconversion(u8 sla,u8 c, u8 Val)
{
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
ack(); //8591应答
SendByte(c); //发送控制字节
ack(); //8591应答
SendByte(Val); //发送DAC的数值
ack(); //8591应答
Stop_I2c(); //结束总线
return(1);
}
启动:SCL高电平中间SDA高变低,紧接着SCL变低,
停止:SCL低电平变高电平后,SDA低变高,
Ack应答是主机释放SDA后,从机(PCF8591把SDA拉低)。
DA转换的过程是,先开始,发送从机地址90,从机应答后,发送寄存器地址,40代表AIN0(AOUT也是40 ),从机应答后,发送写入的数据,从机应答,结束传输。
是不是很简单?DOUT输出电压范围(0-255对应0-3.3v )输出接口接有蓝色LED,输出电压大于2V,LED 开始亮起了,电压越高,越亮。
----------------------------------------------------------------------------------------------/
下面说明读数据程序,稍微复杂一点。
u8 ADCconversion(u8 sla,u8 c)
{
u8 d;
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址0x90
ack(); //从机接收后应达
SendByte(c); //发送控制字节0x43
ack(); //从机接收后应达
Stop_I2c(); //停止总线
//------------------------------------------------
Start_I2c(); //启动总线
SendByte(sla+1); //发送读AIN3指令
ack(); //从机应对不能少
d=RcvByte(); //发送DAC的数值
Ack_I2c(0); //主机接收后应答不能少否则不稳定
Stop_I2c(); //结束总线
return(d);
}
我们读取第4个AIN口因为接有可变电阻,可以直观看到输入数据变化,AIN3地址是0x43(第一个是光敏)(第二个是热敏)(第三个是直通)
下面分析程序,下面是AD转换,接收第四个AIN的电压信号,0-3.3V对应数字0-255:
AD起始的程序和DA相同,开始-发送从站地址90,从站应答,发送控制字43,从站应答,之后的程序就不一样的,停止
再次开始,发送读指令91,从站应答,主机读8位数据,读到后主机应答0,表示接受到,最后停止传输。(注意没有这个主机接收应答,接收到数据就乱了在数据和255之间乱跳)。
可以看到Ad转换开始停止两次。特别注意,主机接收到数据后,主机回复Ack(0)。不是从机。
///------------------------------------------------------------------------------------------------///
最后说一下关于:IIC的GPIO设置。
void GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = SCL_Pin | SDA_Pin; /*初始化IO*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SCL_SET(1);
flash();
SDA_SET(1);
flash();
}
这是GPIO初始化程序,可见GPIO设置为推挽输出,这样I2C的波形用示波器观察就是正经的方波,但是推挽输出只能写数据,I2C读数据的时候必须用in_floating,见下面:
void SDA_IO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = SDA_Pin; /*初始化IO*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
void SDA_IO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = SDA_Pin; /*初始化IO*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
上面程序可见,SCL(PB6)是主机主导,所以一般设为PP不变(推挽输出),SDA因为读到时候是从机发数据,所以对于SDA(PB7)脚,在写数据时候设置成PP(推挽输出),在读入数据时设置成in_floating。这样的波形很漂亮的方波,用示波器观察很爽,逻辑清晰可见。
也有的程序SCL SDA 都设置成OD(开漏输出)不变,这就不用设置变来变去,也是可以的,就是观察波形的时候,波形底部是方波(因为漏极有场效应管通断),波形上部就是三角锯齿波了,(因为没有上场效应管控制,高电位是靠上拉电阻)。这种设置也是可以用,还简单,就是不利于用示波器观察。