32G12K128学习打卡记录 加油GOGOGO!
2024.9.12正式开始学习STC32的内容。
回想以前写程序的时候都是按部就班的直接用别人的库函数,没有了解单片机里面真正重要的东西,拿到学校的教材也是有点晦涩难懂,相信通过接下来的学习,我可以深入的参悟这些知识。
目标:了解冲哥视频中每一行代码的原理,深化对单片机的认识与应用。
开整!
第一
冲哥的视频解答了我很多的疑惑,也给了我一个学习STC32的动力{:loveliness:}。
第二集
红外发射二极管/红外接收管:发送和接受信号
电压比较电位器(滑动变阻器):与基准电压源比较,执行不同指令。
数码管:由许多可独立亮灭的发光二极管组成,显示不同的数字。
LED灯:发光{:lol:}
万能板:如果板子上缺少想要用的元器件可以自己焊上!(洞洞板和核心板结合的想法真是太棒了)
NTC测温:几个小贴片用于测温。检测连续的电压信号,ADC采集速度够快,采集的就越来越平缓。
18B20:温度传感器。采集精度越高速度越慢,如果温度变化过快,会采集到变化较大的离散点。
ADC按键:通过一个单片机引脚读取16个按键。(采集模拟电压来判断按下了那个按键,0-5V对应了连续的0-4095,但是要确定当前的基准电压是多少。
24C02:保存温度传感器和基准电压的一些数值,为了保护用户参数,防止单片机突然的损坏,将这些数据存储在外部存储器里面。
矩阵按键:减少引脚的使用。在密码锁保险箱应用较多。
独立按键:每个按键都有一个引脚对应。(炸弹!)
LCD接口:一个小显示屏,可以显示字符汉字等。
LED:在对应板子上确认是否亮起。
232接口:可连接电脑,但是有点老了。
FLASH扩展:也是存储芯片,但是比24C02存储空间更大,存储一些较大的文件。
测试接口:实现下载和调试的功能。
蜂鸣电路:发出声音。
PWM模拟DAC:数字信号转模拟信号(可以生成一个波形),哈哈今天刚刚学到模拟信号转数字信号,在这里简述一下就当做是复习了{:lol:}。
对于一个单位脉冲信号δ(t),其函数有定义:
1、在ε时间内激发矩形脉冲δ(t)(或三角脉冲,双边指数脉冲,钟形脉冲)所包含的面积为1.
2、当ε无限趋近于0时,δ(t)的极限就叫做单位脉冲函数
图1 图2
我们从极限的角度(图2)看这个函数,可以知道它的表达式为:
而从面积的角度(图1)来看,矩形的"宽"随着ε的减小而减小,其"高"随着ε的减小而迅速增大,不过在这过程中,它的面积总是不变的为1. 故也有以下的表达式:
根据上述描述,为什么可以知道δ(t)仅在 t=0 处,有其面积为1,所以我们引入一个信号X(t),当X(t)与δ(t)相乘时,由于δ(t)的特性,信号X(t)就只剩下了 t=0时的数值,也就是取到了X(t)信号的一个点。
那么怎么取到t为不同值时的信号X(t)呢? 欸☝🤗,只要给δ(t)不同的初相位就行了。这也是单位脉冲函数的乘积性质。
搜索了一下,单片机内部的数字信号处理是用ADC,经过采样量化编码等等步骤后可以得到数字信号,但是它的实现原理给我看的一愣一愣的,还得练呀。
第三集
1.STC-ISP软件的下载附说明书和软件。
2.跟着手册搭建C251开发环境
3.程序包的下载
http://www.stcmcudata.com/STC8F-DATASHEET/STC32G-DEMO-CODE.ZIP
4.第一个工程的编译
添加头文件以及程序测试
5.购买的屠龙刀还在路上,现在只能无实物学习了,期待一手{:loveliness:}
程序下载:将板子通过USB线链接电脑按下P3.2按钮,此时按下并松开OFF按钮,在松开P3.2进入USB下载模式
6.看了一眼跑马灯程序,发现是老师上课用的案例{:titter:},简单回顾一下。
三个头文件,包含了寄存器位置还有很多的定义。
typedef,给数据类型起一个新名字。
延时函数delay_ms 延时n毫秒
#define MAIN_Fosc24000000UL晶振频率
6000为经验值,使得i = MAIN_Fosc / 6000 = 4000 ,即这一个循环里面i就循环了4000次,大致为1ms的时间。
P6^0在STC32G.h中被定义为P60,而且P口是可位寻址的寄存器(地址为0或8的倍数),所以可以直接对一个位进行操作。
这一部分寄存器的内容完全不懂,希望后面可以有学到{:smile:}
本帖最后由 惦饭宝 于 2024-9-17 09:48 编辑
第四集:1.点亮第一颗LED灯😁
概念引入:输出电压=VCC就是高电平,输出电压=GND(一般是0V)就是低电平,分别用1和0来表示这个是理想值。
单片机引脚P口-I/O口:
GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来读取其输入电平,或者控制他输出高低电平。
P0是一组GPIO口,P0.0是一组中的一个GPIO口,因为P0是可位寻址的寄存器。
👇P4.0口输出低电平,使得Q11SS8550三极管导通,三极管导通后让P6口对应位输出低电平0,使发光二极管导通。
原理图下载:
新建工程
1.选择对应的芯片:
2.新建一个.c文件并放置到Source Group1中(直接点ADD New Item....也行)
3.键入代码
4.将Create HEX_File打勾,这样可以自动生成下载文件
5.由于没有头文件,所以查看STC32G手册手动写入P口的地址
定义端口👇
学习总结:
C语言代码的定义方法:
遇到括号代码需要缩进4格
注释要完整!
//单行注释
/*
多行注释
*/
第四集:2.实现自动下载工程😁
问题:每次下载程序之前都需要按下按钮,略显麻烦。
解决方法:见手册5.15用户程序复位到系统区进行 USB 模式 ISP 下载的方法。
Ps:要注意名称的大小写,会报未定义的错误。
stc_usb_hid_32.lib文件下载
另外还需要添加一个初始化函数和一个sys_init();在主函数main中
stc_usb_cdc_32.lib也可以实现直接下载程序的功能,但是要添加以下代码
PS:
并且在STC-ISP中勾选这个按钮:
成功后会弹出CDC的串口👇
课程总结
头文件
头文件中定义了许多SFR(特殊寄存器),也可以删除一些不需要的寄存器定义。
函数定义:
自定义的函数可以放在前面或者后面,但是因为编译器是从头开始往后编译,
所以当函数放到后面了之后,需要声明函数,在前面添加void xxxxx();
代码文件可以复制,直接修改之后不会影响原来的文件
了解了新工程的建立流程
I/O口高低电平对应不同的1/0电平
sbit可以定义某个寄存器
"#include"引入头文件
//写注释是一个良好的习惯{:titter:}
课后作业
个人认为点亮8个灯只要把P6的所有位定义为0,即0000 0000B,
在主函数中写入
while(1){
P6 = 0x00;
}
//成功点亮
本帖最后由 惦饭宝 于 2024-9-17 10:43 编辑
第五集:C语言运算符和进制数的入门😎
1.C语言Printf函数的实现
先添加主程序:
if(DeviceState != DEVSTATE_CONFIGURED)//等待USB完成配置 判断USB是否连接成功
continue;
if (bUsbOutReady) //判断是否接收到数据
{
printf("xxxxx");
usb_OUT_done(); //接受应答(固定模式)
}
Ps:注意大小写,不然容易报错
标志格式:
输出格式字符:
转义字符:
ASCLL码表:
各种进制之间的转换:
那么进制怎么计算才能转换成另一种进制。让我们先考虑十进制转换成 N 进制,以及 N 进制转换成十进制(N 可以为任意进制,比如 2、8 或者 16)。十进制值转换成 N 进制
[*]假设十进制值为 D1
[*]D1 对 N 取余,得到的余数作为 N 进制第一位(低位)的值
[*]D1 除以 N,将结果取整,作为下一轮计算的十进制值,记该值为 D2
[*]D2 对 N 取余,得到的余数作为 N 进制第二位的值
[*]D2 除以 N,将结果取整,作为下一轮计算的十进制值,记该值为 D3
[*]循环执行上面的逻辑,直到 Dx 除以 N 取整后的值为 0
例如,使用上面的公式将十进制值 19 转换成二进制,步骤是
[*]19 % 2 = 1,将 1 作为二进制值的最低位;19 / 2 = 9.5,取整数 9
[*]9 % 2 = 1,将 1 作为第二位;9 / 2 = 4.5,取整数 4
[*]4 % 2 = 0,将 0 作为第三位;4 / 2 = 2
[*]2 % 2 = 0,将 0 作为第四位;2 / 2 = 1
[*]1 % 2 = 1,将 1 作为第五位;1 / = 0.5,取整数 0,流程结束
[*]最终的二进制值为 10011
N 进制值转换成十进制
[*]假设二进制值为 B1
[*]从低位开始,将 B1 每一位的值,乘以 N 的(位数 - 1)次方
[*]将步骤 2 的每个乘积相加,即得到十进制值
例如,要将二进制值 1101 转换成十进制,步骤是
[*]1101 一共有 4 位,从低位开始,第一位是 1,第二位是 0,第三位是 1,第四位是 1
[*]第一位的计算结果是 1 * 2 ^ (1 - 1) = 1 * 2 ^ 0 = 1 * 1 = 1,其中 2 ^ 0 表示 2 的 0 次方
[*]第二位的计算结果是 0 * 2 ^ (2 - 1) = 0
[*]第三位的计算结果是 1 * 2 ^ (3 - 1) = 4
[*]第四位的计算结果是 1 * 2 ^ (4 - 1) = 8
[*]十进制的转换结果是 1 + 0 + 4 + 8 = 13
[*]其他进制的相互转换,可以用上面的方法,先将它转成十进制,再转成目标进制
大一学的C语言以一种奇怪的方式在我脑袋里面复活了🙃
第五集:2.C语言运算符和进制数的入门😊
C语言常用运算符
算术运算符:+ (加) , - (减) ,* (乘) ,/ (除) ,%(取余,模运算) ,++ (自增) ,–(自减)
关系运算符:>(大于) ,<(小于) ,==(等于) ,!=(不等于) ,>=(大于等于) ,<=(小于等于)
逻辑运算:&& (与) ,|| (或) ,! (非)
赋值运算符:
= (赋值)
+= ,-= ,*=, /= ,%= (算术复合赋值运算符)
&=, |=,^=,~,>>,<<(位运算复合赋值运算符)
位运算符:&,|,^,~,>>,<<
条件运算符:?:(条件运算符,三目运算符,三元运算符)
逗号运算符:,(逗号运算符)
指针运算符:&(取地址符) *(寻址符)
求字节运算符:sizeof(获取字节数)
特殊运算符:()(括号运算符,更改表达式运算顺序),[ ](数组下指针访问成员运算符),·(结构体变量访问成员运算符)、
C语言常用数据类型
本帖最后由 惦饭宝 于 2024-9-15 09:02 编辑
第六集:LED闪烁和花式点灯😋
1.基于Delay实现的LED闪烁
1s = 1000ms = 1000 000μs
先键入以下代码:
#define MAIN_Fosc 24000000UL
voiddelay_ms(u8 ms)
{
u16 i;
do{
i = MAIN_Fosc / 6000;
while(--i);
}while(--ms);
}
三个头文件,包含了寄存器位置还有很多的定义。
https://www.stcaimcu.com/data/attachment/forum/202409/13/130911saaaaw9w73717q1t.jpg
typedef,给数据类型起一个新名字。
https://www.stcaimcu.com/data/attachment/forum/202409/13/131008hqqqk8nh8arlqla6.jpg
延时函数delay_ms 延时n毫秒
#define MAIN_Fosc24000000UL 晶振频率//定义了一个主时钟
6000为经验值,使得i = MAIN_Fosc / 6000 = 4000 ,即这一个循环里面i就循环了4000次,大致为1ms的时间。
https://www.stcaimcu.com/data/attachment/forum/202409/13/131121xelnwnkwzcba52zf.jpg
延时函数的缺陷:在delay的过程中无法执行别的步骤
While(1){...}死循环,一直在这个循环中运行
do...while,无论是条件①是否成立,代码②一直会先执行一次
while,他会在代码执行前先判断①条件是否成立,如果成立才会执行②代码
2.函数的使用
在模块化编程里,函数使用分为如下三步:
1.函数定义
返回值·函数名称( 入口参数 )
{
函数要执行的功能
}
@返回值:没有返回值就是void
@函数名称:避开关键词,不重复,非特殊字符随便取
@入口参数:类型+名称,多个参数“,”分开,空就写void2.
2.函数声明
返回值 函数名称( 入口参数 );3.函数调用
3.函数名称(入口参数 );
3.模块化编程
新建xxx.c和xxx.h文件,代表一个功能块,相当于一个头文件{:lol:}
xxx.h格式:
#ifndef___XXX_H
#define___XXX_H
用头文件函数声明...
#endif
xxx.c格式:
#include"xxx.h"
函数定义
添加文件、定要记得引用路径和添加到工程里。
双击Source Group1添加.c文件
引用路径👇
4.课后练习:SOS灯光编写
本帖最后由 惦饭宝 于 2024-9-15 16:27 编辑
第七集:按键点灯😄
1.按键原理
常开按键,开关按下①/②闭合导通
2.机械开关缺陷
因为按键是机械开关所以当机械触点断开、闭合时,由于机械触点的弹性作用,
一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动(电压上下波动)。
对于一个开关来说,整个按键周期中有效的时间大约在50-200ms。
大约10ms之后就是正常的低电平。
3.按键的实现代码
//------------------------------------------------基础代码---------------------------------------------------------
if( KEY == 0)
{
Delay_ms(10);
if( KEY == 0)
{
执行功能;
}
}
//------------------------------------------------按下点亮,松开熄灭------------------------------------------------
if( KEY1 == 0) //判断按键有没有按下
{
delay_ms(10);
if( KEY1 == 0 )
{
printf("按键P32已经按下\r\n");
P60 = 0; //LED0点亮
}
}
else //如果没有按下
{
P60 = 1; //LED0点亮
}
//------------------------------------------------按下熄灭,松开点亮------------------------------------------------
if( KEY2 == 0) //判断按键有没有按下
{
delay_ms(10);
if( KEY2 == 0 )
{
printf("按键P33已经按下\r\n");
P67 = 1; //LED熄灭
}
}
else
{
P67 = 0; //LED熄灭
}
//------------------------------------------------按下一次,状态取反------------------------------------------------
if( KEY2 == 0) //判断按键有无按下
{
delay_ms(10);
if( KEY2 == 0 ) //按键按下
{
while(KEY2 == 0) //如果按键时一直按下的,那就执行while
{
}
printf("如果P33已经按下,led取反一次\r\n");
P67 = !P67; //LED熄灭
}
}
//------------------------------------------------按下一次,灯往左边移动一位-----------------------------------------------
if( KEY2 == 0) //判断按键有没有按下
{
delay_ms(10);// 消抖,等待10毫秒
if( KEY2 == 0 )// 再次判断按键是否仍然被按下,确保按键按下有效
{
printf("P33已经按下,led往左移动一位\r\n");
// 打印消息到串口,表示按键已被按下,LED将往左移动一位
LED_Data = ( (LED_Data<<1) +1 ); //先计算后输出P6
// LED_Data左移一位,使得LED显示往左移动
// 然后在最低位加1,点亮最右边的LED灯
if( LED_Data == 0XFF )// 检查LED_Data是否等于0xFF,即所有LED灯都被点亮
LED_Data = 0xFE; // 如果是,重置LED_Data为0xFE,只保留最高位点亮
P6 = LED_Data; // 将LED_Data的值输出到P6端口,更新LED的状态
while(KEY2 == 0); // 等待按键松开
// 如果按键一直按下,程序会一直停在这个循环中,防止多次触发按键事件
}
}
//------------------------------------------------按下一次,灯往右边移动一位-----------------------------------------------
if( KEY2 == 0) //判断按键有没有按下
{
delay_ms(10);// 消抖,等待10毫秒
if( KEY2 == 0 )// 再次判断按键是否仍然被按下,确保按键按下有效
{
printf("P33已经按下,led往右移动一位\r\n");
// 打印消息到串口,表示按键已被按下,LED将往左移动一位
LED_Data = ((LED_Data >> 1)|0x80); //先计算后输出P6
// LED_Data右移一位,使得LED显示往右移动
// 然后在最高位加1,点亮最左边的LED灯
if( LED_Data == 0XFF )// 检查LED_Data是否等于0xFF,即所有LED灯都被点亮
LED_Data = 0x7F; // 如果是,重置LED_Data为0x7F,只保留最高位点亮
P6 = LED_Data; // 将LED_Data的值输出到P6端口,更新LED的状态
while(KEY2 == 0); // 等待按键松开
// 如果按键一直按下,程序会一直停在这个循环中,防止多次触发按键事件
}
}
//-----------------------------------------------------------------------------------------------------------------------------
4.数组的使用
数组使用分为如下两步
1.定义
类型:名称[长度]={数值};
2.使用
赋值:名称[索引]=数值;
本帖最后由 惦饭宝 于 2024-9-16 17:19 编辑
第八集:蜂鸣器的应用😀
1.认识蜂鸣器
1)有源蜂鸣器内部带震荡源,所以只要一通电就会叫而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。
2)价格不同,有源蜂鸣器要比无源蜂鸣器贵,贵在里面多子震荡源
3)播放音乐上,无源蜂鸣器效果更好
2.控制原理
P54作为开关,低电平导通T2-SS8550三极管,高电平断开。
3.蜂鸣器实战应用
课堂内容:1、按键1按下,蜂鸣10msLED1-8全部点亮200ms在熄灭,表示开机。
if( Run_Flag==0 ) //还没有开机
{
Run_Flag = 1; //开机变量改为1,表示已经开机了
BEEP = 0; //打开蜂鸣
delay_ms(10); //延时10ms
BEEP = 1; //关闭蜂鸣
P40 = 0; //打开led的总电源
P6 = 0X00; //点亮全部LED
delay_ms(200); //延时200ms
P6 = 0XFF; //熄灭全部LED
}
2、开机后,按键2按下,蜂鸣10ms,LED1-8轮流点亮,表示切换煲汤、烧水等功能。
if( KEY2 == 0 )
{
delay_ms( 10 );
if( KEY2 == 0 )
{
while( KEY2 == 0); //等待松开执行了
BEEP = 0; //打开蜂鸣
delay_ms(10); //延时10ms
BEEP = 1; //关闭蜂鸣
Run_Mode ++; //模式选择
if( Run_Mode>8 ) //如果模式>8
Run_Mode = 1; //回到模式1
//P6 = 0XFE; P6<<1+1
P6 = ~(1<< (Run_Mode-1)); //1<<10000 0001 -> 1111 1110
}
}
3、开机后按键1再次按下,蜂鸣10ms,LED全部熄灭:表示关机。
else //已经开机
{
Run_Flag = 0;
BEEP = 0; //打开蜂鸣
delay_ms(10); //延时10ms
BEEP = 1; //关闭蜂鸣
P6 = 0XFF; //熄灭全部LED
Run_Mode = 0; //模式清0
}
4.学习心得
今天网好差 视频一卡一卡的😤,不过也是学会了蜂鸣器、按键、LED的搭配使用方法,头文件的嵌套有点懵,其他还好。
虽然说只需要控制一个引脚就可以让蜂鸣器工作,但是视频里面没有说怎么让蜂鸣器发出不同频率的声音,好奇捏。
文件:
本帖最后由 惦饭宝 于 2024-9-17 20:32 编辑
第九集:静态数码管☝😎
1.认识数码管
数码管也叫LED数码管,内部是由多个发光二极管封装在一起组成,他们可以有很多种颜色,很多种外形,很多种样式,但是本质来说他们都是通过点亮内部的LED来显示的,只要面板做好了,理论可以显示任意的字符或者图案。
按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管,尾缀A表示共阳,K表示共阴
2.控制原理
COM0-COM7为从右到左的数码管编号,其通过控制P7来打开,P6口控制显示的是什么字符。
3,数码管应用
使用数组保存0-9的16进制表示:
SEG_Tab = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90 };//0-9
PS:使用的是上图共阴的管子,共阳需要取反。☝😎
尝试用延时实现0-9的循环显示。
思路:使用简单的延时函数来控制数字的循环显示,每隔一段时间更新一次数字,从0到9,然后重复循环。
示例:
P6 = SEG_Tab; //这个数码管输出段码
num ++;
if( num>9 )
num = 0;
delay_ms(1000);
用按键控制数字的加或者减。
思路:设置两个按键,一个用于数字的增加,一个用于数字的减少。
按下增加按键,数字增加1;按下减少按键,数字减少1。
数字范围限制在0到9,超出范围时进行循环,例如:从9增加到0,从0减少到9。
示例:
P6 = SEG_Tab; //这个数码管输出段码
if( KEY1 ==0 ) // 检测按键KEY1是否被按下
{
delay_ms(10); // 消抖处理,等待10毫秒
if( KEY1 ==0 ) // 再次确认按键KEY1是否仍然被按下
{
BEEP = 0; // 打开蜂鸣器,发出短暂的声音提示
delay_ms(10); // 蜂鸣器持续时间,延时10毫秒
BEEP = 1; // 关闭蜂鸣器
while( KEY1 ==0 ); // 等待按键释放,防止多次触发按键事件
if( num<9 ) // 如果当前显示的数字小于9
num++; // 数字增加1
}
}
if( KEY2 ==0 )
{
delay_ms(10);
if( KEY2 ==0 )
{
BEEP = 0;
delay_ms(10);
BEEP = 1;
while( KEY2 ==0 );
if( num>0 ) // 如果当前显示的数字大于0
num--; // 数字减少1
}
}
4.课后总结
学会了数码管的原理,如何控制管脚了显示对应的字符,以及在那个数码管显示。😎
通过数组的方式将0-9的数值用16进制储存,以后使用快捷方便。
初步学会了数码管的一些应用以及按键的一些搭配形式。
5.课后练习
1)尝试使用数码管显示HJLNOPUtr等字母或符号。
文件: Ps:有些字母和数字串用了,比如0和O。😥我手上现在没有实验箱,无法做验证,可能有一些错误,欢迎批评指正。
2)通过一个按键设置数码管显示数字0-9循环,在按一下另一个按键的时候,数码管上显示的数字几,蜂鸣器就响几声。
本帖最后由 惦饭宝 于 2024-9-17 20:22 编辑
第十集:动态数码管👦
1.数码管动态刷新的原理
数码管动态刷新是一种通过快速切换数码管的显示,以实现多个数码管同时显示不同数字的方法。
它利用人眼的视觉暂留现象,使人感觉到数码管上的数字是同时显示的。
动态刷新主要是通过分时显示的方法,将多个数码管依次点亮。每次只点亮一个数码管,而其他的数码管则保持熄灭状态。
由于每个数码管只在短时间内被点亮,因此需要快速切换,这样人眼在视觉上就会感受到所有数码管是同时亮的。
动态刷新需要多个控制信号来选择哪一个数码管被点亮,以及选择数码管上的哪个段位被点亮。通常需要使用多个I/O口或者串行移位寄存器来实现这些控制信号的输出。
刷新频率要足够快,通常在几十到几百赫兹之间,这样才能避免人眼察觉到闪烁。
2.控制原理
具体的控制的流程如图所示,N表示有几个数码管!
其中需要注意每个延时不能太短,我们这边程序就以1ms为准,且需要保证总共一个循环结束的时间不能大于20ms,因为人眼的视觉不容易分辨出50HZ以上的动态刷新。
3.实验任务
1)在上一课的基础上,新增一个位码选择的数组
u8 COM_Tab = { 0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe }; //0-7的位码数组
2)通过调用数组选择位码
可编辑文件:
3)新建一个数组选择每个位需要显示的内容!
u8 Show_Tab = {3,0,0,0,0,10,0,0}; //让对应数码管显示输入的数字
P6 = SEG_Tab]; //需要显示的数字的内码 赋给 P6 NUM =0 -> Show_Tab] = 1 -> p6 = 0xF9
4.实战小练:
简易10秒免单计数器
👇提前定义数组,以及判断标志
u32 TimCount = 0; //计数单位1ms
bit RUN_State = 0; //开始运行/结束运行
u8 SEG_Tab = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//0-9段码,0-9带小数点
Show_Tab= 1; //选择 1
Show_Tab= 10; //选择 0.
Show_Tab= 0; //选择 0
Show_Tab= 0; //选择 0
👆前四位显示1 0. 0 0
1.在前四位数码管上显示目标时间,即10.00”表示定时时间10秒钟
注意点:带有小数点,需要修改对应数组
2.后四位显示当前的计时00.00,最小单位为10ms
3.按下开始按钮后,每10ms最末尾的数字+1;知道按下结束按钮后停止计数。
if( RUN_State==1 ) // 如果运行状态为1(RUN_State == 1)
{ // 计时器计数加1
TimCount++;
Show_Tab = TimCount/10000%10; // 获取万位数字并显示
Show_Tab = TimCount/1000%10+10; // 获取千位数字并显示(+10用于特殊显示,小数点)
Show_Tab = TimCount/100%10; // 获取百位数字并显示
Show_Tab = TimCount/10%10; // 取10位
}
SEG_Fre(); // 更新数码管显示
if( KEY1 ==0 )
{
delay_ms(10);
if( KEY1 ==0 )
{
BEEP = 0;
delay_ms(10);
BEEP = 1;
while( KEY1 ==0 )
SEG_Fre(); // 不断刷新数码管显示
if( RUN_State==0 ) // 如果运行状态为0
TimCount = 0; // 重置计数器
RUN_State = !RUN_State; // 切换运行状态
}
}
5.课后练习
一、做一个简易时钟,功能如下1.初始状态显示 00-00-00,分别作为时,分,秒
二.每隔一秒钟,秒+1,一分钟,分+1,以此类推
三.时间到达00-00-30的时候,蜂鸣响3秒钟表示闹钟
添加部分:
数组数量加1,第21个为横杠。
u8 SEG_Tab = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xBF}; //0-9段码,0-9带小数点,横杠-
设置Show_Tab第3位和第五位为横杠。
u8 Show_Tab = {0,0,20,0,0,20,0,0}; //Show_Tab对应SEG_Tab的第21个
新增时,分,秒计数单位。
u16 H = 0,M = 0,S = 0;
代码:
//----------------------------------------------------------------------------------------------------------------------------------------------------//
delay_ms(1000);
S++;
// 判断闹钟
if(RUN_State == 0 && S == 30) //第一次S=30时,蜂鸣器响3秒。
{
BEEP = 0;
delay_ms(3000);
BEEP = 1;
RUN_State = 1;
}
//当S == 60时,M+1,当M == 60时,H+1
if(S >= 60)
{
S = 0;
M++;
if(M >= 60)
{
M = 0;
H++;
if(H>24)
{
H = 0;
}
}
}
//0,1,3,4,6,7为数码管需要显示部分;
Show_Tab = S /10 ; //显示秒的十位
Show_Tab = S %10 ; //显示秒的各位
Show_Tab = M /10 ; //显示分的十位
Show_Tab = M %10 ; //显示分的各位
Show_Tab = H /10 ; //显示时的十位
Show_Tab = H %10 ; //显示时的各位
SEG_Fre();
//----------------------------------------------------------------------------------------------------------------------------------------------------//
PS:没有试验箱,代码还未验证,若有错误还望批评指正!
6.课后总结😁
学习了如何在数码管上显示小数点,并通过按键控制计时的开始和停止。按键消抖以及蜂鸣器的使用也是更加熟练。
动态数码管理解:数码管动态刷新是通过快速切换数码管的显示来实现的。每次只点亮一个数码管,通过不断轮流点亮各个数码管,使人眼感受到所有数码管同时显示。
实现过程中,刷新频率必须足够快,通常在几十到几百赫兹之间,以避免视觉上感到闪烁。
学到了如何控制数码管的显示,包括使用位码选择数组和段码选择数组。在实现过程中,重点是通过循环不断切换显示内容,使数码管能够正确显示多个数字。
ALL👆
学习文件:
本帖最后由 惦饭宝 于 2024-9-19 11:41 编辑
第十一集:定时器的使用👌
1.定时器的作用和意义
定时器的工作原理基于定时器芯片上的计数器。当开始计时时,计数器开始递增计数。定时器通过使用时钟信号来触发计数器的递增,随着计数器不断递增,一旦计数器的值达到了预设的时间间隔,定时器将会触发预定的行为,如打开或关闭一个电路
定时器是定时器和计数器的统称。
1)设置为定时器时,可实现硬件计时,或者使程序每隔一固定时间完成一项操作。
2)设置为计数器时候能够对脉冲进行计数
3)替代长时间的delay,提高CPU的运行效率和处理速度,能及时的响应某个事件
2.STC32G单片机定时器使用原理
1)先设置功能为定时器/计数器
定时器/计数器的核心部件是一个加法计数器,本质是对脉冲进行计数。
2)如果在定时器模式下,设置不分频或者12分频。如果定时时间够,那么12分频就可以
PS:默认是12分频。
3)定时器的工作模式
如果设置为16位自动重载,那么它的计数就可以计到0-65535(2^16)。8位自动重载就是0-255(2^8)
自动重载就是每次时间到了就把计数的值自动写进去,不自动重载的话就要手动将定时时间写一遍。
不可屏蔽重载:该中断一开启就是最高优先级,不可被任何中断打断。
4)定时器设置
一旦产生中断,TF0就会硬件置1,运行前TF0可以软件清0。
TR0必须在启动前手动写1,不然中断无法开启!
5)中断
3.定时器的简单应用
系统时钟可以理解为IRC👇
定时时间的计算公式:
手册设置定时器示例:
中断号:需要看手册,比如定时器0就要用interrup 1。
void Timer0_Isr(void) interrupt 1
定时器0配置
void Timer0_Init(void) // 1毫秒@24.000MHz
{
AUXR &= 0x7F; // 定时器时钟12T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0x30; // 设置定时初始值
TH0 = 0xF8; // 设置定时初始值 :TL0,TH0设定定时器多少秒溢出一次
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
}
定时器0中断服务函数
void Timer0_Isr(void) interrupt 1
{
SEG_Fre(); // 数码管刷新的
if( RUN_State == 1 ) // 如果开始运行
{
TimCount++; // 每隔1ms+1
Show_Tab = TimCount/10000%10;
Show_Tab = TimCount/1000%10+10;
Show_Tab = TimCount/100%10;
Show_Tab = TimCount/10%10;
}
}
4.学习总结:
定时器中断与主循环 (while(1)) 独立运行。定时器按固定的时间间隔触发中断,主循环持续执行代码,彼此之间不阻塞。
两者通过全局变量(例如 RUN_State、TimCount)进行交互。主循环负责检测按键,改变 RUN_State;定时器中断根据 RUN_State 的状态来更新 TimCount。
EXCLE计算频率时间:
5.课后练习:
一、第十课的课后作业做一个简易时钟,在此基础上将时钟改成定时器驱动。
二、在上述基础上是增加一个按钮,按下一次就可以让时间暂停,在按一下时间又能继续走,在按一下再暂停!
添加定时器初始化
添加定时器0服务函数
将时钟时分秒的改变放到定时器中运行,在while循环中改变对应参数的数值,从而判断定时器中的if条件。
文件:
ALL👆
向大佬学习!