第一章 硬件部分
1.1 电路的组成部分
1.1.1 译码器和锁存器
具体可回顾之前LED灯的文章:
https://blog.csdn.net/weixin_63568691/article/details/130660096
1.1.2 共阳极数码管:
- 原理图:
- 功能:
(1)可以把数码管可以看作多个LED灯组成的电路,每个数字由8个LED组成
对应情况:
(2)什么叫共阳极?可以理解为LED灯的一端接了VCC,只需要在另一端输入低电平就能将他点亮
(3)共阳极数码管1-F对应的十六进制数(比赛时数据包会有基本的,但是特殊的字母或符号是没有的)
code unsigned char Seg_Table[] =
{
0xc0,
//0
0xf9,
//1
0xa4,
//2
0xb0,
//3
0x99,
//4
0x92,
//5
0x82,
//6
0xf8,
//7
0x80,
//8
0x90,
//9
0x88,
//A
0x83,
//b
0xc6,
//C
0xa1,
//d
0x86,
//E
0x8e
//F
};
1.2 整体电路
(1)由上图可知com口(位选)选择需要亮的数码管,再通过a-g口(段选)确定要选择的数字,dp选择是否显示小数点
(2)其中位选为Y6C,段选为Y7C,即与LED控制类似
第二章 具体实验
2.1 实验内容
前四位显示年份“2022”,接着两位是分隔符“–”,最后两位是月份从一月份开始增加到十二月份,最后循环往复。
2.2 什么是数码管动态显示?
(1)我们如果曾经用过手机照一些分辨率较低屏幕,应该会见到闪烁的屏幕,也就会理解动态显示思路
(2)为什么我们一定要用动态显示呢?如果用静态显示思路,那么每个显示部分都有独立的段选端口和独立的位选端口,以我们八位数码管为例:每一位都要八位的段选和一位的位选,一共就需要七十二位,这样就太浪费资源了
(3)原理:利用人的视觉残留和数码管的余辉效应(1-2ms),我们可以让每个显示部分公用一个段选端口,只需要在极短时间不断改变位选,在人的眼中就会出现八位同时显示的情况
2.3 无操作系统代码
注:一开始接触可以就写这个代码,过省赛应该是没问题的
2.3.1 代码
// 使用程序前,将J13调整为IO模式(2-3脚短接)
#include "reg52.h"
#include "stdio.h"
#define u8 unsigned char
#define u16 unsigned int
// 延时函数(最小约1ms@12MHz)
void Delay_1ms(u16 num)
{
u16 i;
while(num--)
for(i=0; i<628; i++);
}
void Delay_10us(u16 num)
{
u16 i;
while(num--)
for(i=0; i<3; i++);
}
/*
输入变量:无
输出变量:无
功能:关闭所有外设
*/
void Close_All(void)
{
//关闭蜂鸣器和继电器
P0 = 0x00;
P2 = (P2 & 0x1f) | 0xA0;
P2 &= 0x1f;
//关闭LED灯
P0 = 0xff;
P2 = (P2 & 0x1F) | 0x80;
P2 &= 0x1f;
}
u8 SEG_COT[9];
u8 SEG_Code[8];
u8 SEF_POS = 0;
u16 SEG_delay;
void SEG_TSL(u8 *input,u8 *output);
void SEG_Show(u8 num,u16 PIS);
void Timer_0_Init(u16 time);
void SEG_Proc();
// 主函数
void main(void)
{
Close_All();
Timer_0_Init(1000);//1ms
while(1)
{
SEG_Proc();
}
}
void SEG_Proc()
{
u16 year = 2023;
static u8 month=1;
if(SEG_delay)return;
SEG_delay = 1;
sprintf(SEG_COT, "%4u--%02u", year,(u16)month);
SEG_TSL(SEG_COT,SEG_Code);
if(month == 12)
month = 0;
month++;
}
/***************************数码管*****************************/
/*
输入变量:input,输入字符数组;output:输出16进制数数组
输出变量:无
功能:将字符串转化为对应数码管显示的16进制数
注意:记得定义数组——u8 SEG_COT[9];u8 SEG_Code[8];
*/
void SEG_TSL(u8 *input,u8 *output)
{
u8 i=0,temp=0,j=0;
for(i=0;i<8;i++,j++)
{
switch(input[j])
{
case '0': temp = 0xc0; break;
case '1': temp = 0xf9; break;
case '2': temp = 0xa4; break;
case '3': temp = 0xb0; break;
case '4': temp = 0x99; break;
case '5': temp = 0x92; break;
case '6': temp = 0x82; break;
case '7': temp = 0xf8; break;
case '8': temp = 0x80; break;
case '9': temp = 0x90; break;
case 'A': temp = 0x88; break;
case 'B': temp = 0x83; break;
case 'C': temp = 0xc6; break;
case 'D': temp = 0xA1; break;
case 'E': temp = 0x86; break;
case 'F': temp = 0x8E; break;
case 'H': temp = 0x89; break;
case 'L': temp = 0xC7; break;
case 'N': temp = 0xC8; break;
case 'P': temp = 0x8c; break;
case 'U': temp = 0xC1; break;
case '-': temp = 0xbf; break;
case ' ': temp = 0xff; break;
default: temp = 0xff;
}
if(input[j+1] == ".")
{
temp &= 0x7f;
j++;
}
output[i] = temp;
}
}
/*
输入变量:num,要显示数据;PIS,显示位置,从左到右分别为0-7
输出变量:无
功能:操作138译码器,4-7分别对应Y4-Y7,其余都会使译码器不起作用
注意:需要把存放从1-f对应的16进制数数组也移植
*/
void SEG_Show(u8 num,u16 PIS)
{
//消影
P0 = 0xff;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
//改变显示位置
P0 = 0x01<<PIS;
P2 = (P2 & 0x1f) | 0xC0;
P2 &= 0x1f;
//改变数据
P0 = num;
P2 = (P2 & 0x1f) | 0xE0;
P2 &= 0x1f;
}
/***************************定时器*****************************/
/*
输入变量:定时时长___us
输出变量:无
功能:配置并开启定时器0
*/
void Timer_0_Init(u16 time)
{
//12T模式
AUXR &= 0x7f;
//定时器0 模式0
TMOD &= 0xf0;
//设置初值
TH0 = (65536-time)/256;
TL0 = (65536-time)%256;
//打开中断
ET0 = 1;
EA = 1;
//开始计数
TR0 = 1;
}
void Timer_0_IT(void) interrupt 1
{
if(SEG_delay++ == 1000)SEG_delay = 0;//1s
SEG_Show(SEG_Code[SEF_POS],SEF_POS);
if(++SEF_POS == 8 )SEF_POS = 0;
}
2.3 RTX51代码
国赛必备,优点是可以在延时的时候畅通无阻的执行其他东西
#include <rtx51tny.h>
#include <STC15F2K60S2.H>
#include <stdio.H>
#define u8 **加粗样式**unsigned char
#define u16 unsigned int
u8 COD[8],COT[9],PSI;
code unsigned char Seg_Table[] =
{
0xc0,
//0
0xf9,
//1
0xa4,
//2
0xb0,
//3
0x99,
//4
0x92,
//5
0x82,
//6
0xf8,
//7
0x80,
//8
0x90,
//9
0x88,
//A
0x83,
//b
0xc6,
//C
0xa1,
//d
0x86,
//E
0x8e
//F
};
void All_Close();
void SEG_TSL(u8 *input,u8 *output);
void SEG_Show(u8 COD,u8 PSI);
void Startup() _task_ 0
{
All_Close();
os_create_task(1);
os_create_task(2);
os_delete_task(0);
}
void task1() _task_ 1
{
u8 mouth;
while(1)
{
if(++mouth == 13)mouth = 1;
sprintf(COT,"2022--%02u",(u16)mouth);
SEG_TSL(COT,COD);
os_wait2(K_IVL,250);
os_wait2(K_IVL,250);
os_wait2(K_IVL,250);
os_wait2(K_IVL,247);
}
}
void task2() _task_ 2
{
while(1)
{
SEG_Show(COD[PSI],PSI);
if(++PSI == 8)PSI = 0;
os_wait2(K_IVL,2);
}
}
/******************************/
//选择锁存器,输出数据
void Select(u8 cs,u8 s_data)
{
P0 = s_data;
P2 = P2 & 0x0f | (cs<<5);
P2 &= 0x0f;
}
//关闭外设
void All_Close()
{
//关闭蜂鸣器
Select(5,0x00);
//关闭LED
Select(4,0xff);
//关闭数码管
Select(7,0xff);
}
//输入字符串,输出数码管段码
void SEG_TSL(u8 *input,u8 *output)
{
u8 i=0;
for(i=0;i<8;i++)
{
switch(input[i])
{
case '0':output[i] = Seg_Table[0];break;
case '1':output[i] = Seg_Table[1];break;
case '2':output[i] = Seg_Table[2];break;
case '3':output[i] = Seg_Table[3];break;
case '4':output[i] = Seg_Table[4];break;
case '5':output[i] = Seg_Table[5];break;
case '6':output[i] = Seg_Table[6];break;
case '7':output[i] = Seg_Table[7];break;
case '8':output[i] = Seg_Table[8];break;
case '9':output[i] = Seg_Table[9];break;
case '-':output[i] = ~0x40;break;
default:output[i] = 0xff;
}
}
}
void SEG_Show(u8 COD,u8 PSI)
{
//消隐
Select(7,0xff);
//位选
Select(6,0x01<<PSI);
//段选
Select(7,COD);
}