PCF8591简单介绍
芯片的作用是ADDA转换,有16脚,其中AIN0~AIN3为模拟信号输入,A0~A3为芯片地址,SDA、SCL为I2C引脚,AOUT为DA输出端。
PCF8951的器件地址为1010A3A2A1A0(二进制),我买的芯片A0~A3都接GND,所以地址为0x90。
PCF8951使用方法
通过I2C通信读取数据和写入数据。
读取AD数据:发送第一个字节为器件地址,第二个字节为寄存器地址。然后再发送期间地址,此时读取数据即可读取出AD值。
写入DA数据:发送器件地址,发送寄存器地址,发送8位DA值。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
D7:0
D6:模拟输出启动标志位(为1有效)。
D5&D4:模拟输入方式:00四路单端输入,01三鹿差分输入,10单端差分缓和输入,11两个差分输入。
D3:0
D2:自动增量标志位(为1有效)(开启时通道在AD转换完成自动递增)。
D1&D0:AD通道选择:00通道0,01通道1,10通道2,11通道3。
软件I2C代码部分
这里我使用STM32F103C8T6驱动该芯片,分别使用软件I2C和硬件I2C驱动该芯片,顺便了解两者区别。
MyI2C.h(软件I2C的代码)
void MyI2C_W_SCL(uint8_t BitValue)//封装函数,方便操作
{
GPIO_WriteBit(GPIOA, I2C_SCL, (BitAction)BitValue);//进行位操作
}
void MyI2C_W_SDA(uint8_t BitValue)//封装函数,方便操作
{
GPIO_WriteBit(GPIOA, I2C_SDA, (BitAction)BitValue);
}
uint8_t MyI2C_R_SDA(void)//封装函数,方便操作
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOA, I2C_SDA);//位读取
return BitValue;
}
void MyI2C_Init(void)//软件I2C的GPIO端口初始化
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;//开漏输出,弱上拉模式
GPIO_InitStructure.GPIO_Pin = GPIO_I2C_Pin;//引脚为自己宏定义
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_WriteBit(GPIOA, I2C_SCL, Bit_SET);
GPIO_WriteBit(GPIOA, I2C_SDA, Bit_SET);
}
void MyI2C_Start(void)//I2C开始信号
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop(void)//I2C结束信号
{
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)//I2C发送一个字节
{
uint8_t i;
for(i = 0; i < 8; i ++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_ReceiveByte(void)//I2C读取一个字节
{
uint8_t Byte=0, i;
MyI2C_W_SDA(1);//释放SDA,控制权给从机
for(i = 0; i < 8; i ++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA())
Byte |= (0x80 >> i);
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_SendAck(uint8_t Ack)//I2C发送应答函数
{
MyI2C_W_SDA(Ack);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveAck(void)//I2C读取应答函数
{
uint8_t Ack = 1;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
Ack = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return Ack;
}
通过软件I2C通信的PCF8591代码
uint8_t RegistCmd;
void PCF8591_SoftWareI2C_Init(void)//再次封装(声明其中一个即可)
{
MyI2C_Init();
}
uint8_t PCF8591_SoftWareI2C_ADC_Ipt(uint8_t Command)//读取AD值函数
{
uint8_t Data = 0;
RegistCmd = (RegistCmd & 0x44) | Command;//进行或操作防止干扰其他
MyI2C_Start();
MyI2C_SendByte(0x90);
MyI2C_ReceiveAck();
MyI2C_SendByte(RegistCmd);
MyI2C_Start();
MyI2C_SendByte(0x91);
MyI2C_ReceiveAck();
Data = MyI2C_ReceiveByte();
MyI2C_SendAck(1);
MyI2C_Stop();
return Data;
}
void PCF8591_SoftWareI2C_DAC_Opt(uint8_t Value)//写DA值函数
{
MyI2C_Start();
MyI2C_SendByte(0x90);
MyI2C_ReceiveAck();
MyI2C_SendByte(0x40);
MyI2C_ReceiveAck();
MyI2C_SendByte(Value);
MyI2C_Stop();
}
主函数代码部分
#include "stm32f10x.h"
#include "OLED.h"
#include "PCF8951.h"
uint8_t AD_Value;
int main(void)
{
OLED_Init();
PCF8591_HardWareI2C_Init();
while(1)
{
AD_Value = PCF8591_SoftWareI2C_ADC_Ipt(0x00);//读取滑动变阻器通道0的AD值
OLED_ShowNum(1, 1, AD_Value, 3);
}
}
/*
for(uint8_t i = 0; i < 255; i ++)//锯齿波的函数,需要注意AD读取和DA转换不能同时操作
{
PCF8591_SoftWare_DAC_Opt(i);
}
*/
软件I2C通信PCF8591的实验现象
通过调节可变电阻器可以观察到数值在0~255之间产生变化
通过DA输出锯齿波,可以看出利用软件I2C输出的波形严重失真了,在5V每格的时候才能看出为锯齿波。
硬件I2C代码部分
void I2C_EventWait(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)//检查事件封装函数,防止死循环
{
uint16_t LOOP = 10000;
while(I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)
{
LOOP --;
if(LOOP == 0)
break;
}
}
void PCF8591_HardWareI2C_Init(void)//I2C初始化
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 40000;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_Init(I2C2, &I2C_InitStructure);
I2C_Cmd(I2C2, ENABLE);
}
void PCF8591_HardWareI2C_ClearRegistCmd(void)//清除RegistCmd 的值
{
RegistCmd = 0;
I2C_GenerateSTART(I2C2, ENABLE);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, 0x90, I2C_Direction_Transmitter);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C2, RegistCmd);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTOP(I2C2, ENABLE);
}
uint8_t PCF8591_HardWareI2C_ADC_Ipt(uint8_t Command)
{
uint8_t Data = 0;
RegistCmd = (RegistCmd & 0x44) | Command;
I2C_GenerateSTART(I2C2, ENABLE);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, 0x90, I2C_Direction_Transmitter);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C2, RegistCmd);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTART(I2C2, ENABLE);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, 0x90, I2C_Direction_Receiver);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);
Data = I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2, DISABLE);
I2C_GenerateSTOP(I2C2, ENABLE);
return Data;
}
void PCF8591_HardWareI2C_DAC_Opt(uint8_t Value)
{
I2C_GenerateSTART(I2C2, ENABLE);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2, 0x90, I2C_Direction_Transmitter);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
I2C_SendData(I2C2, 0x40);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
I2C_SendData(I2C2, Value);
I2C_EventWait(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
I2C_GenerateSTOP(I2C2, ENABLE);
}
主函数代码部分
硬件I2C通信PCF8591的实验现象
AD的实验现象与软件无异。
而通过硬件I2C通信的PCF8591输出的锯齿波波形可以看出比较干净。
本来购买该模块是用来做AD采样同时DA输出相同波形的实验,但是发现该芯片在AD读取的时候不能模拟输出,导致无法完成实验(也可能自己使用错误)。如果有大神可以实现希望能在评论区帮助一下,十分感谢。