在最开始的
#define MAIN_Fosc 24000UL
定义主频为24.000MHz
在main函数中,开头可添加如下代码
EAXFR = 1; // 使能扩展的特殊寄存器
WTST = 0x00; // 设置代码等待时间为0,CPU 执行程序的速度最快
CKCON = 0x00; // 设置外部总线速度最快
以使得程序运行速度最快
点亮一个LED
sbit LED1 = P0 ^ 0;
sbit LED2 = P0 ^ 1;
LED1的对应引脚是P00 LED2的对应引脚是P01
以上代码一般添加在最开头(main函数之前) 用来定义引脚
P0M1 = 0x00; P0M0 = 0x00;
将P0端口的所有引脚都设置为推挽输出模式
因为要使用P0引脚,以上代码暂且理解为初始化 (目前这么理解 2025.3.29,知道具体为什么来还愿)
LED1 = 0;
LED2 = 1;
LED1输出低电平,LED2输出高电平
通过输出高低电平控制LED亮灭
编程实现LED闪烁&LED流水灯&数码管显示
LED闪烁
LED1 = ON;
delay_ms(500);
LED1 = OFF;
delay_ms(500);
很简单,在LED亮灭间穿插延时就好了
LED流水灯&数码管显示
u8 code LED_Index[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
u8 code SEG_Index[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
LED数组的作用,通过十六进制转二进制可以得到LED阵列的状态。如0x08就代表00001000,可得出LED状态
SEG数组的作用,通过不同的十六进制可让数码管显示不同的字符,如0x3F代表1,0x06代表2
void blink_3(void)
{
static u8 j;
P2 = LED_Index[j];
j++;
if(j > 8) j = 0;
delay_ms(500);
}
void blink_4(void)
{
static u8 k;
P0 = SEG_Index[k];
k++;
if(k > 9) k = 0;
delay_ms(500);
}
顺着数组做for循环,通过前面定义好的数组就可以实现LED流水灯或数码管计数了
I/O输入与输出控制按键
按键检测代码
while(1)
{
delay_ms(20); // 延时20毫秒
if(KEY1 == 0) // 检测按键KEY1是否被按下
{
if(i > 3) i = 0;
P2 = LED[i]; // 将P2端口的输出设置为LED数组的第i个元素
i++;
while(KEY1 == 0); // 等待按键释放(按键防抖或按键长按处理)
}
}
注意需要添加消抖延迟函数 & 等待按键释放的while函数
延时代码
void delay_ms(u16 ms)
{
u16 i;
do{
i = MAIN_Fosc / 6000;
while(--i);
}while(--ms);
}
例:需要延时200ms → delay_ms(200)
定时器中断
需要去ISP软件中选择“定时器计算器”工具,生成所需要的定时器初始化函数与定时器执行函数。
例:主频为24.000MHz进行500毫秒中断的定时器函数
void Timer0_Init(void)
{
TM0PS = 0x0F;
AUXR &= 0x7F;
TMOD &= 0xF0;
TL0 = 0xDC;
TH0 = 0x0B;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA=1;
}
注意!
- 一定要有EA=1定时器中断才能正常开启。
- 需要在main函数中进行初始化: Timer0_Init();
定时器执行函数
void Timer0_Isr(void) interrupt 1
{
//在这里编写需要执行的代码
}
定时器的周期调用
u16 Count_ms[3] = {0, 0, 0};
u8 state1=1, state2=1, state3=1, state4=1;
void Timer0_Isr(void) interrupt 1
{
u8 i;
for(i=0; i<3; i++)
{
Count_ms[i]++;
}
if(Count_ms[0]>=300)
{
Count_ms[0] = 0;
state1 = !state1;
P20 = state1;
P23 = !state1;
}
if(Count_ms[1]>=600)
{
Count_ms[1] = 0;
state2 = !state2;
P21 = state2;
}
if(Count_ms[2]>=900)
{
Count_ms[2] = 0;
state3 = !state3;
P22 = state3;
}
if(state1==0 && state2==0 && state3==0) P24=0;
else P24=1;
}
- 定时器中断的进阶用法: 通过定义三个不同阈值的计数器,定时器每中断一次,计数器加一。 实现在300ms,600ms,900ms时分别中断执行不同任务
数码管与定时器
任务:按下KEY1,数码管开始计时(如00:00:00), 按下KEY2,数码管显示年月日(如2024:12:11), 按下KEY3,数码管计时暂停,再按一下KEY3又恢复计时 按下KEY4,数码管计时清零。
while(1)
{
delay_ms(20);
if(P31 ==0){
state=1;
while(P30==0);
}
if(P30 == 0){
state=0;
while(P31==0);
}
if(P32 == 0){
if(state==1){
state=2;
}else if(state==2){
state=1;
}
while(P32==0);
}
if(P33 == 0){
miao=fen=shi=0;
while(P33==0);
}
switch(state){
case 0:seg_date();break;
case 1:seg_count();break;
case 2:seg_count_pause();break;
}
}
在while1中持续检测按键情况,哪个按键被按下则执行相对应的任务
数码管显示日期
void seg(u8 loc, u16 num)
{
switch(loc)
{
case 1:P24=1;P23=1;P22=1;break;
case 2:P24=1;P23=1;P22=0;break;
case 3:P24=1;P23=0;P22=1;break;
case 4:P24=1;P23=0;P22=0;break;
case 5:P24=0;P23=1;P22=1;break;
case 6:P24=0;P23=1;P22=0;break;
case 7:P24=0;P23=0;P22=1;break;
case 8:P24=0;P23=0;P22=0;break;
}
P0=SEG_NUM[num];
delay_ms(1); //延时的目的是为了让数码管有足够的时间显示当前的数字,避免显示闪烁
P0 = 0x00; //在显示下一个数字之前,先将当前的显示清除,避免串扰
}
// 该函数用于在不同的数码管位置显示不同的数字,每个case对应一个数码管的位置
void seg_date(void)
{
seg(1,2);seg(2,0);seg(3,2);seg(4,5);
seg(5,0);seg(6,1);seg(7,0);seg(8,1);
}
如何用数码管显示20250101
数码管显示计数器
void Timer0_Isr(void) interrupt 1
{
if(state==1){
miao ++;
if( miao>59 )
{
miao = 0;
fen++;
if( fen>59 )
{
fen = 0;
shi ++;
if( shi>23 )
shi = 0;
}
}
}
}
利用定时器中断进行时分秒计数
void seg_count_pause(void)
{
seg(1,shi/10);seg(2,shi%10);seg(3,SEG_NUM[16]);seg(4,fen/10);
seg(5,fen%10);seg(6,SEG_NUM[16]);seg(7,miao/10);seg(8,miao%10);
}
将计数器显示在数码管上
PWM
任务:PWM驱动电机,按键K1按下,电机转速提高(一共三档),按键K2按下,电机转速减慢,按键K3按下,电机停止
while(1)
{
delay_ms(20);
if(P31==0)
{
Speed++;
if(Speed > 3) Speed = 3;
while(P31==0);
}
if(P30==0)
{
Speed--;
if(Speed < 0) Speed= 0;
while(P30==0);
}
if(P33==0)
{
Speed = 0;
while(P33==0);
}
switch(Speed)
{
case 0: Compare = 0; break;
case 1: Compare = 50; break;
case 2: Compare = 75; break;
case 3: Compare = 100; break;
}
seg(1,Speed);
}
}
一个普通的按键检测代码,按下对应按键执行对应速度并在数码管显示
通过定时器中断来控制电机的占空比(PWM功能)
先上定时器中断执行代码:
void Timer0_Isr(void) interrupt 1
{
TL0 = 0x38;
TH0 = 0xFF;
counter++;
counter%=100;
if(counter<Compare)
{
motor = 1;
}else
{
motor = 0;
}
}
逐步分析:
TL0 = 0x38;
TH0 = 0xFF;
TL0和TH0是定时器0的低8位和高8位寄存器。 这两行代码的作用是重新装载定时器0的初始值0x38和0xFF是定时器的初始值,具体数值取决于单片机的时钟频率和定时器的模式。这些值决定了定时器溢出的时间间隔- 每当定时器溢出时,就会触发中断,执行这个中断服务例程。
counter++;
counter %= 100;
用于记录定时器中断的次数,当等于100时清零
PWM逻辑
if (counter < Compare)
motor = 1;
else
motor = 0;
Compare表示PWM的占空比阈值
这段代码的作用是根据计数器的值和占空比阈值来控制电机的状态:
如果
counter小于Compare,则将motor设置为1(表示电机开启或输出高电平)。如果
counter大于或等于Compare,则将motor设置为0(表示电机关闭或输出低电平)。
通过调整 Compare 的值,可以改变PWM的占空比,从而控制电机的速度或功率
PWM原理
占空比:占空比是指在一个周期内,高电平时间与总周期时间的比例。例如,如果占空比为50%,则在一个周期内,高电平和低电平各占一半时间。
在这段代码中,
counter的范围是0到99,总共100个计数值。Compare的值决定了高电平的持续时间。例如:如果
Compare= 50,则高电平持续50个计数值,占空比为50%。如果
Compare= 20,则高电平持续20个计数值,占空比为20%。