跟 冲哥教学视频学习STC——记录每一天的成长| 必须立即送强大的实验箱支持啊 !
第1集 认识单片机1.了解什么是单片机,学习到新名词“MCU”,即Mirco Compute Unit。
2.单片机能做什么,生活中的许多电器都与单片机有关,
单片机用库函数好还是寄存器好?建议用寄存器,因为这是最底层,库函数也是由使用者自己来定义的,库函数内部也是通过调用各种寄存器来实现的,只是把他打包成了一个功能单体,简化我们主程序的篇幅。
第2集 了解硬件
主要介绍STC32GK128实验箱的各种硬件与外部电路,大致可分为两类:输入与输出,如矩阵键盘、独立按键,用来检测I/O口的高低电平,这里是输入,点亮1个LED或数码管,这里就是输出。
第3集 开发环境搭建和软件下载
1.STC-ISP软件的下载:这里是将编译好的HEX程序写入到单片机的工作软件,内部就是一个电子工程师常用软件集合,有串口调试助手,芯片手册下载等等。
2.要学会在官网查询对应芯片的手册,要学会利用PDF软件的查询功能,通过关键字快速找到自己想要的内容。
3.安装keil,安装C251编译环境,这里最好不要更改安装目录,直接装到C盘下,C51是开发8051类的,C51与C251可以和谐共存。
4.在STC-ISP软件中选择自己的芯片,在“keil仿真设置”中,添加自己芯片型号和头文件到keil中,可在C:\Keil_v5\C251\INC\STC中找到自己的头文件。
5.掌握P3.2+电源键的操作模式,这就是STC的硬件USB直接下载,进入单片机和下载模式,将程序下载到单片机,这里特别要注意“输入用户程序运行时的IRC频率”要与主程序中的一致,否则程序内的延时会不准确。
第4集 建立工程点亮第一颗LED
1.任何一个程序都是从一灯大师开始的,LED点亮原理就是在LED两端加上一个电压,当然这个电压要在LED额定电压以下。即如果LED的负极接到GND上,那么LED的正极接到P4.0上,P4.0要输出高电平才能点亮。
反之,如果LED的正极接到VCC上,那么LED的负极接到P4.0上,那么P4.0要输出低电平才能点亮。
因为我用的是追风剑,所以就没有视频里的P4.0来要先控制三极管的接通,从而来控制P6.0~P6.7这8个LED的点亮。这个P4.0就相当开一个闸门。当P4.0为低电平时,P6.0和P6.7可用来点亮LED,P4.0为高电平时,
P6.0和P6.7可作为数字IO使用。
2.USB-HID和USB-CDC之不停电下载,高手必须会。这里主要就是在工程里Options for Target 'Target1’要将Memory Mode修改为“xsmall”,L251 Misc中添加“REMOVEUNUSED”,添加好对应的USB库文件
就可以了,特别注意如果单片机是第一次刷入这个程序,那么还是要按P3.2+电源键进行下载模式把程序下载进去才行,后面勾选“每次下载前自动发送自定义命令”,就可直接点编程下载了。
具体操作我在这个学习打卡区也做了截图说明,这里不再赘述了。
3.这集最重要的就是IO口配置模式了,因为STC单片机的IO口在使用前需要配置工作模式,每个IO口可看做PnM1和PnM0,共有4种模式:00(准双向口),01(推挽输出),10(高阻输入),11(开漏输出),
例:P2口的P2.3为推挽输出,其余口为准双向口
P2.7P2.6P2.5P2.4P2.3P2.2P2.1P2.0HEX
P2M1 0 0 0 0 0 0 0 0 0x00,8位二进制用2位十六进制表示
P2M0 0 0 0 0 1 0 0 0 0x08
语句:P2M1 = 0x00;P2M0 = 0x08;
知道这个后自己都可以用Excel自己做个快速配置IO口模式了,当然万能的STC-ISP也给你配置了I/O配置工具,在里面勾选就可以自动生成语句。
第5集 C语言运算符和进制数入门上
1.C语言printf函数的实现,这里要注意stc32_stc8_usb.h头文件内的#define PRINTF_HID //printf输出直接重定向到USB HID接口要把这句注释取消
主函数添加程序
if(DeviceState != DEVSTATE_CONFIGURED)//等待USB完成配置
continue;
if (bUsbOutReady) //如果接收到数据
{
usb_OUT_done(); //接收应答(固定格式)
// 想要执行的语句可以放在这里
}
2.格式字符%的含义与使用,转义字符\的释义与使用。
3.printf("当前温度:%.2f当前湿度:%2.2f",11.2,55.2); 这里就是要注意“ ”内的%也几个,后面的数值也要有几个。
4.ASCII字符码表的查询,冲哥附了一张截图,把表格的形式分为高8位与低16位,共128个字符,先写高位,再写低位,如“30”表示字符‘0’,注意30是HEX,换成DEC是48。
5.数的进制主要讲了二进制,十进制,十六进制,进制简单来讲就是逢几进1,如二进制基本数是0和1,八进制基本数是0~7,十进制基本数是0~9,十六进制基本数是0~F。
STC的P口可以看成由8位二进数组成,每1位代表1个I/O,如P2 = 00000001;这里就是表示P2.0 = 1;其他P2.1~P2.7为0,也可写成P2 = 0x01;与P2.0 = 1;是一样效果。
第5集 C语言运算符和进制数入门下
1.C语言运算符主要分为三类:算术运算符(+-*/%)、位运行符(>>,<<,&,|,^,~)、赋值运行符(+=,-=,*=,/=,%=,>>=,<<=,&=,|=,^=,~=),
a>>2,a<<2,左移和右移运算符注意“操作数在左侧,移的位数在右侧”。
2.数据的基本类型
bit:0或1;
signed:有符号型,有正负之分
unsigned:无符号型,只有正值,我们经常听到这是支持几位的运算,或者最大几位的数据类型。
无符号型:如果是8位,则unsigned值最大是2的8次方,为255,如果是16位,则为2的16次方,最大为65535,如果是32位,则为2的32次方,最大为4294967295。
有符号型:如果是8位,则signed值范围为-127~128,如果是16位,则signed值范围为-32768~32767,如果是32位,则signed值范围为-2147483648~2147483647。
第6集 LED闪烁和花式点灯上
1.这1集主要是讲自定义函数,以延时函数为例,可以定义延时时间,让我们的LED变得可以闪烁,具有更多的玩法。
while函数的用法是()内的条件为1时执行{ }内的语句,条件为0时跳出循环。
也讲了--a与a--的差别:--a先运算,即a的数值会立刻更新,而a--会在下次循环时才会更新数值。如果想及时计算就用--a,--a比a--少执行1次。
2.函数的使用
1).函数定义
返回值数据类型 函数名称(数据类型 入口参数1,数据类型 入口参数2,...,参数数据类型 入口参数n,)
{
// 函数要执行的功能语句;
}
2).函数声明
返回值函数名称 (数据类型 入口参数);
3).函数引用
函数名称(入口参数);入口参数这里要填上面声明的具体数值,这个数值要与数据类型一致。
同时要注意声明了几个入口参数,在定义和调用时也要与之对应,不能多或少。
3.模块化编程
这里是把你想要实现的功能封装到1个或多个函数内,通过对函数的引用,可以减少在主程序中循环写段语句,有助于精简主程序,同时也方便
程序的阅读。比如在多个地方需要调用延时函数,而这个延时函数又带有入口参数,我们通过修改入口参数的值就可以实现不同的延时值。程序
就会被我们玩出花来了。
再拓展一下,之前玩过一段ESP8266的开发板,这个是通过Arduino来编程的,网上有很多开发者会开发不同的功能函数,把他们封装成库函数。
比如用几个IO点来驱动LCD1602显示屏,print("HELLO")就可以直接驱动1602显示屏了,你所看到的都是开发者已经帮你写好底层了,你只要按
照他库函数里定义的关键字和参数,按照语法格式就可以了,可以说非常方便,但是有个缺陷就是你不了解底层的代码函数。第1集中冲哥建议
用寄存器调用,因为用寄存器我们可以来编写成具体功能的库函数。
模块化编程是1个未来的趋势,随着单片机的功能越来越强大,要实现的功能也越大,需要多人来进行开发,各人把负责的功能模块写好,把语句
的语法共享出来,更加方便使用者来进行调试。
新建xxx.c和xxx.h文件,代表一个功能块
xxx.h格式
#ifndeg __xxx_h
#define __xxx_h
调用头文件
函数声明
#endif
xxx.c格式
#include "xxx.h"
函数定义
添加文件一定要记得引用路径和将xxx.c添加到工程里,在main.c主程序中要调用与之对应的xxx.h文件,
这样就完成了模块化编程。
大家如果有想参考模块编程可以参考下我写的程序。为什么我要在这打卡呢,就是证明自己确实把冲哥的视频看进去了,并且能举一反三,得出
自己的一些心得体会。
在这1课里,我自己定义了对2位数进行操作的四则运算的函数,通过对LED的闪烁,自己定义闪烁的频率与延时,这里给自己留个课后任务,后
面要去想想怎么做个呼吸灯,这牵涉到PWM了。
网购的STC32GK128 PDIP40到货了,手搓开发板搞起来!
1.用P2.~P2.7做流水灯,采用一灯大师贴子里的用SS8550三极管基极接到P4.5端口,发射极接MCU-VCC,集电极接LED共阳极。当P4.5为低电平时,发射极与基极导通,电流饱合后,发射极与集电极导通,P2.0~P2.7输出低电平时,对应的LED点亮,可做流水灯实验,P4.5为高电平时,P2.0~P2.7可做数字IO实验。为了美观,这里第一次尝试用5.1K的贴片电阻焊接,没有用色环电阻,效果也是一样。
2.其他是参观芯片手册的最小系统开发板来的,用的104PF和47uF电容,可以消除电源噪音。电源按钮是采用鼠标按键的常闭端,P3.2是接的10K上接电阻,按钮按下时P3.2会与GND接通拉低。
USB是用的以前老式打印机的大的USB口,刚好手上有多的数据线,没有用那种电脑主机端的USB母头。
3.后面需要再完善,增加1个数码管和蜂鸣器,矩阵键盘也安排上。
4.现在用的是硬件USB下载,后面用USB-TTL/USB转RS232串口看下能不能正常烧录程序。
5.焊接过程花费5H,动手过程其乐无穷!
零件清单:
STC32GK128 PDIP40封装*1块
40PIN IC底座*1
2.54mm排针*2排
8Pin的2.54mm排针*2
3mm发黄光LED*8个
3mm发绿光LED*1个
5.1K贴片电阻*9个
《---最小系统开发板需要零件
270欧电阻*1个,没有300欧电阻,用270代替的
22欧电阻*2个
10K电阻*1个
0.1uf(104pf)电容*2个
47uf电容*1个,没有22uf,用16V 47uf电容代替的
以上测试都可正常使用,程序下载无问题。
-------------------------------》
USB母座*1
三极管SS8550*1
22
成品操作视频,一切正常。上电一瞬间,内心有一种成就感!
更新:自制STC32G开发板更新数码管和蜂鸣器,这个小小的洞洞板就先开发到这了!附上成品照片。
后期更多配件用外挂形式来做实验吧。
在焊接调试过程中,也不是一把成的,也有很多的小插曲,比如端口写错了,元件少焊1个正极或负极啦,永远相信冲哥说的,不能半途而废,要多去细心找原因,总有解决的一天。
38
点亮成品效果图:LED流水灯、3位共阴极数码管、蜂鸣器都可正常点亮工作。附上自己编写的代码,均测试通过。
/*--------------------------------------------------------------------------------------
此程序是为自制STC32G开发板所编写,其中单片机芯片采用PDIP40封装。
1.本开发板可采用硬件USB下载程序,即P3.2搭配电源键的操作模式;
2.P2.0~P2.7为流水灯实验,P4.5为LED流水灯的总开关,只有P4.5为低电平时,LED才能点亮;
3.P0.0~P0.7为数码管段码,高电平输出有效,P1.0,P1.1,P1.3为数码管的3个段码,共阴极为
低电平有效;
4.P5.4为接蜂鸣器,参照STC32G实验箱V9.4增加。
编写日期:2024年2月26日 版本:V01 开发者:dumon
--------------------------------------------------------------------------------------*/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义1个主时钟为24MHz
// 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
// 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
sbit ON_LED = P4^5; // 点灯总开关,三极管是小电流控制大电流的开关
sbit LED_1 = P2^0; // 流水灯LED1
sbit LED_2 = P2^1; // 流水灯LED2
sbit LED_3 = P2^2; // 流水灯LED3
sbit LED_4 = P2^3; // 流水灯LED4
sbit LED_5 = P2^4; // 流水灯LED5
sbit LED_6 = P2^5; // 流水灯LED6
sbit LED_7 = P2^6; // 流水灯LED7
sbit LED_8 = P2^7; // 流水灯LED8
// 数码管相关说明,P0.0~P0.7为数码管的8个段码,P1.0,P1.1,P1.3为3个位码,共阴极数码管
// 位码要接低电平、段码接高电平,共阳极数码管位码要接高电平、段码接低电平
sbit DIG_A = P0^0; // 数码管段码‘A’
sbit DIG_B = P0^0; // 数码管段码‘B’
sbit DIG_C = P0^0; // 数码管段码‘C’
sbit DIG_D = P0^0; // 数码管段码‘D’
sbit DIG_E = P0^0; // 数码管段码‘E’
sbit DIG_F = P0^0; // 数码管段码‘F’
sbit DIG_G = P0^0; // 数码管段码‘G’
sbit DIG_P = P0^0; // 数码管段码‘dP’
sbit SEG_1 = P1^0; // 第1位数码管位码
sbit SEG_2 = P1^1; // 第2位数码管位码
sbit SEG_3 = P1^3; // 第3位数码管位码
// 蜂鸣器参照STC32G实验箱V9.4增加,P5.4输出低电平接三极管基极,同流水灯控制原理,
// 在蜂鸣器负责两端并联反接1个二极管,防止短路
sbit BEEP = P5^4; // 蜂鸣器端口
// 数组变量
u8 LED_Blinker = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
u8 DIG_Number = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, /*不带小数点*/
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF, /*带小数点*/
0x00 /*全部熄灭*/};
/*
P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0 BIN HEX
不带小数 DP G F E D C B A
'0'0 0 1 1 1 1 1 1 0011 1111 0x3F
'1'0 0 0 0 0 1 1 0 0000 0110 0x06
'2'0 1 0 1 1 0 1 1 0101 1011 0x5B
'3'0 1 0 0 1 1 1 1 0100 1111 0x4F
'4'0 1 1 0 0 1 1 0 0110 0110 0x66
'5'0 1 1 0 1 1 0 1 0110 1101 0x6D
'6'0 1 1 1 1 1 0 1 0111 1101 0x7D
'7'0 0 0 0 0 1 1 1 0000 0111 0x07
'8'0 1 1 1 1 1 1 1 0111 1111 0x7F
'9'0 1 1 0 1 1 1 1 0110 1111 0x6F
带小数就把P0.7全部置1,再重新计算8位二进制数的HEX值
*/
u8 SEG_Number= {0xFE,0xFD,0xF7};
u8 a,b,c;
void delay_ms(u16 ms)
{
u16 i;
do{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
void main()
{
WTST = 1;
// IO口模式配置
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
ON_LED= 1;
BEEP = 1;
while(1)
{
while(a<3)
{
P1 = SEG_Number;
while(b<21)
{
P0 = DIG_Number;
delay_ms(100);
b++;
}
b = 0;
a++;
}
a = 0;
ON_LED = 0;
for(c=0; c<8; c++)
{
P2 = LED_Blinker;
delay_ms(200);
}
ON_LED = 1;
BEEP = 0;
delay_ms(50);
BEEP = 1;
}
}
第9集、第10集 数码管的静态显示和动态显示
第9集和第10集主要讲了用单片机来驱动数码管显示,怎么来看待数码管呢,其实他就是一堆LED的组合,只是把他摆成了个数字8的形状,而通过点亮这个数字8的不同笔划,我们可以得到一系列的字符表,如数字“0~9”,甚至还有字母"a~z",这个就牵涉到7段字符表,这里就不再多述了。本质就是7个LED来组成字符,这7个LED要么是共阳极(正极),要么是共阴极(负极),大家在接线的时候一定要特别注意。并且这个LED的耐压值和电流值较小,所以大家也一定要串联1个限流电阻,一般是2K~5K,要看下数码管的显示亮度来选择合适的电阻。
数码管分为段码和位码,段码就是每个LED单独的那头,而位码就是共阴或共阳的那头,数码管有多少位就代表它有多少个位码,如常用的四位共阴数码管,就是有4个位码,段码一般是8个,包含右下角那个小数点。注意了段码的ABCDEFG段是分别并联起来的。可以看上面贴子的3位数码管图
怎么点亮相应的数码管和想要显示的字符呢?需要将上面的位码与段码组合,先讲下静态显示,以四位共阴极数码管为例,有以下步骤,
第1,你想要显示在第几个位码,如第2位,那么你要把这第2位要输出低电平(共阻极数码管么),
第2,你想要在第2位上显示什么字符,如数字”1“,数字1要显示,B段和C段LED要点亮,对应的B和C段要输出高电平,这样就点亮你想要显示的数码管字符了。
上面是只在某一位上显示1个字符,如果想要在不同位上显示不同的数字,那应该怎么办呢,这就牵涉到动态显示了。
我们人眼的结构很特别,有一种物理现象叫视觉残留,举个例子,我们人眼盯着明亮的白炽灯泡观看一段时间(10s),当灯突然熄灭后,人眼还有一瞬间还能感到明亮,这就叫视觉残留,所以关灯的时候先提前闭上眼睛就不会有抓瞎的感觉,能提前适应黑暗吧。
而数码管的动态显示呢,就是利用视觉差,从1个字符移到另1个字符用时极短,从第1位到第4位总时间不超过20s,人眼看到的就是同时4个字符。
视频里说了,ABCDEFG段是并联的,比如说时同给4个位码低电平,第1位AB高电平,第2位CD高电平,第3位EF高电平,第4位G高电平,那得到的显示就是”8888“。
记住1个原则,动态显示时,不能是2个或多个位码同时高电平,这样会显示乱码,只能是从第1位输出,并配合相应的段码输出后,延时2ms,再转移到第2位,依次循环,从第1位到最后1位的总累计时间不能超过20ms,这就是动态效果。
本课程我运用自己学到的知识有while和for循环,以及数组的应用,大大减少了程序的篇幅,提高程序的可阅读性。
这里也讲下自己对两个条件判断语句的心得体会:
条件判断初始化
while(条件判断式) // 条件判断式为0时跳出循环,为1时执行循环语句内容,这里注意一定要在语句内要有能跳出循环的语句,否则会形成死循环
{
循环语句内容;
能跳出循环的语句;
}
如:
a = 0 ;
while(a<3)
{
循环语句内容;
a++; // 这个a++就是此处代表能跳出循环的语句,如果没有这句就会一直死循环。
}
for(变量初始化;条件判断式;变量变化)
{
循环语句内容;
}
如
int a;
for( a = 0; a<3; a++)
{
循环语句内容;// a++也可以放到这里
}
最后讲下循环的嵌套,即循环套循环的执行过程:从A判断到B,再判断到C,先执行C再返回判断B,再返回判断A,即先执行最里面的循环再到外在的循环,这里可以看下我写的计时器程序,有用3个嵌套循环
while(A)
{
while(B)
{
while(C)
{
语句内容C;
}
}
}
/*--------------------------------------------------------------------------------------
用数码管来制作定时器
编写日期:2024年2月26日 版本:V01 开发者:dumon
--------------------------------------------------------------------------------------*/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义1个主时钟为24MHz
// 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
// 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
sbit ON_LED = P4^5; // 点灯总开关,三极管是小电流控制大电流的开关
sbit LED_1 = P2^0; // 流水灯LED1
sbit LED_2 = P2^1; // 流水灯LED2
sbit LED_3 = P2^2; // 流水灯LED3
sbit LED_4 = P2^3; // 流水灯LED4
sbit LED_5 = P2^4; // 流水灯LED5
sbit LED_6 = P2^5; // 流水灯LED6
sbit LED_7 = P2^6; // 流水灯LED7
sbit LED_8 = P2^7; // 流水灯LED8
// 数码管相关说明,P0.0~P0.7为数码管的8个段码,P1.0,P1.1,P1.3为3个位码,共阴极数码管
// 位码要接低电平、段码接高电平,共阳极数码管位码要接高电平、段码接低电平
sbit DIG_A = P0^0; // 数码管段码‘A’
sbit DIG_B = P0^0; // 数码管段码‘B’
sbit DIG_C = P0^0; // 数码管段码‘C’
sbit DIG_D = P0^0; // 数码管段码‘D’
sbit DIG_E = P0^0; // 数码管段码‘E’
sbit DIG_F = P0^0; // 数码管段码‘F’
sbit DIG_G = P0^0; // 数码管段码‘G’
sbit DIG_P = P0^0; // 数码管段码‘dP’
sbit SEG_1 = P1^0; // 第1位数码管位码
sbit SEG_2 = P1^1; // 第2位数码管位码
sbit SEG_3 = P1^3; // 第3位数码管位码
// 蜂鸣器参照STC32G实验箱V9.4增加,P5.4输出低电平接三极管基极,同流水灯控制原理,
// 在蜂鸣器负责两端并联反接1个二极管,防止短路
sbit BEEP = P5^4; // 蜂鸣器端口
// 数组变量
u8 LED_Blinker = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
u8 DIG_Number = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, /*不带小数点*/
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF, /*带小数点*/
0x00 /*全部熄灭*/};
/*
P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0 BIN HEX
不带小数 DP G F E D C B A
'0'0 0 1 1 1 1 1 1 0011 1111 0x3F
'1'0 0 0 0 0 1 1 0 0000 0110 0x06
'2'0 1 0 1 1 0 1 1 0101 1011 0x5B
'3'0 1 0 0 1 1 1 1 0100 1111 0x4F
'4'0 1 1 0 0 1 1 0 0110 0110 0x66
'5'0 1 1 0 1 1 0 1 0110 1101 0x6D
'6'0 1 1 1 1 1 0 1 0111 1101 0x7D
'7'0 0 0 0 0 1 1 1 0000 0111 0x07
'8'0 1 1 1 1 1 1 1 0111 1111 0x7F
'9'0 1 1 0 1 1 1 1 0110 1111 0x6F
带小数就把P0.7全部置1,再重新计算8位二进制数的HEX值
*/
u8 SEG_Number= {0xFE,0xFD,0xF7}; // 位码,1位,2位,3位单独显示
u16 a,b,c,d; // 循环计数变量
u32 time; // 定义1个计时变量,用来累加计时
u32 count_time = 3000; // 定义1个目标计时常量,3000表示30s
//u8 S1,S2,S3; // 3个段位数值存储变量
u8 S = {0,0,0 }; // 用1个数组来存储位码
void delay_ms(u16 ms) // 自定义延时函数
{
u16 i;
do{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
void main()
{
WTST = 1;
// IO口模式配置
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
while(1)
{
// while(a<3)
// {
// P1 = SEG_Number;
// while(b<10)
// {
// P0 = DIG_Number;
// delay_ms(1000);
// b++;
// }
// b = 0;
// a++;
// }
// a = 0;
// 正计时,从0一直加到设置的count_time值
// 循环嵌套,先从最内侧开始循环,逐层往外,共循环a*b*c= 99900次,因为是3位数码管,省略十位和个位,只显示万、千、百位;
// 这里用delay_ms(6),搭配3次循环刚好是time每加100,总用时1S。用手机计时器计算基本没差
for( a= 0; a<100; a++)
{
for( b= 0; b<333; b++)
{
for( c= 0; c<3; c++)
{
// 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
// 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
// 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
S = time%100000/10000; // 显示万位 76549=INT(MOD(C2,100000)/10000)算万位
S = time%10000/1000; // 显示千位
S = time%1000/100; // 显示百位
P1 = SEG_Number; // 选择位码
P0 = DIG_Number]; // 根据数字显示段码
delay_ms(6);
time++;
// 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
while( time == count_time) // 当time = count_time时,计时结束
{
for( c= 0; c<3; c++)
{
S = time%100000/10000; // 计算百位 76549=INT(MOD(C2,100000)/10000)算万位
S = time%10000/1000; // 计算十位
S = time%1000/100; // 计算个位
P1 = SEG_Number;
P0 = DIG_Number];
delay_ms(1);
BEEP = 0;
}
} // 内部while循环,用于保持计时结束后的循环执行语句
} // c的循环
} // b的循环
} // a的循环
// // 倒计时计数,从设置值一直倒数到0
// for( a= 0; a<100; a++)
// {
// for( b= 0; b<333; b++)
// {
// for( c= 0; c<3; c++)
// {
// // 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
// // 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
// // 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
// S = count_time%100000/10000; // 显示万位 76549=INT(MOD(C2,100000)/10000)算万位
// S = count_time%10000/1000; // 显示千位
// S = count_time%1000/100; // 显示百位
// P1 = SEG_Number; // 选择位码
// P0 = DIG_Number]; // 根据数字显示段码
// delay_ms(6);
// count_time--;
// // 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
// while( count_time == 0) // 当time = count_time时,计时结束
// {
// for( c= 0; c<3; c++)
// {
// S = count_time%100000/10000; // 计算百位 76549=INT(MOD(C2,100000)/10000)算万位
// S = count_time%10000/1000; // 计算十位
// S = count_time%1000/100; // 计算个位
// P1 = SEG_Number;
// P0 = DIG_Number];
// delay_ms(1);
// BEEP = 0;
// }
// } // 内部while循环,用于保持计时结束后的循环执行语句
// } // c的循环
// } // b的循环
// } // a的循环
}
}
必须立即送强大的实验箱支持啊 ! 感谢神农鼎大哥的支持!
现在还只是入门阶段,先把编程基础打好,还只是停留在运用基本”控制结构“,如if...else,while,for,switch...case等,这些是可以控制程序运转方向的。
还有就是C语言运算符,分为算术运算符,位运行符、关系运算符、布尔运算符,复合运算符。
算术运算符:=、+、-、*、/、%
位运算符:&、|、^、~、<<、>>
关系运算符:==、!=、<、>、<=、>=
布尔运算符:&&、||、!
复合运算符:++、--、+=、-=、*=、/=、&=、|=等
还有就是在编程时一定要根据实际的需求定义好数据类型,不要出现数据类型不符的情况。
例如:delay_ms(u8 ms)定义参数ms数据类型为unsigned char,如果在调用时写成delay_ms(30000);
这个30000是超出unsigned char数据类型范围,可能就会导致一些不可预料的报警出现。
编程是个严谨的过程,这个要特别注意。
前一个贴子附带的代码有处小错误,DIG_A~DIG_DP全部是P0^0,没有更改过来,大家注意一下。
附上更正后的代码:
/*--------------------------------------------------------------------------------------
用数码管来制作定时器
编写日期:2024年2月26日 版本:V01 开发者:dumon
--------------------------------------------------------------------------------------*/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义1个主时钟为24MHz
// 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
// 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
sbit ON_LED = P4^5; // 点灯总开关,三极管是小电流控制大电流的开关
sbit LED_1 = P2^0; // 流水灯LED1
sbit LED_2 = P2^1; // 流水灯LED2
sbit LED_3 = P2^2; // 流水灯LED3
sbit LED_4 = P2^3; // 流水灯LED4
sbit LED_5 = P2^4; // 流水灯LED5
sbit LED_6 = P2^5; // 流水灯LED6
sbit LED_7 = P2^6; // 流水灯LED7
sbit LED_8 = P2^7; // 流水灯LED8
// 数码管相关说明,P0.0~P0.7为数码管的8个段码,P1.0,P1.1,P1.3为3个位码,共阴极数码管
// 位码要接低电平、段码接高电平,共阳极数码管位码要接高电平、段码接低电平
sbit DIG_A = P0^0; // 数码管段码‘A’
sbit DIG_B = P0^1; // 数码管段码‘B’
sbit DIG_C = P0^2; // 数码管段码‘C’
sbit DIG_D = P0^3; // 数码管段码‘D’
sbit DIG_E = P0^4; // 数码管段码‘E’
sbit DIG_F = P0^5; // 数码管段码‘F’
sbit DIG_G = P0^6; // 数码管段码‘G’
sbit DIG_P = P0^7; // 数码管段码‘dP’
sbit SEG_1 = P1^0; // 第1位数码管位码
sbit SEG_2 = P1^1; // 第2位数码管位码
sbit SEG_3 = P1^3; // 第3位数码管位码
// 蜂鸣器参照STC32G实验箱V9.4增加,P5.4输出低电平接三极管基极,同流水灯控制原理,
// 在蜂鸣器负责两端并联反接1个二极管,防止短路
sbit BEEP = P5^4; // 蜂鸣器端口
// 数组变量
u8 LED_Blinker = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};
u8 DIG_Number = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, /*不带小数点*/
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF, /*带小数点*/
0x00 /*全部熄灭*/};
/*
P0.7 P0.6 P0.5 P0.4 P0.3 P0.2 P0.1 P0.0 BIN HEX
不带小数 DP G F E D C B A
'0'0 0 1 1 1 1 1 1 0011 1111 0x3F
'1'0 0 0 0 0 1 1 0 0000 0110 0x06
'2'0 1 0 1 1 0 1 1 0101 1011 0x5B
'3'0 1 0 0 1 1 1 1 0100 1111 0x4F
'4'0 1 1 0 0 1 1 0 0110 0110 0x66
'5'0 1 1 0 1 1 0 1 0110 1101 0x6D
'6'0 1 1 1 1 1 0 1 0111 1101 0x7D
'7'0 0 0 0 0 1 1 1 0000 0111 0x07
'8'0 1 1 1 1 1 1 1 0111 1111 0x7F
'9'0 1 1 0 1 1 1 1 0110 1111 0x6F
带小数就把P0.7全部置1,再重新计算8位二进制数的HEX值
*/
u8 SEG_Number= {0xFE,0xFD,0xF7}; // 位码,1位,2位,3位单独显示
u16 a,b,c,d; // 循环计数变量
u32 time; // 定义1个计时变量,用来累加计时
u32 count_time = 3000; // 定义1个目标计时常量,3000表示30s
//u8 S1,S2,S3; // 3个段位数值存储变量
u8 S = {0,0,0 }; // 用1个数组来存储位码
void delay_ms(u16 ms) // 自定义延时函数
{
u16 i;
do{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
void main()
{
WTST = 1;
// IO口模式配置
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
while(1)
{
// while(a<3)
// {
// P1 = SEG_Number;
// while(b<10)
// {
// P0 = DIG_Number;
// delay_ms(1000);
// b++;
// }
// b = 0;
// a++;
// }
// a = 0;
// 正计时,从0一直加到设置的count_time值
// 循环嵌套,先从最内侧开始循环,逐层往外,共循环a*b*c= 99900次,因为是3位数码管,省略十位和个位,只显示万、千、百位;
// 这里用delay_ms(6),搭配3次循环刚好是time每加100,总用时1S。用手机计时器计算基本没差
for( a= 0; a<100; a++)
{
for( b= 0; b<333; b++)
{
for( c= 0; c<3; c++)
{
// 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
// 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
// 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
S = time%100000/10000; // 显示万位 76549=INT(MOD(C2,100000)/10000)算万位
S = time%10000/1000; // 显示千位
S = time%1000/100; // 显示百位
P1 = SEG_Number; // 选择位码
P0 = DIG_Number]; // 根据数字显示段码
delay_ms(6);
time++;
// 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
while( time == count_time) // 当time = count_time时,计时结束
{
for( c= 0; c<3; c++)
{
S = time%100000/10000; // 计算百位 76549=INT(MOD(C2,100000)/10000)算万位
S = time%10000/1000; // 计算十位
S = time%1000/100; // 计算个位
P1 = SEG_Number;
P0 = DIG_Number];
delay_ms(1);
BEEP = 0;
}
} // 内部while循环,用于保持计时结束后的循环执行语句
} // c的循环
} // b的循环
} // a的循环
// // 倒计时计数,从设置值一直倒数到0
// for( a= 0; a<100; a++)
// {
// for( b= 0; b<333; b++)
// {
// for( c= 0; c<3; c++)
// {
// // 数码管的动态刷新,先选择位码,再选段码,延时2ms,位码逐步往前移,利用人眼视觉暂留达到同时显示3个数字目的,
// // 位码的切换必须总延时必须在20ms内,否则会看到明显的数字移动和停留。
// // 如这里循环3次,有3个位码,每个位码延时6ms,总延时=3*6=18ms,可以将延时改成10ms看看显示效果
// S = count_time%100000/10000; // 显示万位 76549=INT(MOD(C2,100000)/10000)算万位
// S = count_time%10000/1000; // 显示千位
// S = count_time%1000/100; // 显示百位
// P1 = SEG_Number; // 选择位码
// P0 = DIG_Number]; // 根据数字显示段码
// delay_ms(6);
// count_time--;
// // 用while保持状态让数码管一直显示最后的数值不变,并且蜂鸣器工作,代表计时结束
// while( count_time == 0) // 当time = count_time时,计时结束
// {
// for( c= 0; c<3; c++)
// {
// S = count_time%100000/10000; // 计算百位 76549=INT(MOD(C2,100000)/10000)算万位
// S = count_time%10000/1000; // 计算十位
// S = count_time%1000/100; // 计算个位
// P1 = SEG_Number;
// P0 = DIG_Number];
// delay_ms(1);
// BEEP = 0;
// }
// } // 内部while循环,用于保持计时结束后的循环执行语句
// } // c的循环
// } // b的循环
// } // a的循环
}
}