dumon
发表于 2024-2-28 10:22:32
电路中常见的上拉电阻与下拉电阻是什么意思?今天就来讲一下。
上拉的意思就是IO口通过串联1个电阻(一般为5K或10K)与VCC相连接,IO口为高电平状态,上就是电压往上的意思。
下拉的意思就是IO口通过串联1个电阻(一般为5K或10K)与GND相连接,IO口为低电平状态,下就是电压往下的意思。
大家想想看,为什么要串联1个电阻,在后面的图片会有说明。
当然冲哥视频讲了,高低电平只是1个电压范围,并不是具体到某一数值,如:0~1.8V范围内我们都可以认为是低电平,4.5~5.5V范围内我们可以认为是高电平。
这里也提下醒,不同的电子设备也许都支持串口通讯,但是他们的VCC也可能不一样,比如5V的MCU和3.3V的MCU都支持串口通讯,但是他们的RX,DX电平可能
不一样,5V的MCU我们需要将其RX,DX降压到3.3V后才能与3.3V的MCU进行信息通讯。这里特别提醒下就是这个意思。
附个图吧,看过这个图后就知道电路中说的上拉和下拉是什么意思了,以后在电子设计中假如需要保持某个IO上电后上拉或下拉就能知道了,这也是一个非常重要的知识点。
dumon
发表于 2024-3-7 18:24:20
第7集 按键点灯
本章是主要介绍按键的一些常用功能,按键我们按功能分为自复位和自锁型,按按键状态我们又分为常开型和常闭型。按键在电路中主要分为0或1,即表示断开或接通。
自复位——不能保持按键状态,必须一直按住才能保持状态,松手后按键回复为初始状态。
自锁型——可以保持按键状态,只需按下一次后松手可以维持状态,再次按下后又回复到初始状态。
常开型——未按下时为断开状态,按下后为接通。
常闭型——未按下时为接通状态,按下后为断开。
正常我们的按键都是机械按钮,可以理解为是一个金属弹片,就像冲哥视频里说的,机械按钮在接触的一瞬间ms级别,电平会发生上下变化,这个时候检测电平是不准确的,
我们必须等弹片完全接触好后再去读取引脚电平,这个延时过程我们称之为消抖。
像我平时工作时有接触到PLC,PLC里面有2个指令,是脉冲上升沿触发和脉冲下降沿触发,我觉得跟本集按键点灯里有很多功能类似。
脉冲上升沿是指信号从0变为1的瞬间去执行指令,而脉冲下降沿触发是指信号从1变为0的瞬间去执行指令,必须有这个0变为1或1变为0的切换过程。
冲哥是使用STC32G实验箱来做实验的,正常P3.2和P3.4.P3.5都是上拉到5V,即正常情况下为1,按键按下为变为0.
这里要注意一下,在按键消抖后,有动作执行语句和while(),此两者的顺序不一样,得到的结果也不一样。比如:
1.将while()放到动作执行语句之前,那么在你没有松开按键前,是不是整个语句都被while()按在这里了,只有当你松开按键是不是才能执行动作语句。
那这个按键未按下前为1,按下后为0,再次松开为1,是不是我们就是卡的这个0转为1的上升沿才执行动作语句。
2.那假如把while()放到动作执行语句之后,是不是我们只要满足按键消抖后就会立刻执行完动作语句,但如果你一直不松按键,是不是就会被后面的while()按在这里了。
那这个按键未按下前为1,按下后为0,是不是我们就是卡的这个1转为0的上升沿才执行动作语句。
至于为什么加while(),是因为我们只想让这个动作执行一次,避免多次执行。
while的顺序需要你根据自己的实际需要来放置位置,比如你需要按钮按下立刻执行,就把动作语句放到while()之前,
如果你想按钮按下待松开后才执行,就把动作语句放于while()之后,这样只有松开按钮后才能跳出循环执行动作语句。我称之为滞后性。
至于按键循环按动,每次按下都切换状态,这也是很简单的,就是对P口取反。或者用1个状态变量,每次按下都把状态变量去取反。这样是不是就可以实现1个按钮来控制
电机启停了。这在PLC也是一个经典的案例,单按钮启停。从这里也能找到对照哦。
这个代码我是有照着冲哥视频做了一遍,自己根据不同的方法又做了一遍,通过交叉对比加深自己的认识。
88
/***********************************************************************
第7集 按键点灯(实操)
1.P3.3按键按下P2.0上的LED点亮,松开熄灭
2.P3.4按键按下P2.7上的LED熄灭,松开点亮
3.P3.5按键按下一次,P2.1上的LED状态改变一次
4.P3.3按键按下一次,LED往左边走一个,P3.5按键按下一次,LED往右边走一个,
***********************************************************************/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义一个主时钟24MHz
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init();
void delay_ms(u16 ms);
sbit button_P33 = P3^3 ; // 按键P3.3,上拉到5V
sbit button_P34 = P3^4 ; // 按键P3.4,上拉到5V
sbit button_P35 = P3^5 ; // 按键P3.4,上拉到5V
sbit LEDON = P4^5 ; // P4^5 = 0, P2.0~P2.7上的LED才能点亮
sbit LED20 = P2^0 ; // P2.0上的LED
sbit LED21 = P2^1 ; // P2.1上的LED
sbit LED27 = P2^7 ; // P2.7上的LED
bit buttonState ; // 布尔变量存储按键状态
void main()
{
sys_init();//系统初始化
usb_init();//USB CDC 接口配置
EA = 1;
LEDON = 0 ;
P2 = 0xFE ;
while(1)
{
// 1.P3.3按键按下P2.0上的LED点亮,松开熄灭
if(button_P33 == 0)
{
delay_ms(10);
while(button_P33 == 0)
{
LED20 = 0 ;
}
LED20 = 1;
}
// 2.P3.4按键按下P2.7上的LED熄灭,松开点亮
if(button_P34 == 0)
{
delay_ms(10);
while(button_P34 == 0)
{
LED27 = 1 ;
}
LED27 = 0;
}
// 3.1 P3.5按键按下一次,P2.1上的LED状态改变一次
if(button_P35 == 0)
{
delay_ms(10);
if(button_P35 == 0)
{
while(button_P35 == 0)
{
LED21 = buttonState ;
}
buttonState = !buttonState ;
}
}
// 3.2 P3.5按键按下一次,P2.1上的LED状态改变一次,按下后立刻执行
// if(button_P35 == 0)
// {
// delay_ms(10);
// if(button_P35 == 0)
// {
// LED21 = !LED21 ;
// while(button_P35 == 0);
// }
// }
// 3.3 P3.5按键按下一次,P2.1上的LED状态改变一次,教材中冲哥的方法,松开按键后执行,滞后性
// if(button_P35 == 0)
// {
// delay_ms(10);
// if(button_P35 == 0)
// {
// while(button_P35 == 0);
// LED21 = !LED21 ;
// }
// }
// 4.P3.3按键按下一次,LED往左边走一个,P3.5按键按下一次,LED往右边走一个,
// if(button_P33 == 0)
// {
// delay_ms(10);
// if(button_P33 == 0)
// {
// P2 = (P2<<1)+1; // LED往左移动1个
// while(button_P33 == 0); // 检测到按键未释放时,停留在此步
// if(P2 == 0xFF) // 当最左边LED点亮时,切换到最右边LED
// {
// P2 = 0xFE;
// }
// }
// }
// if(button_P35 == 0)
// {
// delay_ms(10);
// if(button_P35 == 0)
// {
// P2 = (P2>>1)|0x80; // LED往右移动1个
// while(button_P35 == 0); // 检测到按键未释放时,停留在此步
// if(P2 == 0xFF) // 当最右边LED点亮时,切换到最左边LED
// {
// P2 = 0x7F;
// }
// }
// }
}
}
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
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; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
}
dumon
发表于 2024-3-9 20:03:38
第8集 蜂鸣器的应用
本集是在前面按键点灯的基础上,对按键应用的一次拓展,这里牵涉到多个按键一起工作,如何处理好这些按键的功能,按键的先后对程序有无影响,了解动作流程,并针对程序运行实际效果对程序进行修改是本集重点。
先来看下程序要求:
1.按键1按下,蜂鸣10ms,LED1~8全部点亮200ms再熄灭,表示开机。
2.开机后,按键2按下,蜂鸣10ms,LED1-8轮流点亮,表示切换煲汤、烧水等功能。
3.开机后按键1再次按下,蜂鸣10ms,LED全部熄灭,表示关机。
开发板:STC32G自制开发板
IO分配:
LED1~8:P2口,控制P2口点灯的是P4.5引脚,即P4.5为0,P2口输出0才能点亮LED,
按键1:P33,按键2:P34
蜂鸣器:P5.4
按键优先级:按键1高于按键2,因为按键1必须先按下工作后,按键2才能工作,同时按键1只要按下,所有LED全部熄灭,代表按键2的功能也停止,这里一定要理解。
同时我们在程序编写时要考虑一些特别情况,比如说按键1我按下一直不松,会不会一直循环开机关机之类。
这里我用的是跟冲哥不一样的思路,我在学习的时候我都会先不看冲哥的编程,而是自己先思考一遍,自己根据前面所学我是否能独立的把这个程序编辑出来,如果想不通再去参考下冲哥和程序思路,这样可以做到举一反三,拓展自己的处理问题的方式。
下面请看我自己所编写的程序,经过实测可以达到程序要求的功能。具体介绍都在程序注释内,希望能给朋友们一些启发。
开发板放在公司里了,没有及时用相机拍下程序运行的画面,待周一补上吧。
/***********************************************************************
第8集 蜂鸣器实战应用(实操)
1.按键1按下,蜂鸣10ms,LED1~8全部点亮200ms再熄灭,表示开机。
2.开机后,按键2按下,蜂鸣10ms,LED1-8轮流点亮,表示切换煲汤、烧水等功能。
3.开机后按键1再次按下,蜂鸣10ms,LED全部熄灭,表示关机。
***********************************************************************/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义一个主时钟24MHz
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init();
void delay_ms(u16 ms);
void BEEP_on(u16 ms);
void On_Off_Blinker(u16 ms,u8 times);
sbit button_P33 = P3^3 ; // 按键1 上电后是上拉到5V,开关按下是下拉到0V
sbit button_P34 = P3^4 ; // 按键2 上电后是上拉到5V,开关按下是下拉到0V
sbit LEDON = P4^5 ; // P4^5 = 0, P2.0~P2.7上的LED才能点亮
sbit LED20 = P2^0 ; // P2.0上的LED
sbit LED21 = P2^1 ; // P2.1上的LED
sbit LED22 = P2^2 ; // P2.2上的LED
sbit LED23 = P2^3 ; // P2.3上的LED
sbit LED24 = P2^4 ; // P2.4上的LED
sbit LED25 = P2^5 ; // P2.5上的LED
sbit LED26 = P2^6 ; // P2.6上的LED
sbit LED27 = P2^7 ; // P2.7上的LED
sbit BEEP = P5^4; // 蜂鸣器
bit buttonState ; // 布尔变量存储按键状态
u8 a; // LED数组索引号
u16 on_time = 100; // 开机蜂鸣器响时100ms
u16 fu_time = 10; // 选择功能蜂鸣器响时10ms
u16 of_time = 1000; // 关机蜂鸣器响时1000ms
u16 on_led_blinker_time = 200; // 开机LED点亮时间200ms
u16 of_led_blinker_time = 500; // 关机LED点亮时间500ms
u8on_led_blinker_times = 1; // 开机LED闪烁次数1次
u8of_led_blinker_times = 3; // 关机LED闪烁次数3次
u8 LED = {0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F,0x00,0xFF }; // 数组用于记录8个LED灯
// 1111 1110 0xFE;
// 1111 1101 0xFD;
// 1111 1011 0xFB;
// 1111 0111 0xF7;
// 1110 1111 0xEF;
// 1101 1111 0xDF;
// 1011 1111 0xBF;
// 0111 1111 0x7F;
void main()
{
sys_init();//系统初始化
usb_init();//USB CDC 接口配置
EA = 1;
LEDON = 0 ; // 打开P2上的LED开关
while(1)
{
if(button_P33 == 0) // 按键1首次按下,button_P33,button_P34按下为0,松开为1
{
delay_ms(10);
if(button_P33 == 0) // 按键消抖,再次确认按键1已按下
{
while(button_P33 == 0); // 按键1按下后需松开才运行下面程序
BEEP_on(on_time); // 蜂鸣器响100ms表示开机
//开机LED闪烁
On_Off_Blinker(on_led_blinker_time,on_led_blinker_times);
while(button_P33 !=0) // 按键1已松开,表示已开机,可按P34执行功能选择,再次按下P33则跳出循环
{
if(button_P34 == 0) // 按键2按下
{
delay_ms(10);
if(button_P34 == 0) // 按键消抖,再次确认按键2已按下
{
while(button_P34 == 0); // 按键2按下后需松开才运行下面程序
BEEP_on(fu_time); // 选择功能蜂鸣响时10ms
P2 = LED;
a++;
}
if(a>=8) a = 0; //当a=8 时,功能选择完毕,对a置0,又重新开始
}
}
a = 0; //对a清零,避免下次开机时记忆在P2点灯状态下,又从功能1开始
BEEP_on(of_time); // 蜂鸣器长响1000ms表示关机
//关机LED闪烁
On_Off_Blinker(of_led_blinker_time,of_led_blinker_times);
while(button_P33 == 0); // 防止一直按着按键1不松,等松开后又开机
}
}
}
}
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
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; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
}
void BEEP_on(u16 ms) //蜂鸣器响铃延时函数
{
BEEP = 0;
delay_ms(ms);
BEEP = 1;
}
void On_Off_Blinker(u16 ms,u8 times) //开关机亮灯频率与次数
{
for(times; times>0; times--)
{
P2 = LED;
delay_ms(ms);
P2 = LED;
delay_ms(ms);
}
}
dumon
发表于 2024-3-12 08:40:51
92
程序演示视频如上:
dumon
发表于 2024-3-12 13:41:26
第8集 蜂鸣器实战应用(实操)——冲哥视频思路
1.按键1按下,蜂鸣10ms,LED1~8全部点亮200ms再熄灭,表示开机。
2.开机后,按键2按下,蜂鸣10ms,LED1-8轮流点亮,表示切换煲汤、烧水等功能。
3.开机后按键1再次按下,蜂鸣10ms,LED全部熄灭,表示关机。
4.增加按键3,按下后表示启动,选择的对应的功能的LED持续闪烁,表示正在工作,
且在工作的时候无法切换功能和关机。关机必须再长按KEY3后,退出运行状态,
才能关机。
动作流程: 先按KEY1开机 → 按KEY2选择工作模式 → 按KEY3启动 → 再按KEY3停止 → 按KEY1关机
说明:
1.未按KEY3启动前,可随时按KEY1进行关机;
2.按KEY3启动后,只能再长按KEY3退出运行,此时不能再选择工作模式,只能再按KEY1关机后,重新开机选择工作模式运行。
用2个位变量(bit Run_Flag,bit RunState)来存储状态,位变量就是只有0或1两个值,如开/关机,启动/停止状态。
冲哥视频的思路是3个if(),每个if去完成不同的功能模块,如第1个if是去完成开/关机——KEY1,第2个if是去选择工作模式——KEY2,第3个if是启动和停止——KEY3。
KEY1来控制Run_Flag的状态转换,初始状态Run_Flag=0,当KEY1按下后,如果是关机状态,执行开机程序,如果是开机状态,执行关机程序。通过对Run_Flag的判断,来执行对应的程序,并则执行程序后把Run_Flag取相反值。
即如果Run_Flag = 0,KEY1按下后,Run_Flag = 1,执行开机程序, 当Run_Flag = 1时,KEY1按下,Run_Flag = 0,执行关机程序。这里就是前面所学的按键点灯,先按下点灯,再按下熄灭,不断循环。
第2个if()主要就是加入了两个与条件,即Run_Flag ==1,RunState == 0,必须同时满足在“开机状态”和“停止状态”,才能按下KEY2键切换不同工作模式。这样才能满足我们第4点要求。
因为有8种模式,所以在内部写了1个循环8次移动LED的程序。
第3个if()主要是说明KEY3按键的功能,KEY3有个执行的前提条件:处于开机状态,并选好工作模式,KEY3按下才起作用,所以这里也是与运算。
这里做了一个很巧妙的地方,当已按KEY1开机,并且按KEY2选择好工作模式,按下KEY3,会进入启动状态,这里有个while()循环,不停闪烁模式代表的LED灯。这里面就必须设定1个跳出循环的条件,即如果在循环中我检测到KEY3再次按下,那么我就将循环的条件改变,就会跳出while()循环,从而达到将运行状态切换到停止状态的目的。
这里要注意的是,如果按KEY3已进入启动状态,那么必须再次长按KEY3退出启动状态,再按KEY1才可以关机,关机程序中会对Run_Flag =0,RunState = 0,RunMode = 0操作,这样才能继续进入下个选择模式和进行启动状态。程序的运行逻辑是这样。
可以实现功能,但应该还有优化空间。
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义一个主时钟24MHz
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init();
void delay_ms(u16 ms);
void BEEP_on(u16 ms);
bit Run_Flag = 0; // 开关机变量0-1,0表示关机,1表示开机
u8Run_Mode = 0; // 工作模式0~8,0:没有模式
u8StoreMode; // 存储工作模式
bit RunState = 0; // 启动状态,0启动,1停止
sbit KEY1 = P3^3 ; // 按键1
sbit KEY2 = P3^4 ; // 按键2
sbit KEY3 = P3^5 ; // 按键3
sbit LEDON = P4^5 ; // P4^5 = 0, P2.0~P2.7上的LED才能点亮
sbit LED20 = P2^0 ; // P2.0上的LED
sbit LED21 = P2^1 ; // P2.1上的LED
sbit LED22 = P2^2 ; // P2.2上的LED
sbit LED23 = P2^3 ; // P2.3上的LED
sbit LED24 = P2^4 ; // P2.4上的LED
sbit LED25 = P2^5 ; // P2.5上的LED
sbit LED26 = P2^6 ; // P2.6上的LED
sbit LED27 = P2^7 ; // P2.7上的LED
sbit BEEP = P5^4; // 蜂鸣器
u8 a; // LED数组索引号
u16 on_time = 100; // 开机蜂鸣器响时100ms
u16 fu_time = 10; // 选择功能蜂鸣器响时10ms
u16 of_time = 1000; // 关机蜂鸣器响时1000ms
void main()
{
sys_init();//系统初始化
usb_init();//USB CDC 接口配置
EA = 1;
while(1)
{
if(KEY1 == 0)
{
delay_ms(10);
if(KEY1 == 0)
{
while(KEY1 == 0); // 等待KEY1松开后执行下面程序
if(Run_Flag == 0) // 如果是关机状态,执行开机程序
{
Run_Flag = 1;
BEEP_on(10);
LEDON = 0 ; // 打开P2上的LED开关,这里就设置了必须处于开机状态,KEY2按键才能生效
P2 = 0x00;
delay_ms(200);
P2 = 0xFF;
}
else // 如果是开机状态,执行关机程序
{
Run_Flag = 0;
BEEP_on(10);
LEDON = 1 ; // 关闭P2上的LED总开关
P2 = 0xFF;
Run_Mode = 0;
RunState = 0; // 关机后变为停止状态
}
}
}
if(Run_Flag == 1 && RunState == 0 && KEY2 == 0) // 必须开机后KEY2按键才能切换模式
{
delay_ms(10);
if(Run_Flag == 1 && RunState == 0 && KEY2 == 0)
{
while(KEY2 == 0); // 等待KEY2松开后执行下面程序
BEEP_on(10);
++Run_Mode;
if(Run_Mode>8) Run_Mode = 1;
StoreMode = ~(1<<(Run_Mode-1)); // 0000 0001 向右移位
P2 = StoreMode ;
}
}
if(Run_Flag == 1 && Run_Mode>0 && KEY3 == 0) // 处于开机状态,并选好工作模式,KEY3按下才起作用
{
delay_ms(10);
if(Run_Flag == 1 && RunState == 0 && KEY3 == 0)
{
while(KEY3 == 0); // KEY3松开后才执行下面程序
while(RunState == 0) // 循环工作,检测到有KEY3按下则转为停止
{
// 保持为当前工作模式
P2 = StoreMode;
delay_ms(200);
P2 = 0xFF;
delay_ms(200);
if(KEY3 == 0) // KEY3再次按下时,变为停止
{
delay_ms(10);
if(KEY3 == 0)
{
while(KEY3 == 0);
RunState = 1; // 变为停止状态
}
}
}
}
}
}
}
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
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; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
}
void BEEP_on(u16 ms)
{
BEEP = 0;
delay_ms(ms);
BEEP = 1;
}
93
dumon
发表于 2024-3-13 22:22:49
第8集 蜂鸣器实战应用(实操)——冲哥视频思路
本程序在上一个程序的基础上有做更新,主要是第3个if()程序,根据按键点灯那栏所学,用按键去切换状态,不断按下对状态取反操作,可以达到按一下点灯,再按一下关灯的目的。
本次程序增加功能:KEY2只要在开机状态和KEY2按下时就能选择工作模式,KEY3在开机后第1次按下为启动状态,并进入锁定状态,此时按KEY2和KEY1键均不能起作用,必须再按KEY3到停止状态,此时可以继续按KEY2选择功能或按KEY1直接关机。或者继续按KEY3又继续进入启动状态。
这样是不是很像我们洗衣机,洗衣机的盖子有个感应器,当我们在洗衣机工作时直接打开盖子,是不是洗衣机会立刻停止。那假如我们想往里面再加衣服怎么办,是不是有个启动/暂停键,我们按下这个键就可以打开盖子往里加衣服,并且再次按启动键后又可以继续运行之前的设定过程。本程序就是模拟这个情况。
所以,程序是来源于生活,程序需要贴紧生活,根据实际需求不断去完善我们的程序!
/***********************************************************************
第8集 蜂鸣器实战应用(实操)——冲哥视频思路
1.按键1按下,蜂鸣10ms,LED1~8全部点亮200ms再熄灭,表示开机。
2.开机后,按键2按下,蜂鸣10ms,LED1-8轮流点亮,表示切换煲汤、烧水等功能。
3.开机后按键1再次按下,蜂鸣10ms,LED全部熄灭,表示关机。
4.增加按键3,按下后表示启动,选择的对应的功能的LED持续闪烁,表示正在工作,
且在工作的时候无法切换功能和关机。关机必须再长按KEY3后,退出运行状态,
才能关机。
动作流程: 先按KEY1开机 → 按KEY2选择工作模式 → 按KEY3启动 → 再按KEY3停止 → 按KEY1关机
说明:
1.按KEY2选择好工作模式后,按KEY3进入运行模式,再按KEY3退出运行模式,此时再按KEY3会继续运行上次工作模式,
<font color="#ff0000">2.按KEY3退出运行模式后,再按KEY2可再修改工作模式。这里KEY3充当1个暂停键的作用。</font>
3.进行运行模式后,需先按KEY3退出运行模式,再按KEY1关机。
***********************************************************************/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义一个主时钟24MHz
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init();
void delay_ms(u16 ms);
void BEEP_on(u16 ms);
bit Run_Flag = 0; // 开关机变量0-1,0表示关机,1表示开机
u8Run_Mode = 0; // 工作模式0~8,0:没有模式
u8StoreMode = 0xFE; // 存储工作模式
bit RunState = 0; // 启动状态,0启动,1停止
sbit KEY1 = P3^3 ; // 按键1
sbit KEY2 = P3^4 ; // 按键2
sbit KEY3 = P3^5 ; // 按键3
sbit LEDON = P4^5 ; // P4^5 = 0, P2.0~P2.7上的LED才能点亮
sbit LED20 = P2^0 ; // P2.0上的LED
sbit LED21 = P2^1 ; // P2.1上的LED
sbit LED22 = P2^2 ; // P2.2上的LED
sbit LED23 = P2^3 ; // P2.3上的LED
sbit LED24 = P2^4 ; // P2.4上的LED
sbit LED25 = P2^5 ; // P2.5上的LED
sbit LED26 = P2^6 ; // P2.6上的LED
sbit LED27 = P2^7 ; // P2.7上的LED
sbit BEEP = P5^4; // 蜂鸣器
u8 a; // LED数组索引号
u16 on_time = 100; // 开机蜂鸣器响时100ms
u16 fu_time = 10; // 选择功能蜂鸣器响时10ms
u16 of_time = 1000; // 关机蜂鸣器响时1000ms
void main()
{
sys_init();//系统初始化
usb_init();//USB CDC 接口配置
EA = 1;
while(1)
{
if(KEY1 == 0)
{
delay_ms(10);
if(KEY1 == 0)
{
while(KEY1 == 0); // 等待KEY1松开后执行下面程序
if(Run_Flag == 0) // 如果是关机状态,执行开机程序
{
Run_Flag = 1;
BEEP_on(10);
LEDON = 0 ; // 打开P2上的LED开关,这里就设置了必须处于开机状态,KEY2按键才能生效
P2 = 0x00;
delay_ms(200);
P2 = 0xFF;
}
else // 如果是开机状态,执行关机程序
{
Run_Flag = 0;
BEEP_on(10);
LEDON = 1 ; // 关闭P2上的LED总开关
P2 = 0xFF;
Run_Mode = 0;
RunState = 0; // 关机后变为停止状态
}
}
}
if(Run_Flag == 1 && KEY2 == 0) // 必须开机后KEY2按键才能切换模式
{
delay_ms(10);
if(Run_Flag == 1 && KEY2 == 0)
{
while(KEY2 == 0); // 等待KEY2松开后执行下面程序
BEEP_on(10);
++Run_Mode;
if(Run_Mode>8) Run_Mode = 1;
StoreMode = ~(1<<(Run_Mode-1)); // 0000 0001 向右移位
P2 = StoreMode ;
}
}
if(Run_Flag == 1 && Run_Mode>0 && KEY3 == 0) // 处于开机状态,并选好工作模式,KEY3按下才起作用
{
delay_ms(10);
if(Run_Flag == 1 && RunState == 0 && KEY3 == 0)
{
RunState = !RunState; // 变为停止状态
while(KEY3 == 0); // KEY3松开后才执行下面程序
while(RunState == 1) // 循环工作,检测到有KEY3按下则转为停止
{
// 保持为当前工作模式
P2 = StoreMode;
delay_ms(200);
P2 = 0xFF;
delay_ms(200);
if(KEY3 == 0) // KEY3再次按下时,变为停止
{
delay_ms(10);
if(KEY3 == 0)
{
while(KEY3 == 0);
RunState = 0; // 变为停止状态
}
}
}
}
}
}
}
void delay_ms(u16 ms)
{
u16 i;
do
{
i = MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
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; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
}
void BEEP_on(u16 ms)
{
BEEP = 0;
delay_ms(ms);
BEEP = 1;
}
dumon
发表于 2024-3-14 15:01:42
第9集数码管的静态显示
1位数码管可以看做是共阴极或共阳极的多个LED的组合,比如我们常见的7段式共阴极数码管,就是7个LED把他们的阴极全部连到GND上,那是不是还剩下7个阳极,当我们分别给这7个LED的阳极接到VCC,是不是我们就可以得到各种组合。那把这7个LED排成8的样子,是不是就可以显示0~9这几个数字了。通过不同的组合还可以形成字母等等。
那如果是多位数码管,那他们内部是怎么连线的呢,其实也很简单。假如现在有4位数码管,这个位就代表共阴或共阳极,4表示这个数码管共有4个共阴或共阳极,这里还有1个概念,叫段码,段码简单理解为单个数码管内LED的数量。加上小数点,我们1位数码管就有8个LED在里面,称之为8段,用A,B,C,D,E,F,G,DP表示,4位数码管的每一段都是连到一起的,比如第1位,第2位,第3位,第4位的A是全部联到一起的。那4位数码管共有几个引脚呢,8个段码+4个位码,共12个。如果我们给第1个位码高电平,第1段低电平,那么第1位上的第1个段码LED会点亮。如果1,2,3,4位全部接高电平,第1段接你电平,那么4个位上的第1段全部会点亮。这就是数码管的引脚说明。段码就是组成1个图案里有多少个LED灯,可能实际里面不只8个,还有米字形的。这个要看自己的个人需要。
第1位 第2位 第3位 第4位 ... ...第n位
____ ____ ____ ____ ____
| ___ | | ___ | | ___ | | ___ | | ___ |
| ___ | . | ___ | . | ___ | . | ___ | . | ___ | .
// 数码管相关说明,P0.0~P0.7为数码管的8个段码,P1.0,P1.1,P1.3为3个位码,共阴极数码管
// 位码要接低电平、段码接高电平,共阳极数码管位码要接高电平、段码接低电平,如:给第1位数码管位码低电平,数码管段码‘A’高电平,就能点亮第1位上的‘A'段码,如果给第1,2,3数码管位码低电平,数码管段码‘A’高电平,就能点亮第1,2,3位上的‘A'段码,
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位数码管位码
/*
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 DIG_Number = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, /*不带小数点*/
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF, /*带小数点*/
0x00 /*全部熄灭*/};
u8 SEG_Number= {0xFE,0xFD,0xF7};
用1个循环嵌套就能实现在从第1位显示到第n位,段码也可以循环显示
while(a<3)
{
P1 = SEG_Number; // 先选择位码
while(b<21)
{
P0 = DIG_Number; // 再选择段码
delay_ms(100);
b++; //段码数组循环
}
b = 0; // 对b值复位
a++; // 跳到下个位码
}
a = 0; //到达第3个位码后复位
本集最后留了1个问题,通过上面的实例,我们已经实现了数码管显示字符,但是有没有发现,是不是4个位或多个位都只能显示相同的段码内容,如果想在每个位码上显示不同的数字或字符,就需要学习下一课的内容了,数码管的动态显示,让数码管的每个位显示不同数字,如1234。
电子爱好者2024
发表于 2024-3-14 20:50:14
非常好的版主,适合小白来学习,以后就跟着您来混啦!{:5_300:}
dumon
发表于 2024-3-15 08:44:31
自己作为一个也刚开始接触学习STC单片机的学习者,学无先后,希望自己在课程中发现的和遇到的一些问题能够分享给大家,让大家少走一些弯路,多避一些坑,就感到非常满足了。
分享也是一种快乐!并乐在其中!感谢大家的支持与鼓励!{:handshake:}
soma
发表于 2024-3-15 09:23:42
能自己搭硬件的都是牛人啊