找回密码
 立即注册
楼主: njslmcc

学习心得

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-15 07:03:22 | 显示全部楼层
第八集 定时器周期性调度任务 学习笔记

如何修正编译错误,
ISP更多设置,分包时间显示与否

一、用期性任务介绍
任务1:用一个定时器实现这个任务。LED1实现0.3秒取反一次,LED2实现06秒取反一次,LED3实现0.9秒取反一次
数组使用分两步:
1、定义:类型 名称[长度] = {数值};
2、使用:赋值:名称[索引] = 数值

任务2:数组点亮LED,实现流水灯
注意事项:LED是0亮1灭,数组长度要把握好,流水灯移动的时间
变量定义
        u8 State[8]   = {0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
核心代码
        if(Count_ms[0]>=500)
        {            
                Count_ms[0] = 0;
                P0 = ~State[num];   //num 取值0-7
                num++;
                if(num>7) num=0;
        }

任务3:按键1按一下,LED通过数组移动一下
注意事项:按键不能再通过while判断是否按下松开了,可以通过按键按下计数
变量定义
        u16 Key_Vol = 0;                 //按键按下持续时间
核心代码
        P0 = ~State[num];              //num 取值0-7
      
        if(Count_ms[1]==1000)       //1000ms执行一次
        {
                Count_ms[1] = 0;
                printf("Ai8051u\r\n");
        }
        
        if(Count_ms[2]==10)             //10ms执行一次
        {
                Count_ms[2] = 0;
                if(P32 == 0)                 //按键按下
                {
                        Key_Vol ++;
                        if(Key_Vol==5)      //按键按下
                        {
                                num ++;
                        }
                }
                else
                {
                        Key_Vol = 0;
                }
       }

二、文件的创建(.c和.h)
创建程序文件三步,把硬件需要的初始化做一个config.c
新建文件并保存,添加到工程,添加到引用路径
一般一个.c和一个.h文件,执行一个外设或者一个任务或功能,这样可以让代码看起来简洁明了

新建XXX.c和XXX.h文件,代表一个功能块,
XXX.h格式:
#ifndef __XXX_H
#define __XXX_H
调用头文件
函数声明,
#endif

XXX.C格式
#inlude “XXX.H”
函数定义
添加文件一定要记得引用路径和添加一工程里。

三、结构体的介绍


四、结构体数组的周期性任务调度
LED1   0.3秒闪一次,
LED2   0.6秒闪一次,
LED3   0.9秒闪一次
1、都有定时器1ms加的变量
2、都有一定设定的计数目标
3、都有需要执行的功能
4、定时时间到了才能执行

typedef struct
{
        u8   Run;                                // 任务状态,Run/Stop
        u16 TIMCount;                        // 定时器计数器
        u16 TRITime;                          // 重载计数器
        void (*TaskHook)(void);           //任务函数
}TASK_COMPONENTS;

任务4:按键1按一下,LED通过数组移动一下,用虚拟LED显示
思路:
变量定义
u8 State[8]   = {0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};        //事先编排好
u8 Key_Vol[2] = {0, 0};

核心代码
void Key_Task(void)
{
        if (P32 == 0)
        {
                Key_Vol[0]++;               

                if (Key_Vol[0]==5)
                {
                        //按键按下的任务
                        P0 = ~State[Key_Vol[1]];
                        Key_Vol[1]++;  
                }
        }
        else
        {
                Key_Vol[0] = 0;
        }
}

简易舞台灯光控制系统:
1、按下按钮1,一键启动或关闭LED动作
2、启动后电脑上的LED开始工作
P0口的LED灯循环往下移动
P2口的LED灯循环往上移动
P4口的LED高四位和低四位交替闪烁
P1口的8个LED灯全亮或全灭交替(P1.2的位置用P5.2代替)

在Task.c 中写上如下任务表:
TASK_COMPONENTS Task_Comps[]=
{
        //状态  计数    重载数   函数
        {0,       10,      10,       Key_Task},                  /* task 1 Period: 10ms */
        {0,       100,    100,     LED1_Task},                /* task 2 Period: 100ms */
        {0,       200,    200,     LED2_Task},                /* task 3 Period: 200ms */
        {0,       300,    300,     LED3_Task},                /* task 4 Period: 300ms */
        {0,       400,    400,     LED4_Task},                /* task 5 Period: 400ms */
};

在io.c中写上:

//按下按钮1,一键启动或关闭LED动作
u8 Stage1 = 0;      //LED的初值,LED可以为P3上的一个口
u8 Key_Vol = 0;

void Key_Task(void)
{
        if (P32==0)
        {
                Key_Vol++;
                if (Key_Vol==5)
                {
                        //按键按下的任务
                        Stage1 = !Stage1;
                        LED0 = Stage1;   
                }         
        }
        else
        {
                Key_Vol = 0;
        }
}

// P0口的LED灯循环往下移动
u8 Stage2 = 0;      //初值
u8 LED1[8] = {0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}

void LED1_Task(void)
{
        P0 = ~LED1(Stage2);
        Stage2++;
        if (Stage2>7)  Stage2 = 0;  
}

// P2口的LED灯循环往上移动
u8 Stage3 = 0;      //初值
u8 LED2[8] = {0x80,0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}

void LED2_Task(void)
{
        P2 = ~LED2(Stage3);
        Stage3++;   
        if (Stage3>7)  Stage3 = 0;  
}

// P4口的LED高四位和低四位交替闪烁
u8 Stage4 = 0;  //初值
u8 LED4[2] = {0x0f, 0xf0}

void LED3_Task(void)
{
        P4 = LED4(Stage4);
        Stage4++;   
        if (Stage4>1)  Stage4 = 0;  
}

// P0 P1口的8个LED灯全亮或全灭交替(P1.2的位置用P5.2代替)
u8 Stage5 =0;  //初值
u8 LED5[2] = {0x00,0xff}

void LED4_Task(void)
{
        P0 = P1 = LED5(Stage5);   //(P1.2的位置用P5.2代替)怎么实现?
        Stage5++;  
        if (Stage5>1)  Stage5 = 0;  
}

在io.h中写上
void Key_Task(void);
void LED1_Task(void);
void LED2_Task(void);
void LED3_Task(void);
void LED4_Task(void);

这一课的内容很有用,再遇到类似的多任务,只要做好如下3步:
1、在Task.c的列表中按“状态,计数,重载数,函数”的格式加上新的任务,
2、在io.c中写好函数
3、在io.h中登记一下


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-17 06:03:32 | 显示全部楼层
第九集 数码管 学习笔记

擎天柱交流网址:https://www.stcaimcu.com/thread-11902-1-1.html
里面会有注意事项及常见编程错误等信息

一、数码管介绍
本质都是LED,类型分共阴共阳,74HC595

二、数码管显示原理
显示段码,位码,总显示时间不能大于20ms

三、数码管静态显示

任务1:在第一位上显示0
在任务列表中加入
   {0,  1,    1,     Seg_Task},  

在io.c中加入代码:

u8 SEG_NUM[]=
{
    0x3F,       /*'0', 0*/
    0x06,       /*'1', 1*/
    0x5B,       /*'2', 2*/
    0x4F,       /*'3', 3*/
    0x66,       /*'4', 4*/
    0x6D,       /*'5', 5*/
    0x7D,       /*'6', 6*/
    0x07,       /*'7', 7*/
    0x7F,       /*'8', 8*/
    0x6F,       /*'9', 9*/
    0x77,       /*'A', 10*/
    0x7C,       /*'B', 11*/
    0x39,       /*'C', 12*/
    0x5E,       /*'D', 13*/
    0x79,       /*'E', 14*/
    0x71,       /*'F', 15*/
    0x40,       /*'-', 16*/
    0x00,       /*' ', 17*/
    0x80,       /*'.', 18*/
};

u8 T_NUM[8] =
{
    0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80
};

void Init_595(void)
{
    HC595_SER = 0;
    HC595_RCK = 0;  
    HC595_SCK = 0;
}

/************向HC595发送一个字节函数************/

void Send_595(u8 dat)
{
    u8 i;
   
    for (i=0;i<8;i++)
    {
        dat<<=1;
        HC595_SER   = CY;     //先把数据写到引脚上
        HC595_SCK = 1;        //输出上升沿的时钟信号
        HC595_SCK = 0;
    }   
}

void Display_Seg(u8 HC595_1, u8 HC595_2)
{
    Send_595(HC595_1);  //数码管段码输出,高电平点亮
    Send_595(HC595_2);  //数码管位码输出,低电平点亮
   
    HC595_RCK = 1;      //数据输出        
    HC595_RCK = 0;   
}   

u8 Seg_no = 0;

void Seg_Task(void)
{
    Display_Seg( SEG_NUM[0], ~T_NUM[0]);
}

在io.h中增加如下定义:
#define HC595_SER         P34
#define HC595_RCK         P35
#define HC595_SCK         P32

void Init_595(void);
void Send_595(u8 dat);
void Display_Seg(u8 HC595_1, u8 HC595_2);
void Seg_Task(void);

就可以在第一位显示出“0”了。

四、数码管动态显示

任务2,显示12345678
只要改写下列函数
void Seg_Task(void)
{
    Display_Seg( SEG_NUM[Seg_no+1], ~T_NUM[Seg_no]);     //刷段码和位码
    Seg_no++;
    if(Seg_no>7)
        Seg_no = 0;
}

任务3,按00-00-00的格式显示时分秒
在任务列表中加入
   {0, 1000,  1000,  TIMECOUNT_Task}, /* task 5 Period: 1000ms */  

在io.c中加入代码
u8 shi  = 0;
u8 fen  = 0;
u8 miao = 0;

void TIMECOUNT_Task(void)
{
    miao ++;
    if( miao>59 )
    {
        miao = 0;
        fen++;
        if( fen>59 )
        {
            fen = 0;
            shi ++;
            if( shi>23 )
                shi = 0;
        }
    }
}

void Seg_Task(void)
{
    for(i=0;i<8;i++)
    {
        //算出每一位的值并显示出来
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    Seg_no++;
    if(Seg_no>7)
        Seg_no = 0;
}

在io.h中增加如下定义:
void TIMECOUNT_Task(void);

就能在LED上显示时分秒了

五、虚拟显示—LED和数码管
(有空的时候可以看一下)

课后小练习

简易10秒免单计数器,
1、在前四位数码管上显示目标时间,即“10.00”表示定时时间10秒钟
2、后四位显示当前的计时00.00,最小单位为10ms
3、按下开始按钮后,每10ms最末尾的数字+1,直到按下结束按钮后停止计数。

在任务表添加两个任务
   {0,  1,    1,     Seg_Task},      
   {0, 10,  10,  TIMECOUNT_Task},

在io.c中加入如下代码:

u8 Stage1 = 0;      
u16 Key_Vol[2] = {0, 0};

void TIMECOUNT_Task(void)
{
    if (P32==0)
    {
        Key_Vol[0]++;
        if (Key_Vol[0]==5)
        {
            //按键按下的任务
            Stage1 = !Stage1;
            if(Stage1==0)
                Key_Vol[1]++;
            else
                Key_Vol[1]=0;   
        }         
    }
    else
    {
        Key_Vol[0] = 0;
    }
}

void Seg_Task(void)
{
    if(Seg_no==0)
    {
        num = 1;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==1)
    {
        num = 0;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==2)
    {
        num = 0;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==3)
    {
        num = 0;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==5)
    {
        num = Key_Vol[1]/1000;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==6)
    {
        num = (Key_Vol[1]%1000)/100;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==7)
    {
        num = (Key_Vol[1]%100)/10;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==8)
    {
        num = Key_Vol[1]%10;
        Display_Seg( SEG_NUM[num], ~T_NUM[Seg_no]);
    }

    Seg_no++;
    if(Seg_no>7)
        Seg_no = 0;
}



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-18 09:26:44 | 显示全部楼层
第十集 虚拟键盘LED和数码管 学习笔记

摘要
一、虚拟显示——LED
1、硬件:擎天柱
2、软件:最新版的ISP
3、参数设置:仿真调试接口,接口设置
4、选择指定的接口和协议:

功能1:控制DIP40的各个管脚上LED的状态
命令格式:4CH 45H 44H 28H   x1          x2                   px
                命令头                  数据长度 P0~P5的屏蔽位 P0~P5的状态
库函数声明:void LED40_SendData(BYTE * dat, BYTE size);

功能2:控制DIP40的指定管脚上LED的状态
命令格式:4CH 45H 44H 28H 第5字节    第6字节                    第7字节
                命令头                 数据长度   P0~P5的端口屏蔽位   端口的状态
端口屏蔽位(01H:P0,02H:P1,04H:P2,08H:P3,10H:P4,20H:P5)
库函数声明:void LED40_SetPort(BYTE port, BYTE dat);
其中函数已封装,用起来更方便
port中0代表P0
port中1代表P1
port中2代表P2
port中3代表P3
port中4代表P4
port中5代表P5

功能3:控制DIP40的指定管脚输出高电平
命令格式:4CH 45H 44H 28H 第5字节             第6字节       第7字节
                命令头                 数据长度 (02H) 固定为00H   端口的状态数据
端口的状态数据
px的bit7,固定为1
px的bit6,bit5,bit4,bit3,指定第几组管脚(0:P0,1:P1,2:P2,。。。。)
px的bit2,bit1,bit0,指定管脚的第几位(0:bit0,1:bit1,。。。。)
库函数声明:void LED40_SetBit(BYTE port, BYTE bt);

功能4:控制DIP40的指定管脚输出低电平
命令格式:4CH 45H 44H 28H  第5字节               第6字节      第7字节
                命令头                  数据长度(02H) 固定为00H   端口的状态数据
端口的状态数据
px的bit7,固定为0
px的bit6,bit5,bit4,bit3,指定第几组管脚(0:P0,1:P1,2:P2,。。。。)
px的bit2,bit1,bit0,指定管脚的第几位(0:bit0,1:bit1,。。。。)
库函数声明:void LED40_ClrBit(BYTE port, BYTE bt);

任务1,P2口流水灯,P10闪烁
把定时器周期性调度任务拷过来,下载最新的USB库函数,头文件更新
1、在task.c中建立一个任务
  {0, 500,   500,   TASK_1},

2、在io.c中加入如下代码:
u8 P2_State = 0x01;
u8 P10_State = 0;
void TASK_1(void)
{
//-----------p2端口流水灯---------------
    LED40_SetPort(2,~P2_State);     //点亮P20
    P2_State = (P2_State<<1);
    if(P2_State == 0)
        P2_State = 1;
   
//-----------p10闪烁---------------
    if(P10_State == 0)
        LED40_SetBit(1,0);
    else
        LED40_ClrBit(1,0);
   
    P10_State = !P10_State;
}

3、在io.h中添加定义:
void TASK_1(void);
即可完成任务1。

二、虚拟显示——数码管

功能1:在数码管上显示字符串
命令格式:37H 53H 45H 47H    53H         00H 00H 00H 00H s1 s2 s3 s4
                命令头                    功能选择  保留                     字符的ASC码,以’\0’结尾
库函数声明:void SEG7_ShowString(const char *fmt,…….);
库函数调用:SEG7_ShowString(“%08lx”,0x1234abcdL);

功能2:在数码管上显示4字节长整形数
命令格式:37H 53H 45H 47H   4CH         x1                           x2                00H
                命令头                   功能选择  存储格式0:LE,1:BE   进制 2/10/16  保留
后面的d1 d2 d3 d4为四字节长整形数
库函数声明:void SEG7_ShowLong(long n, char radix);  //n: 长整形数,rdix:进制
库函数调用:SEG7_ShowLong(0x876432,16);

任务2:左边数码管显示P32按下次数,右边数码管显示P33按下次数
1、在task.c中建立2个任务
{0, 10,    10,    Key_Task},        /* task 4 Period: 10ms */
{0, 500,   500,   TASK_2},

2、在io.c中加入如下代码:
u16 Key1_Count = 0;     //P32按键次数
u16 Key2_Count = 0;     //P33按键次数
u32 Key_Count = 0;
void Key_Task(void)
{
//-----------P32按键次数---------------
    if (P32 == 0)
    {
        Key_Vol++;
        if (Key_Vol==5)
        {
            //按键按下的任务
            Key1_Count++;
        }
    }
    else
    {
        Key_Vol = 0;
    }

//-----------P33按键次数---------------   
    if (P33 == 0)
    {
        Key_Vol_2++;
        if (Key_Vol_2==5)
        {
            //按键按下的任务
            Key2_Count++;
        }
    }
    else
    {
        Key_Vol_2 = 0;
    }
}

void TASK_2(void)
{   
    Key_Count =  Key1_Count*10000 +  Key2_Count;   
    SEG7_ShowLong(Key_Count,10);
}

3、在io.h中加上
void Key_Task(void);
void TASK_2(void);
即可完成任务2。

三、虚拟键盘

虚拟键盘接口
功能:在虚拟键盘上按键,然后发送十一日键值到设备
命令格式:4BH 45H 59H  50H   00H    x1           00H 00H
                命令头                    保留   按键键值  保留
比如收到35H(十进制53),就说明按下了5

任务3:按下数字按键,在数码管显示相应的按键数字
1、在task.c中建立任务
{0, 10,    10,      Key_Task},        /* task 4 Period: 10ms */
{0, 500,   500,   TASK_3},

2、在io.c中加入代码
u32 Rec_Num = 0;
void TASK_3(void)
{   
    SEG7_ShowLong(Rec_Num,10);
}

3、在io.h中添加
void Key_Task(void);
void TASK_3(void);

4、在main.c中加入定义
extern u32 Rec_Num;
在主程序循环中加入
        if (bUsbOutReady)                           //如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
            //printf("");
            Rec_Num = UsbOutBuffer[5];
            usb_OUT_done();
        }
即可完成任务3。

课后小练
密码锁
1、没有输入时,显示“--------”
2、有输入时,按下一个按键,开始按顺序写入,比如第个按1,显示“1-------”
3、当按下的密码为“12345678”时,数码管显示open的字符,否则,还是显示“--------”

//-------------试做了一下,逻辑也不是很对,后面再探究问题在哪里------------------
1、在task.c中建立任务
{0, 10,    10,      Key_Task},        /* task 4 Period: 10ms */
{0, 500,   500,   TASK_4},

2、在io.c中加入代码
u32 Show_Str[8] = {-};
u32 Rec_Str[8] = {-};
u32 Rec_Num = 0;
u8 num = 0;

//--------------这一段还要完善-----------
void NumToStr(void)
{
    if (Rec_Num是数字)  赋值 Rec_Str[num];

    num++;

    if (num>7)
    {
        if ( Rec_Str = "12345678"
        {
             Show_Str = "--OPEN--";
        }
        else
        {
             Show_Str = "--------";
        }
    }
}

void TASK_4(void)
{   
    SEG7_ShowString(Show_Str);   //
}

3、在io.h中添加
void Key_Task(void);
void TASK_4(void);

void NumToStr(void);

4、在main.c中加入定义
extern u32 Rec_Num;
在主程序循环中加入
        if (bUsbOutReady)                           //如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
            //printf("");
            Rec_Num = UsbOutBuffer[5];
            usb_OUT_done();
        }



回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-18 17:54:30 | 显示全部楼层
第十一集 矩阵按键 学习笔记

用行和列组合,以减少IO口的使用数量
任务1:数码管显示当前的按键号
把多任务定时器那个目录整个拷过来

在main.c中,加入
    Init_595();  //在EA = 1;之前

在task.c中建立2个任务
    {0,     10,     10,     Task_1},
    {0,     100,    100,    Seg_Task},

在io.c中加入
u8 SEG_NUM[]=
{
    0x3F,       /*'0', 0*/
    0x06,       /*'1', 1*/
    0x5B,       /*'2', 2*/
    0x4F,       /*'3', 3*/
    0x66,       /*'4', 4*/
    0x6D,       /*'5', 5*/
    0x7D,       /*'6', 6*/
    0x07,       /*'7', 7*/
    0x7F,       /*'8', 8*/
    0x6F,       /*'9', 9*/
    0x77,       /*'A', 10*/
    0x7C,       /*'B', 11*/
    0x39,       /*'C', 12*/
    0x5E,       /*'D', 13*/
    0x79,       /*'E', 14*/
    0x71,       /*'F', 15*/
    0x40,       /*'-', 16*/
    0x00,       /*' ', 17*/
    0x80,       /*'.', 18*/
};

u8 T_NUM[8] =
{
        0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80
};

void Init_595(void)
{
    HC595_SER = 0;
    HC595_RCK = 0;  
    HC595_SCK = 0;
}

/************向HC595发送一个字节函数************/
void Send_595(u8 dat)
{
    u8 i;
   
    for (i=0;i<8;i++)
    {
        dat<<=1;
        HC595_SER   = CY;     //先把数据写到引脚上
        HC595_SCK = 1;        //输出上升沿的时钟信号
        HC595_SCK = 0;
    }   
}

void Display_Seg(u8 HC595_1, u8 HC595_2)
{
    Send_595(HC595_1);  //数码管段码输出,高电平点亮
    Send_595(HC595_2);  //数码管位码输出,低电平点亮
   
    HC595_RCK = 1;      //数据输出        
    HC595_RCK = 0;   
}   

u8 Seg_no = 0;

void Seg_Task(void)
{
    if(key_num == 0xff)
        Display_Seg( SEG_NUM[17], ~T_NUM[0]);
    else
        Display_Seg( SEG_NUM[key_num], ~T_NUM[0]);
}

u8 key_num = 0xff;

//任务1:数码管显示当前的按键号
void Task_1(void)
{
    COL1 = 0;
    COL2 = 0;
    COL3 = 0;
    COL4 = 0;
    ROW1 = 1;
    ROW2 = 1;
   
    if((ROW1 == 0)||(ROW2 == 0))
    {
        if(( ROW1 == 0 ) && ( ROW2 == 0 ))
        {
            ;
        }
        else if((( ROW1 == 0 ) && ( ROW2 == 1 )) || (( ROW1 == 1 ) && ( ROW2 == 0 )))
        {
            if( ROW1 == 0)      //判断行起始序号
                key_num = 0;
            else if( ROW2 ==0)
                key_num = 4;
            
            COL1 = 1;
            COL2 = 1;
            COL3 = 1;
            COL4 = 1;
            ROW1 = 0;
            ROW2 = 0;
            
            if(COL1 == 0)       //判断列起始序号
            {
                key_num += 0;
            }
            else if(COL2 == 0)
            {
                key_num += 1;               
            }
            else if(COL3 == 0)
            {
                key_num += 2;               
            }
            else if(COL4 == 0)
            {
                key_num += 3;               
            }
        }
        COL1 = 0;
        COL2 = 0;
        COL3 = 0;
        COL4 = 0;
        ROW1 = 1;
        ROW2 = 1;
    }
    else
    {
        key_num = 0xff;   
    }
}

在io.h中加入
#define HC595_SER         P34
#define HC595_RCK         P35
#define HC595_SCK         P32

#define ROW1    P06
#define ROW2    P07
#define COL1    P00
#define COL2    P01
#define COL3    P02
#define COL4    P03

//extern u8 key_num;

void Task_1(void);
void Init_595(void);
void Seg_Task(void);

编译下载,运行OK。

任务2:密码锁
1、没有输入时,显示“--------”
2、有输入时,按下一个按键,开始顺序写入
3、当按下的密码为“12345670”,数码管显示“       1”
   否则,还是显示“--------”

在main.c中,加入
    Init_595();    //在 EA = 1;之前

在Task.c中,加入
    {0,    10,   10,    Task_1},
    {0,    1,    1,     Seg_Task},  
    {0,    10,   10,    PW_write_Task},  

在io.c中,加入
u8 SEG_NUM[]=
{
    0x3F,       /*'0', 0*/
    0x06,       /*'1', 1*/
    0x5B,       /*'2', 2*/
    0x4F,       /*'3', 3*/
    0x66,       /*'4', 4*/
    0x6D,       /*'5', 5*/
    0x7D,       /*'6', 6*/
    0x07,       /*'7', 7*/
    0x7F,       /*'8', 8*/
    0x6F,       /*'9', 9*/
    0x77,       /*'A', 10*/
    0x7C,       /*'B', 11*/
    0x39,       /*'C', 12*/
    0x5E,       /*'D', 13*/
    0x79,       /*'E', 14*/
    0x71,       /*'F', 15*/
    0x40,       /*'-', 16*/
    0x00,       /*' ', 17*/
    0x80,       /*'.', 18*/
};

u8 T_NUM[8] =
{
        0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80
};

#define HC595_SER         P34
#define HC595_RCK         P35
#define HC595_SCK         P32

void Init_595(void)
{
    HC595_SER = 0;
    HC595_RCK = 0;  
    HC595_SCK = 0;
}

/************向HC595发送一个字节函数************/
void Send_595(u8 dat)
{
    u8 i;
   
    for (i=0;i<8;i++)
    {
        dat<<=1;
        HC595_SER = CY;       //先把数据写到引脚上
        HC595_SCK = 1;        //输出上升沿的时钟信号
        HC595_SCK = 0;
    }   
}

void Display_Seg(u8 HC595_1, u8 HC595_2)
{
    Send_595(HC595_1);  //数码管段码输出,高电平点亮
    Send_595(HC595_2);  //数码管位码输出,低电平点亮
   
    HC595_RCK = 1;      //数据输出        
    HC595_RCK = 0;   
}   

u8 Seg_no = 0;
u8 password[8] = {16,16,16,16,16,16,16,16};

void Seg_Task(void)
{
    u8 num = 0;
   
    if(Seg_no==0)
    {
        Display_Seg( SEG_NUM[password[0]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==1)
    {
        Display_Seg( SEG_NUM[password[1]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==2)
    {
        Display_Seg( SEG_NUM[password[2]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==3)
    {
        Display_Seg( SEG_NUM[password[3]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==4)
    {
        Display_Seg( SEG_NUM[password[4]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==5)
    {
        Display_Seg( SEG_NUM[password[5]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==6)
    {
        Display_Seg( SEG_NUM[password[6]], ~T_NUM[Seg_no]);
    }
    else if(Seg_no==7)
    {
        Display_Seg( SEG_NUM[password[7]], ~T_NUM[Seg_no]);
    }
   
    Seg_no++;
    if(Seg_no>7)
        Seg_no = 0;
}

u8 Key_Vol3 = 0;
u8 key_no = 0;

void PW_write_Task(void)
{
    if (key_num != 0xff)
    {
        Key_Vol3++;
        if (Key_Vol3==5)
        {
            //按键按下的任务
            if( key_no == 0 )
            {
               password[0] = 16;
               password[1] = 16;
               password[2] = 16;
               password[3] = 16;
               password[4] = 16;
               password[5] = 16;
               password[6] = 16;
               password[7] = 16;  
            }
            password[key_no] = key_num;            
            key_no++;
            //password[7] = 17;
            if(key_no == 8)     //密码输入完毕
            {
                if ((password[0] == 1) &&
                    (password[1] == 2) &&
                    (password[2] == 3) &&
                    (password[3] == 4) &&
                    (password[4] == 5) &&
                    (password[5] == 6) &&
                    (password[6] == 7) &&
                    (password[7] == 0))
                {
                    password[0] = 17;                    
                    password[1] = 17;                    
                    password[2] = 17;
                    password[3] = 17;                    
                    password[4] = 17;                    
                    password[5] = 17;
                    password[6] = 17;                    
                    password[7] = 1;                    
                }
                else
                {
                     password[0] = 16;   
                     password[1] = 16;   
                     password[2] = 16;   
                     password[3] = 16;   
                     password[4] = 16;   
                     password[5] = 16;   
                     password[6] = 16;   
                     password[7] = 16;
                }
               
                key_no = 0;           
            }               
        }
    }
    else
    {
        Key_Vol3 = 0;
    }   
}

u8 key_num = 0xff;

//任务1:数码管显示当前的按键号
void Task_1(void)
{
    COL1 = 0;
    COL2 = 0;
    COL3 = 0;
    COL4 = 0;
    ROW1 = 1;
    ROW2 = 1;
   
    if((ROW1 == 0)||(ROW2 == 0))
    {
        if(( ROW1 == 0 ) && ( ROW2 == 0 ))
        {
            ;
        }
        else if((( ROW1 == 0 ) && ( ROW2 == 1 )) || (( ROW1 == 1 ) && ( ROW2 == 0 )))
        {
            if( ROW1 == 0)      //判断行起始序号
                key_num = 0;
            else if( ROW2 ==0)
                key_num = 4;
            
            COL1 = 1;
            COL2 = 1;
            COL3 = 1;
            COL4 = 1;
            ROW1 = 0;
            ROW2 = 0;
            
            if(COL1 == 0)       //判断列起始序号
            {
                key_num += 0;
            }
            else if(COL2 == 0)
            {
                key_num += 1;               
            }
            else if(COL3 == 0)
            {
                key_num += 2;               
            }
            else if(COL4 == 0)
            {
                key_num += 3;               
            }
        }
        COL1 = 0;
        COL2 = 0;
        COL3 = 0;
        COL4 = 0;
        ROW1 = 1;
        ROW2 = 1;
    }
    else
    {
        key_num = 0xff;   
    }
}

在io.h中加入
#define HC595_SER         P34
#define HC595_RCK         P35
#define HC595_SCK         P32

#define ROW1    P06
#define ROW2    P07
#define COL1    P00
#define COL2    P01
#define COL3    P02
#define COL4    P03

extern u8 key_num;

void Key_Task(void);
void Task_1(void);
void Init_595(void);
void Seg_Task(void);
void PW_write_Task(void);

编译成功,运行OK

课后练习题目
简易洗衣机面板
1、  按下开机键后,数码管显示1,表示默认为清洗模式1
2、  用矩阵按键模拟洗衣机的操作面板,前五个按钮模拟1-5的清洗模式按键选择几就显示数字几,表示当前为第几个功能
3、  按下启动后,按照选择的模式对应的时间开始倒计时,倒计时结束后,数码管熄灭,表示清洗完成,清洗时间任意设定。




回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:31
  • 最近打卡:2025-10-16 03:46:58
已绑定手机

4

主题

9

回帖

134

积分

注册会员

积分
134
发表于 2025-9-19 06:04:06 来自手机 | 显示全部楼层
你遇到过那家产品很牛的公司,是哪家,我去观摩一番看看对我有没有帮助,遇到问题直接给一个光盘,这点我在感觉上,很是好奇,既然公司很牛,都什么年代了,还用光盘记载资料,资料容量肯定很庞大吧,说所有的开发问题这里面都有答案,一个小问题就要去研读一整张光盘,这对我们普通开发者而言,也太恐怖了,最后只能放弃,就当前的局势来说,光盘的价值重点不在于研读,而是用来收藏,异或是喂给ai模型去学,遇到小问题我会另辟蹊径去解决问题。只有大问题,譬如STC-ISP下载软件bug,我才会考虑发问,我是跳过学习就直接尝试做产品的新人,尽量避免无效的学习。这就好比一些博士,高学历的人,耗尽年华,所学知识,98%一辈子都用不上,一边学一边遗忘,沉默成本太高了,耗散了社会资源,我得出结论,知识也不太值得去学,只要能看得懂就足矣。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-21 17:01:32 | 显示全部楼层
刘师傅*** 发表于 2025-9-19 06:04
你遇到过那家产品很牛的公司,是哪家,我去观摩一番看看对我有没有帮助,遇到问题直接给一个光盘,这点我在 ...

谢谢你的回复,你提出的问题在AI时代更加突出,人类几乎所有学到的知识在AI面前都被秒杀了,在这种情况下,现有的教育方式已经没有未来,下一步连人类继续存在的意义都是个问题。

我们这里只是记录一下自己的学习体会,不用纠结那些问题。
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-21 17:39:37 | 显示全部楼层
第十二集 复位系统 学习笔记

确保系统处于确定状态:
      复位操作可以确保单片机在开始工作时处于已知的状态,使其能够正确初始化各个寄存器和外设。
避免不确定行为:
      没有进行复位时,内部控制寄存器的内容可能是随机的,这可能导致定时器溢出、中断异常、外设误操作等不确定行为。
初始化系统:
      复位操作可以进行系统的初始化,包括清除寄存器、设置默认值、配置时钟等,为系统正常运行做好准备。
保证程序正常开始执行:
      复位确保程序从正确的地址开始执行,避免跳转到未知的地址或执行错误的指令

一、硬件复位
1、对复位时间要求不高,可选上电复位。对复位时间要求高,P32 P33 至少一个上拉
2、低压复位,复位电压可选,也可设中断
3、硬件复位,复位脚(第9脚)用做 IO口的勾去掉,接300欧电阻到地,成传统的复位脚
4、看门狗复位,要喂狗

任务1,编写看门狗程序
在main.c中输入如下语句:
在main.c中
        while (DeviceState != DEVSTATE_CONFIGURED);           //等待USB完成配置
        WDT_CONTR = 0X24;                                                 //启动看门狗,0.5秒
        while(1)
        {
                if (bUsbOutReady)                                               //如果接收到了数据
                {
                        //USB_SendData(UsbOutBuffer,OutNumber);  //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                        usb_OUT_done();                                        //
                }
                Task_Pro_Handler_Callback();                               //执行功能函数
                //if(P33)!=0)                                                       //用于测试
                WDT_CONTR = 0X34;                                          //喂狗
        }

在config.c中
void Delay10ms(void)        //@24.000MHz
{
        unsigned long edata i;

        _nop_();
        _nop_();
        _nop_();
        i = 59998UL;
        while (i) i--;
}

void USB_Reset_U(void)
{
        P3M0 = 0x00;
        P3M1 = 0x00;

        P3M0 &= ~0x03;
        P3M1 |= 0x03;

        USBCON = 0X00;
        USBCLK = 0X00;
        IRC48MCR = 0X00;
        Delay10ms();
}

void Sys_init(void)
{        
        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_Reset_U();
}

在config.h中
void Sys_init(void);                       //函数声明
void Timer1_Init(void);                  //1毫秒@24.000MHz
void USB_Reset_U(void);
void Timer10_Init(void);                //10毫秒@24.000MHz

二、软件复位
只有IAP_COUTR一个寄存控制器
当IAP_COUTR = 0x60;         //进入下载模式,可用P33按钮检测
当IAP_COUTR = 0x20;         //软件复位,从头运行用户程序

void KEY_Task(void)
{
        if( P33 == 0 )
        {
                Key_Vol++;
                if( Key_Vol==5 )
                {
                        //按键按下的任务
                        USB_Reset_U();
                        
                        IAP_CONTR = 0X60;                //0x60进入下载模式,0x20进入软件复位
                }
        }
        else
        {
                Key_Vol = 0;
        }        
}

课后思考:
密码锁
1.没有输入时,显示“- - - - - - - -”
2.有输入时,按下一个按键,开始按顺序写入
    例如,第一个按下1,显示“1 - - - - - - -”   
    例如,第二个按下3,显示“1 3 - - - - - -”
3.当按下的密码为“ 1 2 3 4 5 6 7 0”时,数码管显示“       1”的字符,否则,还是显示“- - - - - - - -”
新增:
1.看门狗,超时1秒自动复位
2.增加开机版本号,开机显示三秒的U 1.00 版本号
3.增加手动复位,P33按钮按下时重启(方便查看版本号和清除密码)

思考如下:
1.看门狗,超时1秒自动复位
在main.c中
        while (DeviceState != DEVSTATE_CONFIGURED);           //等待USB完成配置
        WDT_CONTR = 0X25;                                                 //启动看门狗,1秒 @24Mhz
        while(1)
        {
                if (bUsbOutReady)                                               //如果接收到了数据
                {
                        //USB_SendData(UsbOutBuffer,OutNumber);  //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
                        usb_OUT_done();                                        //
                }
                Task_Pro_Handler_Callback();                               //执行功能函数
                WDT_CONTR = 0X34;                                          //喂狗
        }

config.c和config.h与上面一样

2.增加开机版本号,开机显示三秒的U 1.00 版本号

在Task中增加一个3秒的任务
{0,    3000,   3000,    Task_3},

在io.c中
u 8 State3 = 0;                                                          //在Task3中State3=1;

void Seg_Task(void)
{
    if(State3==0)
        password[8] = {17,17,17,17, 1,18,0,0};                //1.00 版本号
    else
        password[8] = {16,16,16,16,16,16,16,16};           //--------
.....................................
}

3.增加手动复位,P33按钮按下时重启(方便查看版本号和清除密码)
void KEY_Task(void)
{
        if( P33 == 0 )
        {
                Key_Vol++;
                if( Key_Vol==5 )
                {
                        //按键按下的任务
                        USB_Reset_U();
                        
                        IAP_CONTR = 0X20;                //0x60进入下载模式,0x20进入软件复位
                }
        }
        else
        {
                Key_Vol = 0;
        }        
}


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-21 22:09:39 | 显示全部楼层
第十三集 中断系统 学习笔记

1、中断系统介绍
当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统。
● CPU 总是先响应优先级别最高的中断请求
● CPU 能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,
● 每一个中断源可以用软件独立地控制为开中断或关中断
● 部分中断的优先级别均可用软件设置。高优先级的中断请求可以打断低优先级的中断
当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求,CPU 暂停当前的工作,转而去处理这个紧急事件,处理完以后,再回到原来被中断的地方,继续原来的工作,这样的过程称为中断。实现这种功能的部件称为中断系统。
● CPU 总是先响应优先级别最高的中断请求
● CPU 能够暂停对原来中断源的服务程序,转而去处理优先级更高的中断请求源,处理完以后,再回到原低级中断服务程序,
● 每一个中断源可以用软件独立地控制为开中断或关中断
● 部分中断的优先级别均可用软件设置。高优先级的中断请求可以打断低优先级的中断

2、外部中断介绍
外部中断就是在单片机的一个引脚上(带INT的),由于外部因素导致了一个电平的变化(比如由高变低),而通过捕获这个变化,单片机内部自主运行的程序就会被暂时打断,转而去执行相应的中断处理程序,执行完后又回到原来中断的地方继续执行原来的程序
外部中断0:IT0,IE0,EX0,EA
外部中断1:IT1,IE1,EX1,EA
外部中断x:x=2-4,INTxIF,EXn,EA

3、外部中断用法

任务一、编写外部中断1的程序
在main.c中
        INT1_Init();                                //外部中断1初始化
        EA = 1;                                      //IE |= 0X80;

在io.c中
void INT1_Init(void)
{
        IT1 = 1;                        //下降沿中断
        EX1 = 1;                       //打开中断允许
        EA = 1;                        //打开总中断
}

void INT_ISR(void) interrupt 2
{
        P01 = !P01;
}

课后小练
雕刻机保护系统
1.按下矩阵键盘上的按钮1开始工作
2.工作时打开LED0,表示雕刻机电源已打开
3.当外部中断1端口导通时,立刻关闭LED0,
    表示切断雕刻机电源,从而实现保护功能。

与任务一同样的:
在main.c中
        INT1_Init();                                //外部中断1初始化
        EA = 1;                                      //IE |= 0X80;

在Task.c中
        {0, 10,   10,    KEY_Task},      /* task 1 Period: 10ms */

在io.c中
u8 Key_Vol = 0;

void KEY_Task(void)
{
        if( P32 == 0 )
        {
                Key_Vol++;
                if( Key_Vol==5 )
                {
                        //按键按下的任务
                        P01 = 1;                        //打开电源
                }
        }
        else
        {
                Key_Vol = 0;
        }        
}

void INT1_Init(void)
{
        IT1 = 1;                        //下降沿中断
        EX1 = 1;                       //打开中断允许
        EA = 1;                         //打开总中断
}

void INT_ISR(void) interrupt 2
{
        P01 = 0;                        //关闭电源
}

在io.h 中添加定义
void KEY_Task(void);
void INT1_Init(void);


回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-23 08:15:09 | 显示全部楼层
第十四集 IO中断(所有普通IO都支持的“外部中断”)

学习笔记

摘要
一、IO中断介绍

相比于外部中断,IO中断有以下优点:
1.支持所有的IO口(外部中断只有特定的外部中断的引脚)
2.可以任意配置上升沿/下降沿/高电平/低电平(外部中断只有上升/下降沿中断)
缺点:
IO中断同时只能支持一种中断模式,外部中断0和1可以同时支持上升/下降沿中断(可以用两个IO端口实现双边沿检测!)。

二、IO中断用法
1、选择合适的中断模式:PnIM0,PnIM1,下降沿,上升沿,低电平,高电平
2、打开端口的中断功能:PnINTE.x,0关1开,n 、x =(0~7),
3、配置IO口的中断:PnINTF.x,0无1有,标志位软件清0
4、中断号处理:装大于31的拓展工具或13号空中断跳转

任务1:编写IO中断的程序
在main.c中写入
P3_IO_Init();                //IO中断初始化
EA = 1;

在io.c中写入
void P3_IO_Init(void)
{
        P3IM0 = 0x00;                //IO中断模式为下降沿
        P3IM1 = 0x00;

        P3INTE = 0x08;        //打开中断
}

void P3_IO_ISR(void) interrupt 40
{
        u8 intf;
        intf = P3INTF;

        if( intf )                //判断有没有IO中断
        {
                P3INTF = 0;
                If( intf & 0x08)                //判断是否是P33按下
                {
                        P01 = !P01;
                }               
        }
}

在io.h中写入
//void INT1_Init(void);
void P3_IO_Init(void);

三、中断优先级的设置
相同优先级,靠前的中断源先执行,执行完之后再执行低中断源,且一个中断源在执行的时候不能被打断。
定时器0和P3中断都是最低优先级,定时器0中断号1,P3中断号40,执行完定时器0,再执行P3,再执行定定时器0,再执行...
PINIPL和PINIPH,00为最低级,11为最高级

任务2:编写P4端口的IO中断 打断 P3低电平中断的的程序(注意优先级)

开显示任务,
平时显示0
P33中断显示1,
P47中断显示2,
低平触发

在main.c中写入
P3_IO_Init();                //IO中断初始化
P4_IO_Init();
EA = 1;

while(1)
{
        ……..
        Task_Pro_Handler_Callback();                //执行功能函数
        password[0] = 0;
}

在io.c中写入
void P3_IO_Init(void)
{
        P3IM0 = 0x00;                //IO中断模式为低电平
        P3IM1 = 0xff;

        P3INTE = 0x08;                //打开中断
}

void P3_IO_ISR(void) interrupt 40
{
        u8 intf;
        intf = P3INTF;

        if( intf )                //判断有没有IO中断
        {
                P3INTF = 0;
                If( intf & 0x08)                //判断是否是P33按下
                {
                        password[0] = 1;
                }               
        }
}

void P4_IO_Init(void)
{
        P4IM0 = 0x00;                //IO中断模式为低电平
        P4IM1 = 0xff;

        P4INTE = 0x80;                //打开中断P4.7
}

void P4_IO_ISR(void) interrupt 41
{
        u8 intf;
        intf = P4INTF;

        if( intf )                //判断有没有IO中断
        {
                P4INTF = 0;
                If( intf & 0x80)                //判断是否是P47按下
                {
                        password[0] = 2;
                }               
        }
}

在io.h中写入
//void INT1_Init(void);
extern u8 key_num ;
extern u8 passward[8];
void P3_IO_Init(void);
void P4_IO_Init(void);

现在就可以看到P3中断的优先级是高于P4中断的优先级的

如果我们要反过来,让P4中断的优先级高于P3中断的优先级

在void P4_IO_Init(void)中增加
PINIPH |=(1<<4);        //与入最高优先级
PINIPL |=(1<<4);

在config.c的void Timer0_Init(void)中增加
        IPH |= (1<<1);
        IP  |= (1<<1);

这样P4 优先级就高于P3了

相同优先级:靠前的中断源先执行,执行完之后再执行低中断源,且一个中断源在执行的时候不能被打断。
定时器0和P3中断都是最低优先级,定时器0中断号1,P3中断号40,执行完定时器0再执行P3

课后小练
多路抢答器
1.上电后一位数码管显示0,表示没有按下
2.选择任意三个独立按键作为三个用户,分别
   代表“用户1”,“用户2”,“用户3”
3.谁第一个按下按钮,数码管就显示数字几
   代表抢答成功

在main.c中写入
P3_IO_Init();                //IO中断初始化
EA = 1;

while(1)
{
        ……..
        Task_Pro_Handler_Callback();                //执行功能函数
        password[0] = 0;
}

在io.c中写入
void P3_IO_Init(void)
{
        P3IM0 = 0x00;                //IO中断模式为低电平
        P3IM1 = 0xff;

        P3INTE = 0x08;                //打开中断
}

void P3_IO_ISR(void) interrupt 40
{
        u8 intf;

        intf = P3INTF;

        if( intf )                //判断有没有IO中断
        {
                //P3INTF = 0;
                if( intf & 0x02)                //判断是否是P31按下
                {
                        password[0] = 1;
                }               
                else if( intf & 0x04)                //判断是否是P32按下
                {
                        password[0] = 2;
                }
                else if( intf & 0x08)                //判断是否是P33按下
                {
                        password[0] = 3;
                }
        }
}
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:39
  • 最近打卡:2025-10-15 18:27:22
已绑定手机

1

主题

26

回帖

255

积分

中级会员

积分
255
发表于 2025-9-26 08:26:15 | 显示全部楼层
第十五集 定时器做计数器 学习笔记

一. 计数器的作用
只要输出信号是高低电平变化的传感器,想要计算个数的就可以用计数器的功能。

二. 定时器做为计数器的用法


任务1:编写定时器1计数的的程序
(为了方便计数,10个脉冲中断一次)
新建两个文件,tim.c和tim.h
在main.c中写入
#include “tim.h”

TIM1_Count_Init();                //在EA = 1之前

在tim.c中写入
#include “tim.h”
u32 Count_T1 = 0;

void TIM1_Count_Init(void)
{
        //TMOD  搜索,找到相关定义
        T1_CT = 1;                //设置外部计数
        T1_M1 = 0;                //设置为16位自动重载
        T1_M0 = 0;
        T0_GATE = 0;

        TH1 = (65536-Count_num)>>8        //65526
        TL1 = (65536-Count_num);

        P3PU |= 0x20;                //上拉电阻
        TR1 = 1;
        ET1 = 1;
}

void Timer1_Isr(void) interrupt 3                //1MS执行一次
{
        Count_T1 ++;                //T1引脚测到10个脉冲就溢出1次
                                        //不要过于频繁地中断,影响稳定性
}

//因P35 有冲突,故用ISP里的万能虚拟数码管显示
void T1_RunTask(void)
{
        u32 count_th_tl = 0;
        count_th_tl = ((u16)TH1<<8 + (u16)TL1);
        count_th_tl -= 65526;
        SEG7_ShowLong(Count_T1 * Count_num + count_ th_tl, 10);
}

在tim.h中写入
#ifndef __TIM_H
#define __TIM_H
#include “config.h”

#define Count_num 10        //累计脉冲单位

void TIM1_Count_Init(void);
void T1_RunTask(void);

#endif

在task.c中写入
#include “tim.h”

增加一个任务
(0,100,100,T1_RunTask),//定时器1的计数功能

时钟选24M,下载,三个勾选,USB不停电下载
打开仿真调试接口,显示按键次数

至此,任务1就完成了。

三.定时器1测量INT1引脚低电平脉冲宽度

任务2:编写INT1测量低电平时间
(由按键模拟信号,100us的计数周期计数!)
ISP定时器计算工具,24MHz,100微秒,使能中断
在main.c中写入
#include “tim.h”

Timer1_Init();                //在EA = 1之前

在tim.c中
u32 Count_T1 = 0;
void Timer1_Isr(void) interrupt 3
{
        static u32 count_p33 = 0;
        
        if( P33 == 0 )                //按键按下开始计数
        {
                count_p33 ++ ;
        }
        else
        {
                if( count_p33>0 )                //表示之前按下了这个按键
                {
                        Count_T1 = count_p33;
                }
                count_p33 = 0;
        }
}

void Timer1_Init(void)                //100微秒@24.000MHz
{
        AUXR &= 0xBF;                        //定时器时钟12T模式
        TMOD &= 0x0F;                        //设置定时器模式
        TL1 = 0x38;                                //设置定时初始值
        TH1 = 0xFF;                                //设置定时初始值
        TF1 = 0;                                //清除TF1标志
        TR1 = 1;                                //定时器1开始计时
        ET1 = 1;                                //使能定时器1中断
}

void T1_RunTask(void)
{
        //SEG7_ShowLong(Count_T1, 10);                             //显示整数
        SEG7_ShowString("%07.01f",((float)Count_T1)/10);   //显示1位小数
}

在tim.h中
#define Count_num 10                //累计脉冲单位

//void TIM1_Count_Init(void);
void Timer1_Init(void);                //100微秒@24.000MHz
        
void T1_RunTask(void);

在task.c中写入
#include “tim.h”

有一个任务
(0,100,100,T1_RunTask),//定时器1的计数功能
至此,任务2就完成了。

课后小练
CT计数器:
在设备的出料端口有一个感应器,每次有成品出来就会有一个低电平出来,计算相邻的两个产品出来的时间差来计算CT时间。
1.计算P33引脚的相邻两次按下的时间,精确到100ms(即单个时间)
2.计算按下P33的次数(即总产量)
3.数码管前四位显示单个时间 ,后四位显示次数

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-10-16 05:38 , Processed in 0.135332 second(s), 91 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表