蓝桥杯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的使用等等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推荐一款Linux、数据库、Redis、MongoDB统一管理平台!

官方演示 状态查看 ssh 终端 文件操作 数据库操作 sql 编辑器 在线增删改查数据 Redis 操作 Mongo 操作 系统管理 账号管理 角色管理 资源管理 一.安装 1.下载安装包 cd /opt wget https://gitee.com/dromara/mayfly-go/releases/download/v1.7.1/mayfly-go-linux-amd64.zi…

CES 2024:AI赋能机器人,国产机器人更亮眼

原创 | 文 BFT机器人 一年一度的国际消费电子展(CES)又与我们见面了。作为全球消费电子和科技创新的盛会&#xff0c;CES每年都吸引着无数目光。今年&#xff0c;AI赋能机器人成为展会的一大亮点&#xff0c;而国产机器人更是凭借其创新技术和实用功能&#xff0c;成为全场焦点…

使用QT实现播放gstreamer的命令(二)

一、前言 上一篇文章写到了&#xff0c;如何快速使用C来执行gstreamer的命令&#xff0c;如何在QT中显示gstreamer的画面&#xff0c;原文如下&#xff1a; https://blog.csdn.net/Alon1787/article/details/135107958 二、近期的其他发现&#xff1a; 1.gstreamer的画面显示在…

蓝桥杯AT24C02问题记录

问题1&#xff1a;从这个图片上可以看出这两个在IIC的.c文件里延时时间不一样&#xff0c;第一张图使用了15个_nop_(); 12M晶振机器周期是 1/12M*121uS&#xff1b;nop()要延时1个指令周期。延时时间不对会对时序产生影响&#xff0c;时序不对&#xff0c;则AT24C02有没被使用…

DAY34:贪心算法part、1005\134\135

Leetcode: 1005 K次取反后最大化的数组和 基本思路 这道题的思路比较简单&#xff0c;如果有负数&#xff0c;就先把最大的负数转化成正数&#xff0c;如果全部转换完之后还有k剩余&#xff0c;就将最小的正数反复正负变化。但是需要注意一点代码的写法。 代码注意点 定义绝…

关于Spring Boot和MyBatis常见的十道面试题

拦截器和过滤器有什么区别&#xff1f; 拦截器&#xff08;Interceptor&#xff09;和过滤器&#xff08;Filter&#xff09;都是用于在请求道道目标资源的之前或之后进行处理的组件。主要区别有以下几点&#xff1a; 依赖对象不同&#xff1a;过滤器是来时Servlet&#xff0…

input、textarea禁止输入空格,并绑定回车事件

一、原生环境 1. 禁止输入空格 <input type"text" v-model"value" οnkeyup"this.valuethis.value.replace(/\s/g,)" /><textarea type"text" v-model"value" οnkeyup"this.valuethis.value.replace(/\s/g…

IDEA安装MyBatisX插件

IDEA工具在开发人员中经常使用&#xff0c;从dao层到xml文件对应的查看很费劲&#xff0c;这时候就有相应的插件工具出现了MyBatisX。他的好处如下&#xff1a; mapper and xml can jump back and forth mybatis.xml,mapper.xml prompt mapper and xml support auto prompt lik…

将多个excel文件中的特定数据汇总到一个excel中

比如5000个excel文件中都有1月2日的交易数据。现在需要将每个文件中1月2日的数据提出来&#xff0c;组成一个新的excel文件&#xff0c;即1月2日的交易数据文件&#xff0c;以1月2日命名。下面的程序是将5000只股票1月2日的交易数据提出来&#xff0c;形成一个1月2日所有股票的…

Netty核心——Reactor下篇(十)

任务队列中的Task有3种典型使用场景 用户程序自定义的普通任务 比如有一个非常耗时长的业务 异步执行提交该Channel对应的NioEventLoop的TaskQueue中 用户自定义定时任务 该任务提交到scheduleTaskQueue中 非当前Reactor线程调用Channel的各种方法 例如在推送系统的业务线程…

大数据StarRocks(八):资源隔离实战

前言 自 2.2 版本起&#xff0c;StarRocks 支持资源组管理&#xff0c;集群可以通过设置资源组&#xff08;Resource Group&#xff09;的方式限制查询对资源的消耗&#xff0c;实现多租户之间的资源隔离与合理利用。在 2.3 版本中&#xff0c;StarRocks 支持限制大查询&#…

Apache Paimon基础记录

基本都是在官网的学习&#xff0c;简单记录一下其中的核心特点 Apache Paimon 官网 Apache Paimon | Apache Paimon 根据官网介绍去快速了解 paimon 是用来设计做什么&#xff0c;可以做什么&#xff0c;对比与其他数据湖有什么特点&#xff0c;如何使用 Paimon 特点 前身…

酒鬼酒2024年展望:稳发展动能,迈入恢复性增长轨道

文 | 琥珀酒研社 作者 | 渡过 最近几个月来&#xff0c;白酒估值回落到近十年来低位&#xff0c;反映出了整个白酒行业的市场低迷和虚弱现状。不管是头部企业五粮液、泸州老窖&#xff0c;还是区域酒企口子窖、金种子酒等&#xff0c;最近都通过“回购”或“增持”&#xff0…

【C语言】【力扣】刷题小白的疑问

一、力扣做题时的答案&#xff0c;没有完整的框架 疑问&#xff1a; 在学习C语言的初始&#xff0c;就知道C语言程序离不开下面这个框架&#xff0c;为什么力扣题的解答往往没有这个框架&#xff1f; #include <stdio.h>int main() {return 0; } 解答&#xff1a; 力扣平…

常用直线检测算法

概述 在计算机视觉领域&#xff0c;我们经常需要做一些特殊的任务&#xff0c;而这些任务中经常会用到直线检测算法&#xff0c;比如车道线检测、长度测量等。– 资料 直线检测算法汇总_技术挖掘者的博客-CSDN博客_直线检测算法 直线检测算法博文中缺失的几个源码(Hough_lin…

联想乐商店更新安卓APK错误处理

当你点击“重新提交”&#xff0c;联想开放平台会卡死 其实他们的网页是有BUG的。HTTP GET appDetail请求会有个服务器内部错误 联系了联想客服&#xff0c;他们的绕过去方案是&#xff0c;你要选择“已上架” 然后再更新版本就可以了

手把手带你死磕ORBSLAM3源代码(六十三) LocalMapping.cc LocalMapping

目录 一.前言 二.代码 2.1 完整代码 一.前言 LocalMapping类主要负责处理局部地图的构建和优化。它接收来自相机的图像数据,提取特征点,并与先前帧的特征点进行匹配,以估计相机的运动并更新地图。 以下是关于代码的一些详细说明: LocalMapping的构造函数(LocalMappin…

Alzet 代理商,你知道是什么吗?

代理商、供应商傻傻分不清楚。在购买产品时或者在搜索商品时&#xff0c;有些产品会出现代理商、供应商的字样。你知道它们的区别么&#xff1f; 下面&#xff0c;我们就简单了解下Alzet 供应商与Alzet 代理商分别是什么&#xff1f;以及Alzet 渗透泵的产品概况吧。 Alzet 供…

【Python机器学习系列】建立XGBoost模型预测心脏疾病(完整实现过程)

一、引言 前文回顾&#xff1a; 一文彻底搞懂机器学习中的归一化与反归一化问题 【Python机器学习系列】一文彻底搞懂机器学习中表格数据的输入形式&#xff08;理论源码&#xff09; 【Python机器学习系列】一文带你了解机器学习中的Pipeline管道机制&#xff08;理论源码…

【Python】03快速上手爬虫案例三:搞定药师帮

文章目录 前言1、破解验证码2、获取数据 前言 提示&#xff1a;通过用户名、密码、搞定验证码&#xff0c;登录进药师帮网站&#xff0c;然后抓取想要的数据。 爬取数据&#xff0c;最终效果图&#xff1a; 1、破解验证码 使用药师帮测试系统&#xff1a;https://dianrc.ysb…