蓝桥杯2024/1/26笔记-----基于PCF8591的电压采集装置

功能实现要求:

每次建好工程文件夹,里边包含User(放工程文件,mian.c,可以在这里写如同我这个文章的文本文档)、Driver(存放底层文件如Led.c,Led.h等)
新建的工程先搭建框架,可以先书写底层函数(此次书写了五个函数并包含相应的头文件共十个底层文件)


底层函数内容:


1.初始化底层驱动专用文件


比如先用3个IO口控制74HC138译码器,控制Y4为低电平;当Y4为低电平时,或非门74HC02控制Y4C为高电平,使74HC573的OE端口有效,OE端口有效时,可使用P0口控制LED的亮灭。
可以去多了解74HC138译码器,74HC02或非门,74HC573八路输出透明锁存器的相关内容会更好理解
#include <Init.h>

//关闭外设
void System_Init()
{
    P0 = 0xff;
    P2 = P2 & 0x1f | 0x80;
    P2 &= 0x1f;
    P0 = 0x00;
    P2 = P2 & 0x1f | 0xa0;
    P2 &= 0x1f;
}
//头文件
#include <STC15F2K60S2.H>
void System_Init();

2.Led底层驱动专用文件


与初始化底层驱动专用文件同理,需要了解对应的锁存器控制,可以在使用的芯片数据手册查看
#include <Led.h>

void Led_Disp(unsigned char addr,enable)
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(enable)
        temp |=0x01 << addr;
    else
        temp&= ~ (0x01 << addr);
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0x80;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}
void Beep(unsigned char flag)
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(flag)
        temp |=0x40 ;
    else
        temp &= ~ 0x40 ;
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0xa0;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}
void Relay(unsigned char flag)
{
    static unsigned char temp = 0x00;
    static unsigned char temp_Old = 0xff;
    if(flag)
        temp |= 0x10 ;
    else
        temp &= ~ 0x10 ;
    if(temp != temp_Old)
    {
        P0 = ~ temp;
        P2 = P2 & 0x1f | 0xa0;
        P2 &= 0x1f;
        temp_Old = temp;
    }
}

//头文件
#include <STC15F2K60S2.H>
void Led_Disp(unsigned char addr,enable);

3.按键底层驱动专用文件


(板子上的按键从按键4开始到按键19,可根据实际硬件修改)
#include <Key.h>

unsigned char Key_Read()
{
    unsigned char temp = 0;
    P44 = 0;P42 = 1; P35 = 1;P34 = 1;//这个仿真没有P4口,不适用,但是实际运行使用这个
    P37 = 0; P36 = 1; P35 = 1; P34 = 1;
    if(P33 == 0) temp = 4;
    if(P32 == 0) temp = 5;
    if(P31 == 0) temp = 6;
    if(P30 == 0) temp = 7;
    P37 = 1; P36 = 0; P35 = 1; P34 = 1;
    if(P33 == 0) temp = 8;
    if(P32 == 0) temp = 9;
    if(P31 == 0) temp = 10;
    if(P30 == 0) temp = 11;
    P37 = 1; P36 = 1; P35 = 0; P34 = 1;
    if(P33 == 0) temp = 12;
    if(P32 == 0) temp = 13;
    if(P31 == 0) temp = 14;
    if(P30 == 0) temp = 15;
    P37 = 1; P36 = 1; P35 = 1; P34 = 0;
    if(P33 == 0) temp = 16;
    if(P32 == 0) temp = 17;
    if(P31 == 0) temp = 18;
    if(P30 == 0) temp = 19;
    return temp;
    
}
//头文件
#include <STC15F2K60S2.H>

unsigned char Key_Read();

4.数码管底层驱动专用文件


#include <Seg.h>

unsigned char Seg_Dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};//数码管段码储存数组
unsigned char Seg_Wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管位码储存数组

void Seg_Disp(unsigned char wela,dula,point)
{
    P0 = 0xff; //
    P2 = P2 & 0x1f |0xe0;
    P2 &= 0x1f;
    P0 = Seg_Wela[wela];
    P2 = P2 & 0x1f |0xc0;
    P2 &= 0x1f;
    P0 = Seg_Dula[dula];
    if(point)
        P0 &= 0x7f;
    P2 = P2 & 0x1f |0xe0;
    P2 &= 0x1f;
}
//头文件
#include <STC15F2K60S2.H>

void Seg_Disp(unsigned char wela,dula,point);

5.IIC底层驱动文件


/*    #   I2C代码片段说明
    1.     本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
    2.     参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
        中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include "iic.h"

#include "reg52.h"
#include <intrins.h>

sbit sda = P2^1;
sbit scl = P2^0;

#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);
}

//
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;  
}

//
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);
}

unsigned char Ad_Read(unsigned char addr)//AD读取,要有一个入口参数
{
    unsigned char temp;//接收返回值变量
    I2CStart();//启动单总线
    I2CSendByte(0x90);//发送一个0x90,告诉单片机要写数据了
    I2CWaitAck();//等待应答
    I2CSendByte(addr);//发送一个地址(获取的数据)
    I2CWaitAck();//等待应答
    I2CStart();//启动单总线
    I2CSendByte(0x91);//写一个0x91
    I2CWaitAck();//等待应答
    temp = I2CReceiveByte();//读取数据
    I2CSendAck(1);//发送一个非应答信号
    I2CStop();//停止
    return temp;
}

void Da_Write(unsigned char dat)
{
    I2CStart();//启动单总线
    I2CSendByte(0x90);//发送一个0x90,告诉单片机要写数据了
    I2CWaitAck();//等待应答
    I2CSendByte(0x41);//使能DAC转换
    I2CWaitAck();//等待应答
    I2CSendByte(dat);
    I2CWaitAck();//等待应答
    I2CStop();//停止
}
//头文件    
#ifndef _IIC_H
#define _IIC_H

unsigned char Ad_Read(unsigned char addr);//AD读取,要有一个入口参数
void Da_Write(unsigned char dat);

void IIC_Start(void);
void IIC_Stop(void);
bit IIC_WaitAck(void);
void IIC_SendAck(bit ackbit);
void SendByte(unsigned char byt);
unsigned char IIC_RecByte(void);

#endif

工程主函数内容:

1.头文件声明(把需要用到的头文件添加进来)


/*头文件声明区*/
#include <STC15F2K60S2.H>//单片机寄存器专用头文件
#include <Init.h>//初始化底层驱动专用头文件
#include <Led.h>//LED底层驱动专用头文件
#include <Key.h>//按键底层驱动专用头文件
#include <Seg.h>//数码管底层驱动专用头文件
#include "iic.h"//数模转换底层驱动头文件

2.变量声明(把需要用到的所有变量现在这里进行声明)

/*变量声明区*/
unsigned char Key_Val,Key_Old,Key_Down,Key_Up;//按键扫描专用变量
unsigned char Key_Slow_Down;//按键减速专用变量 10ms
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 char Seg_Slow_Down;//数码管减速专用变量
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};//LED显示数据存放数组
//unsigned char dat,dat2;
bit Seg_Disp_Mode;//数码管显示模式变量 0-电压显示界面 1-电压输出界面
float voltage;//实时电压变量
float voltage_Output;//实时电压输出变量
bit Output_Mode;//输出模式专用变量 0-2V 1-随AD输出
bit Seg_Flag = 1;//数码管功能标志位

3.按键处理函数(在这里编写按键控制的函数)

/*键盘处理函数*/
void Key_Proc()
{
    if(Key_Slow_Down)return;
    Key_Slow_Down = 1;//按键减速程序
    
    Key_Val = Key_Read();//读取按下的键码值
    Key_Down = Key_Val & (Key_Val ^ Key_Old);//捕捉下降沿
    Key_Up = ~ Key_Val & (Key_Val ^ Key_Old);//捕捉上升沿
    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;
    }
        
}

4.信息处理函数(需要使用到到的函数进行简单的预处理)


/*信息处理函数*/
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;//随AD输出
    //voltage_Output = Output_Mode?voltage:2;//这个同样可以判断输出电压,使用实现两种电压值输出
    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;//显示U
        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;//点亮小数点
    }
//    //读取的值是上一次转换的结果,读取两个数据时,人为调换一下
//    dat2 = Ad_Read(0x41);//读取AD0x41数据量
//    dat = Ad_Read(0x43);
//    Da_Write(255);
//    
//    Seg_Buf[0] = dat / 100 % 10;
//    Seg_Buf[1] = dat / 10 % 10;
//    Seg_Buf[2] = dat % 10;
//    
//    Seg_Buf[4] = dat2 / 100 % 10;
//    Seg_Buf[5] = dat2 / 10 % 10;
//    Seg_Buf[6] = dat2 % 10;
}

5.其他函数(其他编写的函数,在这里书写会比较方便理解)


/*其他函数*/
void Led_Proc()
{
    unsigned char i;
    Relay(1);//关闭继电器
    Beep(1);//关闭蜂鸣器
    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;
}

6.定时器0中断初始化函数

(这个可以使用STC的定时器计算那里生成c代码,后面要自己添加ET0,EA打开中断)
/*定时器0初始化函数*/
void Timer0Init(void)        //1毫秒@12.000MHz
{
    AUXR &= 0x7F;        //定时器时钟12T模式
    TMOD &= 0xF0;        //设置定时器模式
    TL0 = 0x18;        //设置定时初值
    TH0 = 0xFC;        //设置定时初值
    TF0 = 0;        //清除TF0标志
    TR0 = 1;        //定时器0开始计时
    ET0 = 1;
    EA = 1;
}

7.定时器0中断服务函数


(为了定时执行特定的任务,如此处设置了定时的时间触发了数码管和LED产生特定反应)

/*定时器0中断服务函数*/
void Timer0Serve() 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);//熄灭数码管
        Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
}

8.主函数Main(调用书写的函数实现所需的相应功能)

/*Main*/
void main()
{
    Sys_Init();
    Timer0Init();
    while(1)
    {
        Key_Proc();
        Seg_Proc();
        Led_Proc();
    }
}

其他的详细资料在另一篇PCF8591的笔记,有详细讲解AD数模转换的内容和IIC的使用等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/357621.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32——中断系统和外部中断EXTI

一、中断 1.1中断系统 中断系统是管理和执行中断的逻辑结构&#xff1b; 1.2中断 系统在执行主程序过程中&#xff0c;出现了特定的触发条件&#xff08;触发源&#xff09;&#xff0c;系统停止执行当前程序&#xff0c;转而去执行中断程序&#xff0c;执行完毕后&#xf…

windows系统下启动redis命令

windows系统下启动redis命令 进入redis安装目录 cd redis 输入 redis-server.exe redis.windows.conf 启动redis命令&#xff0c;看是否成功 可能会启动失败&#xff0c;报[1696] 30 Jan 09:46:07.518 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No erro…

云计算底层技术、磁盘技术揭秘虚拟化管理、公有云概述

查看本机是否具备虚拟化支持 硬件辅助虚拟化 处理器里打开 虚拟化Inter VT-x/EPT 或AMD-V 构建虚拟化平台工具软件包 yum 与 dnf Yum和DNF都是用于管理Linux系统中的软件包的工具&#xff0c;但它们在许多方面存在一些差异。以下是一些可能的区别&#xff1a; 依赖解…

运行VUE提示找不到模块validate-engines.js...

原来好好的&#xff0c;突然提示找不到模块validate-engines.js&#xff0c;CMD命令行输入npm -v不是内部或外部命令&#xff0c;node -v可以查看到版本号。 解决&#xff1a; 1. 卸载nodejs&#xff0c;重新下载安装文件&#xff1a;下载nodejs 2. 到目录&#xff1a;C:\Us…

成功解决AttributeError: ‘str‘ object has no attribute ‘decode‘

成功解决AttributeError: ‘str’ object has no attribute ‘decode’. &#x1f335;文章目录&#x1f335; &#x1f333;引言&#x1f333;&#x1f333;报错分析及解决方案&#x1f333;&#x1f333;参考文章&#x1f333;&#x1f333;结尾&#x1f333; &#x1f333;引…

Chiplet,汽车“芯”风向

异构集成、高速互联、算力灵活可扩展正在成为新一轮汽车芯片竞争的焦点。尤其是随着以ChatGPT为代表的大数据、大模型产品在车端的落地&#xff0c;对于芯片的要求还在持续提升。 本周&#xff0c;12家日本汽车制造商&#xff08;包括丰田、日产、本田等&#xff09;、零部件制…

Redis 面试题 | 20.精选Redis高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

三、防御保护---防火墙安全策略篇

三、防御保护---防火墙安全策略篇 一、什么是安全策略二、安全策略的组成1.匹配条件2.动作3.策略标识 三、防火墙的状态检测和会话表1.会话表2.状态检测技术 四、ASPF--隐形通道五、用户认证1.用户认证的分类2.认证方式3.认证策略4.认证域 一、什么是安全策略 传统的包过滤防火…

ORBSLAM3 运行流程 以rgbd_tum.cc函数为例进行分析

一、运行 使用的是D435i相机自己录制的数据。 运行命令&#xff1a; ./Examples/RGB-D/rgbd_tum /opt/vslam/ORB_SLAM3_detailed_comments-dense_map_new/Vocabulary/ORBvoc.txt /opt/vslam/ORB_SLAM3_detailed_comments-dense_map_new/Examples/RGB-D/TUM1.yaml /opt/vsl…

医美诊疗前后要注意的八大诀窍

【记者许家源/综合报导】 随着年龄的增长&#xff0c;许多人都想保持年轻美丽&#xff0c;因此寻求医美诊疗的帮助。然而&#xff0c;进入医美诊所后&#xff0c;你可能会发现&#xff0c;想要打肉毒、除毛等&#xff0c;实际花费和广告中的金额相差甚远。为了避免上当受骗&am…

C# 使用WMI监听进程的启动和关闭

写在前面 Windows Management Instrumentation&#xff08;WMI&#xff09;是用于管理基于 Windows 操作系统的数据和操作的基础结构。具体的API可以查看 WMI编程手册。 WMIC 是WMI的命令行管理工具&#xff0c;使用 WMIC&#xff0c;不但可以管理本地计算机&#xff0c;还可…

Layui + Echarts 5.0

Layui 怎么整合最新版本的 Echarts 5.0&#xff0c;Echarts 4 升级到 5后&#xff0c;有了很大改变&#xff0c;新的配置项4是无法兼容的&#xff0c;所以想要使用新的功能&#xff0c;都需要升级&#xff01; 新建一个echarts.js文件 layui.define(function (exports) {// 这…

【教程】iOS如何抓取HTTP和HTTPS数据包经验分享

&#x1f4f1; 在日常的App开发和研发调研中&#xff0c;对各类App进行深入的研究分析时&#xff0c;我们需要借助专业的抓包应用来协助工作。本文将介绍如何使用iOS手机抓包工具来获取HTTP和HTTPS数据包&#xff0c;并推荐一款实用的抓包应用——克魔助手&#xff0c;希望能够…

Spring: 实体类转换工具总结

文章目录 一、MapStruct1、介绍2、原理3、使用4、问题处理&#xff08;1&#xff09;IDEA编译报错&#xff1a;NullPointerException 一、MapStruct 1、介绍 MapStruct是一个实体类属性映射工具&#xff0c;通过注解的方式实现将一个实体类的属性值映射到另外一个实体类中。在…

前缀和入门(c++语言)

在讲算法之前&#xff0c;我们先来思考一个问题&#xff1a;小明有n个编号为1~n的篮子&#xff0c;每个篮子里装有ai个苹果&#xff0c;求从 x至y 的篮子里的苹果数量之和。 如果没学过前缀和的同学&#xff0c;可能会打出这样的代码&#xff1a; 这种算法要得出一个区间之和&…

隐马尔可夫模型系列——(六)总结与展望

一、总结&#xff1a; 隐马尔可夫模型&#xff08;Hidden Markov Model&#xff0c;HMM&#xff09;是一种用于建模序列数据的统计模型&#xff0c;在语音识别、自然语言处理、金融领域等多个领域都有广泛的应用。其优势包括可以处理动态序列数据、具有一定的鲁棒性、可以灵活…

阿里云服务器2024年最新优惠价格表,轻量应用服务器61元起,云服务器99元起

阿里云服务器2024年最新优惠价格是多少&#xff1f;不同时期阿里云服务器的租用价格不同&#xff0c;进入2024年&#xff0c;阿里云服务器的优惠价格也有所变动&#xff0c;共享型云服务器2核2G最低还是只要99元1年&#xff0c;独享型云服务器2核4G只要199元1年&#xff0c;而轻…

复杂SQL治理实践 | 京东物流技术团队

一、前言 软件在持续的开发和维护过程中&#xff0c;会不断添加新功能和修复旧的缺陷&#xff0c;这往往伴随着代码的快速增长和复杂性的提升。若代码库没有得到良好的管理和重构&#xff0c;就可能积累大量的技术债务&#xff0c;包括不一致的设计、冗余代码、过时的库和框架…

探讨UI自动化测试几步骤

随着软件开发的不断发展&#xff0c;UI自动化测试变得越来越重要&#xff0c;它能够提高测试效率、降低人为错误&#xff0c;并确保软件交付的质量。本文将介绍UI自动化测试的一般步骤和一些最佳实践&#xff0c;以帮助开发团队更好地实施自动化测试。 需求分析和选择测试工具&…

RabbitMQ基本使用,docker安装RabbitMQ,SpringBoot整合RabbitMQ

1.拉取镜像 docker pull rabbitmq:3.9.15-management2.运行容器 docker run -d --hostname rabbit1 --name myrabbit1 -p 15672:15672 -p 5672:5672 -e RABBITMQ_ERLANG_COOKIErabbitcookie rabbitmq:3.9.15-management3.访问地址 安装ip加端口号 http://192.168.123.3:156…