详解参考:
PCF8591芯片的AD/DA转换(适用于蓝桥杯单片机)_pcf8591ad转换-CSDN博客
写在最前,上面链接更细致,这个只是我的总结了各方内容,把重点挑出来,不具有可读性
易错点:
引脚介绍
PCF8591有四个模拟信号输入端(模转数),一个输出端(数转模),因为我们一般使用芯片的内部时钟,所以EXT和OSC接地,而VSS和VCC手册里写是用来上电复位的,只需要照着原理图接就行。
发送接收数据
第一位和第二位 :AD转换的输入通道选择位 00 >>AIN0, 01>>AIN1, 10>>AIN2, 11>>AIN3
第三位 :自动递增标志位,激活时给1,在每次A/D转换后,读取的通道会自动跳到下一位。
第四位:无效位
第五、六位:选择差分输入还是单端输入(如果对精度没有太大要求时选择单端输入置00)
第七位: 使能模拟输出,使能时置1 ,表示切换为DA(数字转模拟模式)
第八位:无效位
原文链接:https://blog.csdn.net/wcl501375/article/details/129626609
芯片资料包代码
/* # I2C代码片段说明
1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
中对单片机时钟频率的要求,进行代码调试和修改。
*/
#define DELAY_TIME 5
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//从IIC总线上发送数据
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//从IIC总线上接收数据
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//等待应答
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//发送应答
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
自写代码
//函数名:ADC转换函数
//入口参数:要进行转换的通道控制位
//返回值:ADC转换的数值
//函数功能:对指定的通道进行ADC转换,函数返回转换的数值
unsigned char AD_Read(unsigned char channel_num_contrl)
{
unsigned char temp;
IIC_Start();//发送开启信号
IIC_SendByte(0x90);//选择PCF8591芯片,确定写的模式
IIC_WaitAck();//等待PCF8591反馈
IIC_SendByte(channel_num_contrl);//确定要转换的通道(0X01光敏电阻或0X03电位器)顺便,使能DA转换(0X41或0X43) 存疑
IIC_WaitAck();//等待PCF8591反馈
IIC_Start();//发送开启信号
IIC_SendByte(0x91);//选择PCF8591芯片,确定读的模式
IIC_WaitAck();//等待PCF8591反馈
temp = IIC_RecByte();//接收数据
IIC_SendAck(1);//选择不应答
IIC_Stop();//停止发送
return temp;
}
//函数名:DAC转换函数
//入口参数:要进行转换的数值
//返回值:无
//函数功能:对入口参数要转换的DA数据进行转换
void DA_Write(unsigned char trans_dat)
{
IIC_Start();//发送开启信号
IIC_SendByte(0x90);//选择PCF8591芯片,确定写的模式
IIC_WaitAck();//等待PCF8591反馈
IIC_SendByte(0x41);//使能DA转换(随便写通道编号,不影响,主要的功能是使能DA)
IIC_WaitAck();//等待PCF8591反馈
IIC_SendByte(trans_dat);//将待转换的数据发送出去
IIC_WaitAck();//等待PCF8591反馈
IIC_Stop();//停止发送
}
main.c
/* 头文件声明区 */
#include <STC15F2K60S2.H>//单片机寄存器专用头文件
#include <Init.h>//初始化底层驱动专用头文件
#include <Led.h>//Led底层驱动专用头文件
#include <Key.h>//按键底层驱动专用头文件
#include <Seg.h>//数码管底层驱动专用头文件
#include "iic.h"//数模转换底层驱动头文件 √
/* 变量声明区 */
unsigned char Key_Val,Key_Down,Key_Old,Key_Up;//按键专用变量
unsigned char Key_Slow_Down;//按键减速专用变量
unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};//数码管显示数据存放数组
unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};//数码管小数点数据存放数组
unsigned char Seg_Pos;//数码管扫描专用变量
unsigned int Seg_Slow_Down;//数码管减速专用变量
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};//Led显示数据存放数组
bit Seg_Disp_Mode;//数码管显示模式变量 0-电压显示界面 1-电压输出界面
float Voltage;//实时电压变量
float Voltage_Output;//实时电压输出变量
bit Output_Mode;//输出模式专用变量 0-2V 1-随AD输出
bit Seg_Flag = 1;//数码管功能标志位
/* 键盘处理函数 */
void Key_Proc()
{
if(Key_Slow_Down) return;
Key_Slow_Down = 1;//键盘减速程序
Key_Val = Key_Read();//实时读取键码值
Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿
Key_Old = Key_Val;//辅助扫描变量
switch(Key_Down)
{
case 19://显示界面切换按键
Seg_Disp_Mode ^= 1;
break;
case 18://输出模式切换按键
Output_Mode ^= 1;
break;
case 16://数码管功能按键
Seg_Flag ^= 1;
break;
}
}
/* 信息处理函数 */
void Seg_Proc()
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//数码管减速程序
Voltage = AD_Read(0x43) / 51.0;//实时读取RB2电压数据
if(Output_Mode == 0)//固定输出2V
Voltage_Output = 2;
else
Voltage_Output = Voltage;
if(Seg_Disp_Mode == 0)//处于电压显示界面
{
Seg_Buf[0] = 11;//U
Seg_Buf[5] = (unsigned char)Voltage;
Seg_Buf[6] = (unsigned int)(Voltage * 100) / 10 % 10;
Seg_Buf[7] = (unsigned int)(Voltage * 100) % 10;
Seg_Point[5] = 1;//点亮小数点
}
else//处于电压输出界面
{
Seg_Buf[0] = 12;//F
Seg_Buf[5] = (unsigned char)Voltage_Output;
Seg_Buf[6] = (unsigned int)(Voltage_Output * 100) / 10 % 10;
Seg_Buf[7] = (unsigned int)(Voltage_Output * 100) % 10;
Seg_Point[5] = 1;//点亮小数点
}
}
/* 其他显示函数 */
void Led_Proc()
{
unsigned char i;
DA_Write(Voltage_Output);//电压输出
for(i=0;i<2;i++)
ucLed[i] = (i == Seg_Disp_Mode);
if(Voltage < 1.5 || (Voltage >= 2.5 && Voltage < 3.5))
ucLed[2] = 0;
else
ucLed[2] = 1;
ucLed[3] = Output_Mode;
}
/* 定时器0中断初始化函数 */
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //定时器中断0打开
EA = 1; //总中断打开
}
/* 定时器0中断服务函数 */
void Timer0Server() interrupt 1
{
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
if(++Seg_Pos == 8) Seg_Pos = 0;//数码管显示专用
if(Seg_Flag == 1)
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
else
Seg_Disp(Seg_Pos,10,0);
Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
}
/* Main */
void main()
{
System_Init();
Timer0Init();
while (1)
{
Key_Proc();
Seg_Proc();
Led_Proc();
}
}