chun1234
发表于 2024-7-26 07:59:11
本帖最后由 chun1234 于 2024-7-26 08:06 编辑
学习冲哥的《STC32G单片机视频教程》 第九课 数码管的静态使用
一、认识数码管: 按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管,尾缀A表示共阳,K表示共阴。 二、控制原理: STC32G开发板使用的是共阳型四位数码管。若以一位的7段数码管为例,一位的数码管加上小数点一共是8个需要控制的发光管,分别是a、b、c、d、e、f、g、dp,由8个引脚分别控制他们的亮灭,也就是说一个引脚控制一个发光管,那么这就是段选。发光的二极管是有两端的,那么这8个发光的二极管有一个公共端,这样就可以控制一位数码管整体,这就是段选。
由一位数码管延伸到四位数码管时,如图所示,每一个发光管都有相应的引脚控制,每一位的数码管都有自己的公共端,通过公共端来控制哪一位的数码管亮或者是灭,这就是位选。
单片机的P6.0~P6.6引脚分别接到数码管的a~g引脚,就可以通过控制P6各引脚电平的高低来控制数码管七段LED的亮灭;单片机的P7.0~P7.7分别通过8支PNP型三极管,驱动8个数码管的公共端com0~com7,也就是位选端,来控制8位数码管的亮灭。
三、数码管实现0-9的显示 1.用数组定义0-9的内码 2.尝试用延时实现0-9的循环显示 3.用按键控制数字的加或者减。
#include "COMM/stc.h" //调用头文件#include "COMM/usb.h" #define KEY1 P32 //定义一个按键 引脚选择P32#define KEY2 P33 //定义一个按键 引脚选择P33 #define BEEP P54 //定义一个按键 引脚选择P54 #define MAIN_Fosc 24000000UL //定义主时钟 char *USER_DEVICEDESC = NULL;char *USER_PRODUCTDESC = NULL;char *USER_STCISPCMD = "@STCISP#"; u8 SEG_Tab = { 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90 }; //0-9 /*数组内16进制与2进制与实际显示数字对照:0xc01100 0000 ---> 00xf91111 1001 ---> 10xa41010 0100 ---> 20xb01011 0000 ---> 30x991001 1001 ---> 40x921001 0010 ---> 50x821000 0010 ---> 60xf81111 1000 ---> 70x801000 0000 ---> 80x901001 0000 ---> 9*/void sys_init(); //函数声明void delay_ms(u16 ms); //unsigned int void main() //程序开始运行的入口{ u8 num = 0; sys_init(); //USB功能+IO口初始化 usb_init(); //usb库初始化 EA = 1; //CPU开放中断,打开总中断。 while(1) //死循环 { if( DeviceState != DEVSTATE_CONFIGURED ) continue; if( bUsbOutReady ) { usb_OUT_done(); } P70 = 0; //开启数码管 //------P32按下一次,数码管显示数字加1;P33按下一次,数码管数字减1 ----- P6 = SEG_Tab; //这个数码管输出段码 if( KEY1 ==0 ) { delay_ms(10); if( KEY1 ==0 ) { BEEP = 0; delay_ms(10); BEEP = 1; while( KEY1 ==0 ); if( num<9 ) { num++; } } } if( KEY2 ==0 ) { delay_ms(10); if( KEY2 ==0 ) { BEEP = 0; delay_ms(10); BEEP = 1; while( KEY2 ==0 ); if( num>0 ) num--; } } }} void sys_init() //函数定义{ WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快 EAXFR = 1; //扩展寄存器(XFR)访问使能 CKCON = 0; //提高访问XRAM速度 P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口 P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口 P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口 P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口 P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口 P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口 P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口 P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口 P3M0 = 0x00; P3M1 = 0x00; P3M0 &= ~0x03; P3M1 |= 0x03; //设置USB使用的时钟源 IRC48MCR = 0x80; //使能内部48M高速IRC while (!(IRC48MCR & 0x01)); //等待时钟稳定 USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。 USBCON = 0x90;} void delay_ms(u16 ms) //unsigned int { u16 i; do { i = MAIN_Fosc/6000; while(--i); }while(--ms);}
chun1234
发表于 2024-7-28 08:30:04
本帖最后由 chun1234 于 2024-7-28 08:33 编辑
学习冲哥的《STC32G单片机视频教程》 第十课数码管的动态显示
一、数码管动态刷新的原理:
动态显示是将所有数码管的8个显示笔划a,b,c,d,e,f,g,dp 的同名端连在一起 ,另外为每个数码管的公共极COM端加位选通控制电路 ,位选通由各自独立的 I/O线控制 ,当单片机输出字形码时 ,所有数码管都接收到相同的字形码 , 但究竟是哪个数码管会显示出字形 ,取决于单片机对位选通COM端电路的控制 , 所以我们只要将需要显示的数码管的选通控制打开 ,该位就显示出字形 ,没有选通的数码管就不会亮。
透过分时轮流控制各个LED数码管的COM端 ,就使各个数码管轮流受控显示 , 这就是动态驱动。 在轮流显示过程中 ,每位数码管的点亮时间为1~2ms,由于人的视觉暂留现象及发光二极体的余辉效应 , 尽管实际上各位数码管并非同时点壳 , 但只要扫描的速度足够快 , 给人的印象就是一组稳定的显示资料 ,不会有闪烁感 ,动态显示的效果和静态显示是一样的 , 能够节省大量的I/0口 , 而且功耗更低。
二、控制原理:
使用动态扫描方式。由于任一时刻只能显示一种数字,当需要多位数码管显示多位数据的时候就需要动态扫描。动态扫描时间上执行的是动态显示,由于动态速度很快,人眼分辨不出,所以看上去是静态显示,这种效果正式我们所需要的。
用这个流程图就能很清晰的了解数码管动态扫描的原理。其中需要注意每个延时不能太短,我们这边程序就以1ms为准,且需要保证总共一个循环结束的时间不能大于20ms,因为人眼的视觉不容易分辨出50HZ以上的动态刷新。
三、8位数码管同时点亮 1.在上一课的基础上,新增一个位码选择的数组 2.通过调用数组选择位码 3.新建一个数组选择每个位需要显示的内容
张旭文
发表于 2024-7-28 21:14:43
一起学习共同进步。
chun1234
发表于 2024-7-30 09:36:04
本帖最后由 chun1234 于 2024-7-30 09:37 编辑
学习冲哥的《STC32G单片机视频教程》 第十一课 定时器的使用
一、学习这一章节 定时器的作用和意义 可以明了以下概念:
STC32G系列单片机内部设置了5个24位定时器/计数器(8位预分频+16位计数)。5个16位定时器T0、T1、T2、T3和T4都具有计数方式和定时方式两种工作方式。对定时器/计数器TO和T1,用它们在特殊功能寄存器TMOD中相对应的控制位CT来选择TO或T1为定时器还是计数器。对定时器/计数器T2,用特殊功能寄存器AUXR中的控制位T2_C/T来选择T2为定时器还是计数器。对定时器/计数器T3,用特殊功能寄存器T4T3M中的控制位T3_C/T来选择T3为定时器还是计数器。对定时器/计数器T4,用特殊功能寄存器T4T3M中的控制位T4_C/T来选择T4为定时器还是计数器。定时器/计数器的核心部件是一个加法计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每1个时钟得到一个计数脉冲,计数值加1;如果计数脉冲来自单片机外部引脚,则为计数方式,每来一个脉冲加1。 本节课主要用T0即timer 0也就是定时器0来实现功能。这个T就是一个定时的一个简称。 以定时器0/1模式寄存器(TMOD)为例: T0 C/T:控制定时器0用作定时器或计数器,清0则用作定时器(对内部系统时钟进行计数),置1用作 计数器(对引脚TO/P3.4外部脉冲进行计数)。
那么,我们为什么要使用定时器呢?回想一下之前写的程序,在实现延时这一功能时,我们使用了delay() 函数,这个函数并没有采用任何外设,只是写了两个循环嵌套,让cpu计数,当计数完成也就代表延时结束,简单点说就是让cpu通过不停的计数来消耗时间,所以这种方式有个很大的弊端,就是当cpu “死跑” 延时的时候,是做不了其他事情的,这个时候就需要一个额外的工具来帮助cpu完成计时,这就是定时器的作用。举个例子,比如你想在十分钟后做某件事,如果身边没有任何工具的话,你只能自己默数600秒,而你在数数的时候是肯定不能分心做其他事情的哈,而如果你身边有块表,那就可以定一个十分钟后的闹钟,让它帮你计时,在这十分钟期间你就可以放心地做其他事情了。
定时器还有一些其他作用,比如用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作。注意:定时器的大部分使用场景都要配合中断。
定时器是定时器和计数器的统称。
1)设置为定时器时,可实现硬件计时,或者使程序每隔一固定时间完成一项操作
2)设置为计数器时候能够对脉冲进行计数
3)替代长时间的delay,提高CPU的运行效率和处理速度,能及时的响应某个事件。
二、STC32G单片机定时器使用原理
T0实现1ms中断
2.1 先设置功能为定时器/计数器(本质都是加法计数器)
STC32G系列单片机内部设置了5个24位定时器/计数器(8位预分频+16位计数)。5个16位定时器T0、T1、T2、T3和T4都具有计数方式和定时方式两种工作方式。对定时器/计数器TO和T1,用它们在特殊功能寄存器TMOD中相对应的控制位CT来选择TO或T1为定时器还是计数器。对定时器/计数器T2,用特殊功能寄存器AUXR中的控制位T2_C/T来选择T2为定时器还是计数器。对定时器/计数器T3,用特殊功能寄存器T4T3M中的控制位T3_C/T来选择T3为定时器还是计数器。对定时器/计数器T4,用特殊功能寄存器T4T3M中的控制位T4_C/T来选择T4为定时器还是计数器。定时器/计数器的核心部件是一个加法计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:如果计数脉冲来自系统时钟,则为定时方式,此时定时器/计数器每12个时钟或者每1个时钟得到一个计数脉冲,计数值加1;如果计数脉冲来自单片机外部引脚,则为计数方式,每来一个脉冲加1。
本节课主要用T0即timer 0也就是定时器0来实现功能。这个T就是一个定时的一个简称。
以定时器0/1模式寄存器(TMOD)为例:
T0 C/T:控制定时器0用作定时器或计数器,清0则用作定时器(对内部系统时钟进行计数),置1用作
计数器(对引脚TO/P3.4外部脉冲进行计数)。
2.2、在定时器模式下,设置不分频或者12分频∶
当定时器/计数器TO、T1及T2工作在定时模式时,特殊功能寄存器AUXR中的TOx12、T1x12和T2x12分别决定是系统时钟/12还是系统时钟/1(不分频)后让TO、T1和T2进行计数。当定时器/计数器T3和T4工作在定时模式时,特殊功能寄存器T4T3M中的T3x12和T4x12分别决定是系统时钟/12还是系统时钟/1(不分频)后让T3和T4进行计数。当定时器/计数器工作在计数模式时,对外部脉冲计数不分频。
定时方式,此时定时器/计数器每12个时钟或者每1个时钟得到一个计数脉冲,计数值加1; 计数差了12倍。默认是除以12的。
2.3、定时器的工作模式
STC32G单片机的定时器0~定时器4工作模式以下表形式说明:
2.4、定时器设置
STC32G单片机有T0~T4共5个通用定时器。定时器核心器件是一个加法计数器,其本质是对脉冲计数。计数脉冲来自系统时钟,定时器工作在定时方式(因为系统时钟是一定的,因而计数器寄存器溢出的时间是可预期的)。定时器的计数脉冲来自外部引脚,则工作在计数器方式。定时器的工作模式可以通过对相应寄存器的C/T位置1或置0来进行设置,如:T0是对TMOD寄存器的B2(T0_CT)位进行设置,置0就工作在定时方式,置1就工作在计数器方式。T1是对TMOD寄存器的B6(T1_CT)位进行设置,置0就工作在定时方式,置1就工作在计数器方式。T2是对AUXR寄存器的B3(T2_CT)位进行设置,置0就工作在定时方式,置1就工作在计数器方式。T3是对T4T3M寄存器的B2(T3_CT)位进行设置,置0就工作在定时方式,置1就工作在计数器方式。T4是对T4T3M寄存器的B2(T3_CT)位进行设置,置0就工作在定时方式,置1就工作在计数器方式。
2.5、中断
EA:总中断允许控制位。EA的作用是使中断允许形成多级控制,即各中断源首先受EA控制,其次还受各中断源自己的中断允许控制位控制,
0:CPU屏蔽所有的中断申请
1:CPU开发中断
ELVD: 低压检测中断允许位。
0:禁止低压检测中断
1:允许低压检测中断
EADC:A/D转换中断允许位。
0:禁止A/D转换中断
1:允许A/D转换中断
ES: 串行口1中断允许位。
0:进制串行口1中断
1:允许串行口1中断
ET1: 定时/计数器T1的溢出中断允许位。
0:禁止T1中断
1:允许T1中断
EX1: 外部中断1 中断允许位。
0:禁止INT1中断
1:允许INT1中断
ET0:定时/计数器T0的溢出中断允许位。
0:禁止T1中断
1:允许T1中断
EX0: 外部中断0中断允许位。
0:禁止INT1中断
1:允许INT1中断
chun1234
发表于 2024-8-3 09:05:58
本帖最后由 chun1234 于 2024-8-3 09:25 编辑
学习冲哥的《STC32G单片机视频教程》 第十二课 计数器的使用
一、计数器的用途
只要输出信号带高低电平变化的,需要计算个数的就可以用计数器的功能。
二、计数器的配置
计数器0与计数器1有多种模式可以通过软件配置,而计数器2/3/4 只有16位自动重装载模式。计数器0与计数器1有多种模式为:
TMOD=0x04: 计数器0-模式0-16位自动重装载模式
TMOD=0x05: 计数器0-模式1-16位不可重装载模式
TMOD=0x06: 计数器0-模式2-8位自动重装载模式
TMOD=0X07: 计数器0-模式3-不可屏蔽中断16位重装载模式
TMOD=0x40: 计数器1-模式 0-16位自动重装载模式
TMOD=0x50: 计数器1-模式1-16位不可重装载模式
TMOD=ox60: 计数器0-模式2-8位自动重装载模式
根据冲哥的视频教程,用下面的代码来验证一下:
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#define KEY1 P32 //定义一个按键 引脚选择P32 #define KEY2 P33 //定义一个按键 引脚选择P33 #define BEEP P54 //定义一个按键 引脚选择P54 #define SEG_Delay1 //延时多少ms
#define MAIN_Fosc 24000000UL//定义主时钟
char *USER_DEVICEDESC = NULL; char *USER_PRODUCTDESC = NULL; char *USER_STCISPCMD = "@STCISP#";
u32 TimCount = 0; //计数单位1ms bit RUN_State = 0; //开始运行/结束运行 u8 num = 0;
void sys_init(); //函数声明 void delay_ms(u16 ms); //unsigned int
void main() //程序开始运行的入口 {
sys_init(); //USB功能+IO口初始化 usb_init(); //usb库初始化
TMOD = 0x40; //设置计数器模式 TL1 = 0xFF; //设置计数初始值 TH1 = 0xFF; //设置计数初始值 TF1 = 0; //清除TF1标志 TR1 = 1; //定时器1开始计时 ET1 = 1; //使能定时器1中断
P3PU = 0x20; //打开内部上拉4.1K
Timer0_Init(); P40 = 0; //led的电源控制三极管打开
EA = 1; //CPU开放中断,打开总中断。
while(1) //死循环 { if( DeviceState != DEVSTATE_CONFIGURED ) // continue; if( bUsbOutReady ) { usb_OUT_done();
} } }
void Timer0_Isr(void) interrupt 3 { P60 = !P60; //led取反 }
void sys_init () //函数定义 { WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快 EAXFR = 1; //扩展寄存器(XFR)访问使能 CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口 P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口 P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口 P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口 P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口 P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口 P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口 P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3M0 = 0x00; P3M1 = 0x00;
P3M0 &= ~0x03; P3M1 |= 0x03;
//设置USB使用的时钟源 IRC48MCR = 0x80; //使能内部48M高速IRC while (!(IRC48MCR & 0x01)); //等待时钟稳定
USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。 USBCON = 0x90; }
void delay_ms(u16 ms) //unsigned int { u16 i; do { i = MAIN_Fosc/6000; while(--i); }while(--ms); }
对以上代码的反复实验,了解到:通过用按键KEY1来模拟下降沿的脉冲,使定时器1接收到,定时器1开始计数。每按下一次按键,P60端口的LED即反转一次,由于按键没有加消除抖动的延时,存在一些误动作。
chun1234
发表于 2024-8-6 15:39:02
本帖最后由 chun1234 于 2024-8-6 17:14 编辑
学习冲哥的《STC32G单片机视频教程》 第十三课 简易多任务处理
一、回顾:通过前一段时间的学习,已经跟着冲哥的视频课程学习了12个章节 1、认识单片机 2、了解单片机硬件 3、开发环境搭建、新工程建立和资料下载 4、点亮一个LED 5、C语言运算符和进制入门 6、LED闪烁和花式点灯 7、按键点亮灯 8、蜂鸣器的使用 9、数码管的静态使用 10、数码管的动态点亮 11、定时器 12、计数器的使用
对于单片机内部功能的使用,用到了 GPIO 和TIM,同时了解到什么时候打开LED? LED打开多久?什么时候切换数码管显示?什么时候按键按下触发什么功能?之前的课主要是学习写程序的方法,分析逻辑,实现我们要的功能,重点是理清程序的逻辑思路。从这一节课开始,我们要规范所写的程序,也就是所写的代码要规范。
二、应用模块化的编程 (.c + .h) 1、LED& 数码管 对应 led_seg.c,led_seg.h 2、按键 对应 key.c, key.h 3、蜂鸣器 对应 beep.c, beep.h 4、定时器 对应 tim.c, tim.h
三、具体的操作步骤:
1、分三步创建程序文件 新建文件并保存 添加到工程 添加引用路径
2、引脚定义都在.h文件 sbit 名称 = P10; #define 名称 P10
3、函数定义三步 定义 声明 调用
修饰符extern用在变量或者函数的声明前,用来说明 “此变量/函数是在别处定义的,要在此处引用” 。 注意:extern修饰的变量不能赋初值。
四、工程文件编写
由原理图得知:8个LED的负极与数码管的8个段选控制端公用P6端口,所以给他们放在了一个文件(led_seg.c和led_seg.h)。 之前的程序是在定时器中通过一个函数刷新数码管,现在就要给他增加刷新LED的功能。在这里刷新显示,在别的地方赋值。
截图如下:
void SEG_Fre( void )
{
//位码选择第一位,段码选择0
P7 = COM_Tab; //位码的选择
P6 = SEG_Tab]; //需要显示的数字的内码 赋给 P6NUM =0 -> Show_Tab] = 1 -> p6 = oxF9
delay_ms(SEG_Delay);
num++;
if( num >7 )
num = 0;
}
chun1234
发表于 2024-8-16 10:04:49
本帖最后由 chun1234 于 2024-8-16 10:26 编辑
学习冲哥的《STC32G单片机视频教程》 第十四课 矩阵按键
一、矩阵按键是什么:采用行列矩阵的方式排列按键,再用扫描法识别按键。
二、识别原理:端口默认为高电平,实时读取到引脚为低电平时表示按下。
第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
第三步:行列组合一下就可以判断出是哪一个按键按下了。
三、矩阵按键用数码管显示按键号的代码如下:
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#include "seg_led.h"
#include "key.h"
#include "beep.h"
#include "tim0.h"
#define MAIN_Fosc 24000000UL //定义主时钟
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
bitTIM_10MS_Flag; //10ms的标志位
void sys_init(); //函数声明
void delay_ms(u16 ms); //unsigned int
void main() //程序开始运行的入口
{
u8 KEY_NUM = 0; //保存矩阵按键的键码
u8 KEY_Str = 0; //表示当前输入了几个密码位
sys_init(); //USB功能+IO口初始化
usb_init(); //usb库初始化
Timer0_Init();
EA = 1; //CPU开放中断,打开总中断。
// SEG0 = 0;
// SEG1 = 1;
// SEG2 = 2;
// SEG3 = 3;
// SEG4 = 4;
// SEG5 = 5;
// SEG6 = 6;
// SEG7 = 7;
LED = 0xff; //初始状态熄灭所有LED
while(1) //死循环
{
if( DeviceState != DEVSTATE_CONFIGURED ) //
continue;
if( bUsbOutReady )
{
usb_OUT_done();
}
if( TIM_10MS_Flag==1 ) //如果10ms到了
{
TIM_10MS_Flag = 0; //清空标志位
// KEY_Deal(); //按键处理
BEEP_RUN(); //蜂鸣运行
KEY_NUM = MateixKEY_Read(); //当前矩阵按键的键值
if( KEY_NUM>0 ) //如果有按键按下
{
BEEP_ON(2); //蜂鸣20ms
Show_Tab = KEY_NUM; //将当前的按键的键值保存到数码管显示变量里
KEY_Str ++; //s输入的密码位数+1
if( KEY_Str == 8 ) //如果密码已经输到了8位
{
KEY_Str = 0; //清空当前密码的位数
//for
if((Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)&&(Show_Tab==1)) //如果密码正确
{
LED0= 0; //点亮LED0
}
else
{
BEEP_ON(200); //密码错误,蜂鸣2秒
}
SEG0 = SEG1 = SEG2 = SEG3 = SEG4 = SEG5 =SEG6 = SEG7 = 21; //将所有的数码管显示位 -
}
KEY_NUM = 0; //清空按键键值
}
}
}
}
void Timer0_Isr(void) interrupt 1
{
static timcount = 0;
SEG_LED_Show(); //数码管刷新的
timcount++; //1ms+1
if( timcount>=10 ) //如果这个变量大于等于10,10ms计数到达
{
timcount = 0;
TIM_10MS_Flag = 1; //10ms时间到了
}
}
void sys_init() //函数定义
{
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
//设置USB使用的时钟源
IRC48MCR = 0x80; //使能内部48M高速IRC
while (!(IRC48MCR & 0x01)); //等待时钟稳定
USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。
USBCON = 0x90;
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
以上代码已经实验通过。体会到矩阵按键可以节省单片机的 IO 口,代码编写起来稍微费一些事,与独立按键相比,各有优缺点。
chun1234
发表于 2024-8-21 16:30:55
本帖最后由 chun1234 于 2024-8-21 16:36 编辑
学习冲哥的《STC32G单片机视频教程》 第十五课 外部中断
一、中断和中断系统 当中央处理机CPU正在处理某件事的时候外界发生了紧急事件请求,要求CPU暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。 能够实现上述功能的部件,称为中断系统。请示CPU中断的请求源称为中断源。微型机的中断系统一般允许有多个中断源。CPU总是先响应优先级别最高的中断请求。
二、什么是外部中断 外部中断的产生通常是由于外部设备或环境的变化触发的,这些变化可能是物理按键的按下、传感器读数的改变、或者其他外部信号的变化。当这些变化发生时,单片机通过中断系统接收到这些信号,并暂停当前程序的执行,跳转到相应的中断服务程序进行处理。处理完成后,单片机再返回到被中断的程序继续执行。
三、外部中断的用法 1、实时处理功能:在实时控制中,外部中断可以随时响应外界变量的变化,如参数、信息的实时变化,向CPU发出中断申请,请求及时处理。例如,当某个外部设备需要CPU立即响应时,可以通过外部中断请求CPU中断当前任务,转而处理外部设备的请求。 2、故障处理功能:对于难以预料的情况或故障,如掉电、存储出错、运算溢出等,外部中断可以由故障源向CPU发出中断请求,CPU随后转到相应的故障处理程序进行处理。这种机制对于保障系统的稳定性和可靠性至关重要。 3、分时操作:外部中断可以解决CPU与慢速外设之间的矛盾,使CPU和外设同时工作。CPU在启动外设工作后继续执行主程序,外设完成工作后通过发出中断申请请求CPU处理。这种方式大大提高了CPU的效率,允许同时处理多个任务。
四、中断系统包含哪些中断源 主要的中断源有 外部中断 (INT0) (INT1) (INT3) (INT4) 定时器中断(Timer0) (Timer1) (Timer2) (Timer3) 串口中断 (UART1) (UART2) (UART3) (UART4)
五、中断的优先级 需要查阅手册,确定中断的优先级
六、这一节课的代码练习:
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#include "seg_led.h"
#include "key.h"
#include "beep.h"
#include "tim0.h"
#include "exit.h"
#define MAIN_Fosc 24000000UL //定义主时钟
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
bitTIM_10MS_Flag; //10ms的标志位
void sys_init(); //函数声明
void delay_ms(u16 ms); //unsigned int
void main() //程序开始运行的入口
{
u8 i;
u8 KEY_NUM = 0; //保存矩阵按键的键码
u8 KEY_Str = 0; //表示当前输入了几个密码位
sys_init(); //USB功能+IO口初始化
usb_init(); //usb库初始化
Timer0_Init(); //定时器0初始化
INT0_Init(); //外部中断0初始化
EA = 1; //CPU开放中断,打开总中断。
SEG0 = 0;
SEG1 = 0;
// SEG2 = 2;
// SEG3 = 3;
// SEG4 = 4;
// SEG5 = 5;
// SEG6 = 6;
// SEG7 = 7;
LED = 0xff; //初始状态熄灭所有LED
while(1) //死循环
{
if( DeviceState != DEVSTATE_CONFIGURED ) //
continue;
if( bUsbOutReady )
{
usb_OUT_done();
}
for(i=0;i<8;i++) //循环八次
{
LED = ~(1<<i); //当前i是几,就点亮第几个LED
delay_ms(500); //延时500ms
}
if( P33 ==0 ) //如果P33按下了
SEG1 += 1; //数码管1的数值+1
}
}
void INT0_Isr(void) interrupt 0
{
SEG0 += 1; //数码管0的数值+1
}
void Timer0_Isr(void) interrupt 1
{
static timcount = 0;
SEG_LED_Show(); //数码管刷新的
timcount++; //1ms+1
if( timcount>=10 ) //如果这个变量大于等于10,10ms计数到达
{
timcount = 0;
TIM_10MS_Flag = 1; //10ms时间到了
}
}
void sys_init() //函数定义
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
//设置USB使用的时钟源
IRC48MCR = 0x80; //使能内部48M高速IRC
while (!(IRC48MCR & 0x01));//等待时钟稳定
USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。
USBCON = 0x90;
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
chun1234
发表于 2024-8-25 15:18:04
本帖最后由 chun1234 于 2024-8-25 15:35 编辑
学习冲哥的《STC32G单片机视频教程》 第十六课 IO中断
一、普通I/O口均可中断,不是传统外部中断STC32G系列支持所有的I/O口中断,且支持4种中断模式:下降沿中断、上升沿中断、低电平中断和高电平中断。每组I/O口都有独立的中断入口地址,且每个I/O口可独立设置中断模式。
二、I/O中断的用法
#include "COMM/stc.h" //调用头文件
#include "COMM/usb.h"
#include "seg_led.h"
#include "key.h"
#include "beep.h"
#include "tim0.h"
#include "exit.h"
#define MAIN_Fosc 24000000UL //定义主时钟
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
bitTIM_10MS_Flag; //10ms的标志位
u16 Tme_CountDown = 0; //全局变量
void sys_init(); //函数声明
void delay_ms(u16 ms); //unsigned int
void main() //程序开始运行的入口
{
u8 LOCK_State = 0xff; //门锁的工作状态
u8 KEY_NUM = 0; //保存矩阵按键的键码
u8 KEY_Str = 0; //表示当前输入了几个密码位
sys_init(); //USB功能+IO口初始化
usb_init(); //usb库初始化
Timer0_Init(); //定时器0初始化
INT0_Init(); //外部中断0初始化
P3Exit_Init();
EA = 1; //CPU开放中断,打开总中断。
SEG0 = 0;
LED = LOCK_State; //初始状态熄灭所有LED
while(1) //死循环
{
if( DeviceState != DEVSTATE_CONFIGURED ) //
continue;
if( bUsbOutReady )
{
usb_OUT_done();
}
if( TIM_10MS_Flag==1 ) //如果10ms到了
{
TIM_10MS_Flag = 0; //清空标志位
if( Tme_CountDown==0 ) //如果没有按下过应急按钮
{
KEY_NUM = MateixKEY_Read(); //当前矩阵按键的键值1-8
BEEP_RUN(); //蜂鸣运行
if( KEY_NUM>0 ) //如果有按键拿下
{
BEEP_ON(2);
LOCK_State ^=(1<<(KEY_NUM-1)); //获取当前是第几个按钮按下,{1-8}-》
}
LED = LOCK_State; //初始状态熄灭所有LED
SEG0 = 20; //熄灭数码管
}
else //按下了应急按钮
{
Tme_CountDown--;
SEG0 = (Tme_CountDown/100+1); //500/100 499
}
}
}
}
void INT0_Isr(void) interrupt 0
{
}
void P3Exit_Isr(void) interrupt 40
{
u8 intf;
intf = P3INTF; //读取中断标志
if( intf )
{
P3INTF = 0; //清空中断标志位,必须软件清空
if( intf & 0x20 ) //p35按下 0010 0000
{
LED = 0x00; //打开所有门锁
SEG0 = 5; //数码管持续显示5
Tme_CountDown = 500;//5秒倒计时的一个变脸
}
}
}
void Timer0_Isr(void) interrupt 1
{
static timcount = 0;
SEG_LED_Show(); //数码管刷新的
timcount++; //1ms+1
if( timcount>=10 ) //如果这个变量大于等于10,10ms计数到达
{
timcount = 0;
TIM_10MS_Flag = 1; //10ms时间到了
}
}
void sys_init() //函数定义
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
P3M0 = 0x00;
P3M1 = 0x00;
P3M0 &= ~0x03;
P3M1 |= 0x03;
//设置USB使用的时钟源
IRC48MCR = 0x80; //使能内部48M高速IRC
while (!(IRC48MCR & 0x01)); //等待时钟稳定
USBCLK = 0x00; //使用CDC功能需要使用这两行,HID功能禁用这两行。
USBCON = 0x90;
}
void delay_ms(u16 ms) //unsigned int
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
三、中断优先级的设置相同优先级,考前的中断源先执行,执行完之后再执行低中断源,且一个中断源在执行的时候不能被打断。例如:定时器0和P3中断都是最低优先级,定时器0中断号1;P3中断号40,执行完定时器0,再执行P3,之后再执行定时器0。
阿杰爱学单片机
发表于 2024-8-25 22:10:06
chun1234 发表于 2024-7-23 15:47
学习冲哥的《STC32G单片机视频教程》 第八课 蜂鸣器的应用
一、认识蜂鸣器:
看了你的帖子,我感觉我在假学习,实验会做 ,就是写不出你这种帖子{:4_171:}