找回密码
 立即注册
查看: 1343|回复: 35

Ai8051U 实验箱 学习开始打卡

[复制链接]
  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-12 23:25:23 | 显示全部楼层 |阅读模式

感谢官方提供的Ai8051U擎天柱开发板和实验箱,到手后立马迫不及待的烧录学习了

1000001595.jpg

学习了冲哥的《8051U深度入门到32位51大型实战教学视频》,收获颇多,对于我这种小白来说简直是手把手教学。

第三集 点亮第一颗LED

已成功烧录代码点亮,代码比较简单,容易理解

#include "ai8051u.h"		//调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可
void main(void)
{
	P2M0 = 0;		//P2端口(P20-P27)为准双向口
	P2M1 = 0;

	while(1)
	{
		P20 = 0;	//P20端口输出0V
		P21 = 0;	//P21端口输出0V
	}
}

第四集 USB不停电下载

实现了开发板不停电下载,免去了反复按电源键和p3.2按键的麻烦,非常实用。

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,本节课程的其余内容(USB不停电下载)均通用!

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

void main(void)
{
    P_SW2 |= 0x80;		//B7位写1,使能访问XFR

    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_init();                                     //USB CDC 接口配置

    IE2 |= 0x80;                                    //使能USB中断
    EA = 1;											//IE |= 0X80;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{

        if (bUsbOutReady)
        {
            USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
            usb_OUT_done();
        }

		P20 = 0;	                                //P00端口输出0V
		P22 = 0;	                                //P02端口输出0V

	}
}

第五集 C语言基础

使用print语句,可以随时查看程序运行情况,方便调试及与单片机交互

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,本节课程的其余内容(C语言测试)均通用!

#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)

u8 X = 200;
u8 Y = 10;

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

void main(void)
{
    P_SW2 |= 0x80;		//B7位写1,使能访问XFR

    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_init();                                     //USB CDC 接口配置

    IE2 |= 0x80;                                    //使能USB中断
    EA = 1;											//IE |= 0X80;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{

        if (bUsbOutReady)							//如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
			if( X && Y )	//如果条件为真,输出什么
			{
				printf("条件为真\r\n");
			}
//			else
//			{
//				printf("条件为假\r\n");
//			}

//			printf("X / Y = %u \r\n",(u16)(X/Y));

//			printf("X %% Y = %u \r\n",(u16)(X%Y));

            usb_OUT_done();							//
        }


	}
}

第六集 I/O输入输出

冲哥用了3个小任务,以实例的形式,介绍了通过检测按键,控制led的亮和灭

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!

#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)

u8 state = 0;					//初始状态

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
	unsigned long edata i;

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


void main(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_init();                                     //USB CDC 接口配置

    IE2 |= 0x80;                                    //使能USB中断
    EA = 1;											//IE |= 0X80;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{

        if (bUsbOutReady)							//如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
            usb_OUT_done();							//
        }

//		//任务1:按下P32按钮灯亮,松开P32按钮灯灭;
//		if( P32 == 0 )								//判断P32按钮是否按下
//		{
//			P20 = 0;
//		}
//		else
//		{
//			P20 = 1;
//		}
//

		//任务2:按下P32按钮灯灭,松开P32按钮灯亮;
//		if( P32 == 1 )								//判断P32按钮是否按下
//		{
//			P20 = 0;
//		}
//		else
//		{
//			P20 = 1;
//		}

		//任务3:按一下灯亮,按一下灯灭
		if( P32 == 0 )								//判断P32按钮是否按下
		{
			Delay20ms();							//延时20ms消抖
			if( P32 == 0 )
			{
				state = !state;						//变量取反 0 1 0 1 0 1
				P20 = state;
				printf("state:%d\r\n",(int)state);

				while( P32 == 0 );					//等待P32松开

			}
		}


	}
}

第七集 定时器中断

也是通过3个小任务,介绍了定时器的使用。

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!

#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)

u8 state = 0;					//初始状态
u8 Run_State = 0;				//运行状态

char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 119998UL;
	while (i) i--;
}
void Timer0_Init(void);		//3秒@24.000MHz		//函数声明

void main(void)
{
	int count=1;									//按键计数变量

    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_init();                                     //USB CDC 接口配置

    IE2 |= 0x80;                                    //使能USB中断
//	Timer0_Init();									//定时器初始化

    EA = 1;											//IE |= 0X80;

	P40 = 0;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{

        if (bUsbOutReady)							//如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
            usb_OUT_done();							//
        }

		//任务1:
//		if( P32 == 0 )								//判断P32按钮是否按下
//		{
//			Delay20ms();							//延时20ms消抖
//			if( P32 == 0 )
//			{
//				printf("按键按下次数\xfd:%d 次\r\n",(int)count);
//				count++;
//
//				while( P32 == 0 );					//等待P32松开
//
//			}
//		}

//		//任务2:灯按一下点亮三秒后熄灭。
		if( P32 == 0 )								//判断P32按钮是否按下
		{
			Delay20ms();							//延时20ms消抖
			if( P32 == 0 )
			{
//				printf("按键按下次数\xfd:%d 次\r\n",(int)count);
//				count++;
				P20 = 0;
				Timer0_Init();
				while( P32 == 0 );					//等待P32松开

			}
		}

//
		//任务3:救护车灯控制器,按下报警按钮,红蓝交替闪烁(LED1和LED2    	  表示红和蓝灯),再按一下报警按钮,红蓝灯停止。
		if( P32 == 0 )								//判断P32按钮是否按下
		{
			Delay20ms();							//延时20ms消抖
			if( P32 == 0 )
			{
				Run_State = !Run_State;				//运行状态取反

				if( Run_State==1 )					//运行
				{
					Timer0_Init();
				}
				else
				{
					TR0 = 0;						//关闭定时器
					P20 = 1;
					P21 = 1;
				}
				while( P32 == 0 );					//等待P32松开

			}
		}

	}
}

//void Timer0_Init(void)		//3秒@24.000MHz	函数定义
//{
//	TM0PS = 0x5B;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
//	AUXR &= 0x7F;			//定时器时钟12T模式
//	TMOD &= 0xF0;			//设置定时器模式
//	TL0 = 0x3F;				//设置定时初始值
//	TH0 = 0x01;				//设置定时初始值
//	TF0 = 0;				//清除TF0标志
//	TR0 = 1;				//定时器0开始计时
//	ET0 = 1;				//使能定时器0中断
//
//	//TM0PS = 91
//	//12T 		/12
//	// THO-TL0 = 319
//
//}

void Timer0_Init(void)		//500毫秒@24.000MHz
{
	TM0PS = 0x0F;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0xDC;				//设置定时初始值
	TH0 = 0x0B;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
}

void Timer0_Isr(void) interrupt 1		//3秒执行一次
{
	state = !state;

	P20 = state;
	P21 = !state;
}

在此基础上,我想实现10秒的倒计时,修改了一下代码,顺利实现:

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件

//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!

#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)

u8 state = 0;					//初始状态
u8 Run_State = 0;				//运行状态
u8 numb=10;                //10秒计数  


char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";

void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
	unsigned long edata i;

	_nop_();
	_nop_();
	i = 119998UL;
	while (i) i--;
}
void Timer0_Init(void);		//3秒@24.000MHz		//函数声明

void main(void)
{
	int count=1;									//按键计数变量

    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_init();                                     //USB CDC 接口配置

    IE2 |= 0x80;                                    //使能USB中断
//	Timer0_Init();									//定时器初始化

    EA = 1;											//IE |= 0X80;

	P40 = 0;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{

        if (bUsbOutReady)							//如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  
            usb_OUT_done();							//
        }



		//任务2-1:灯按一下打印10秒倒计时。
		if( P32 == 0 )								//判断P32按钮是否按下
		{
			Delay20ms();							//延时20ms消抖
			if( P32 == 0 )
			{
				Timer0_Init();

				while( P32 == 0 );					//等待P32松开

			}
		}


	}
}




void Timer0_Isr(void) interrupt 1
{
	numb--;       //每一秒计数减1;
	printf("10秒倒计时:%d \r\n",(u8)numb);    //每一秒打印输出
	if(numb==0){          //当倒计时结束,关闭定时器
	TR0 = 0;
	}
}

void Timer0_Init(void)		//1秒@24.000MHz
{
	TM0PS = 0x1E;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
	AUXR &= 0x7F;			//定时器时钟12T模式
	TMOD &= 0xF0;			//设置定时器模式
	TL0 = 0xFC;				//设置定时初始值
	TH0 = 0x03;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;				//使能定时器0中断
}


image.png

目前就学到这里,后期继续学习。

1 喜欢他/她就送朵鲜花吧,赠人玫瑰,手有余香!
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:237
  • 最近打卡:2025-05-05 08:07:13

61

主题

818

回帖

1506

积分

金牌会员

积分
1506
发表于 2024-12-13 08:50:54 | 显示全部楼层

加油...

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 14:36:35 | 显示全部楼层

第八集 定时器周期性调度任务

任务1: p32按下,led1循环300ms闪烁,led2循环600ms闪烁,led3循环900ms闪烁

1.定义了三个计时变量 u16 count_ms[3] = {0,0,0};

2.在定时器void Timer0_Init(void) //1毫秒@24.000MHz

中断执行函数中,判断计时变量是否到设定时间

for(i=0;i<3;i++){
count_ms[i]++;
if(count_ms[0]>=300){
count_ms[0]=0;
	P20 = !P20;
}
if(count_ms[1]>=600){
count_ms[1]=0;

	P21 = !P21; 
}
if(count_ms[2]>=900){
count_ms[2]=0;

	P22 = !P22;
}
}
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 15:25:16 | 显示全部楼层

任务2:p2流水灯

1.首先定义一个p2口8个led状态的数组:

u8 state[8]={0xFE,  // 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,  // 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,  // 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F};

2.和任务1不同的是,任务1是按下p32按键的时候才启动定时器中断,所以任务1的定时器初始化在if(){XXXX;

Timer0_Init();

}中,此处直接在开机就执行流水灯,所以Timer0_Init();在main函数刚开始


#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)u16 count_ms[3] = {0,0,0};					//三个计时变量
u8 numb=0;u8 state[8]={0xFE,  // 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,  // 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,  // 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F};
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
unsigned long edata i;_nop_();
_nop_();
i = 119998UL;
while (i) i--;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}
void Timer0_Init(void);		//3秒@24.000MHz		//函数声明void main(void)
{
int count=1;									//按键计数变量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_init();                                     //USB CDC 接口配置

IE2 |= 0x80;                                    //使能USB中断
  Timer0_Init();									//定时器初始化

EA = 1;											//IE |= 0X80;



while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  
        usb_OUT_done();							//
    }


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_init();                                     //USB CDC 接口配置

IE2 |= 0x80;                                    //使能USB中断
  Timer0_Init();									//定时器初始化

EA = 1;											//IE |= 0X80;



while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
  
        usb_OUT_done();							//
    }


//         任务2:p2流水灯
if(count_ms[0]>=300){
count_ms[0]=0;			P2=state[numb];
				numb++;

				if(numb>=8){
				numb=0;
				}
			}
}
			P2=state[numb];
				numb++;

				if(numb>=8){
				numb=0;
				}
			}
}
}void Timer0_Isr(void) interrupt 1
{
u8 i;
for(i=0;i<3;i++){
count_ms[i]++;}

}
}

}
void Timer0_Init(void)		//1毫秒@24.000MHz
{
TM0PS = 0x00;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F;			//定时器时钟12T模式
TMOD &= 0xF0;			//设置定时器模式
TL0 = 0x30;				//设置定时初始值
TH0 = 0xF8;				//设置定时初始值
TF0 = 0;				//清除TF0标志
TR0 = 1;				//定时器0开始计时
ET0 = 1;				//使能定时器0中断
}
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 15:35:05 | 显示全部楼层

在流水灯的基础上,将led状态的数组数据再反过来,就是先led流水灯左右左右的流动

u8 state[14]={0xFE,  // 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,  // 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,  // 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F,0xBF,0xDF, 0xEF,0xF7,0xFB,0xFD};
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 16:56:59 | 显示全部楼层

任务3:按下p32按钮,led依次右移一位

这里学习了不使用while(P32==0)这个语句,这样会造成按键按下期间其他任务不执行的情况;

而是改为定时器每10ms执行一次判断p32是否按下,如果按下,则把按键技术变量key_vol加1,当加到5时,即50ms按键都按下,则为按键按下,否则key_vol=0;

videoframe_2561396.png

这样就实现了不同任务互不影响。

#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)u16 count_ms[3] = {0,0,0};					//三个计时变量
u8 numb=0;
u16 key_vol=0;
u8 state[8]={0xFE,  // 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,  // 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,  // 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F};
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
unsigned long edata i;_nop_();
_nop_();
i = 119998UL;
while (i) i--;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
​}
void Timer0_Init(void);		//3秒@24.000MHz		//函数声明void main(void)
{
int count=1;									//按键计数变量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_init();                                     //USB CDC 接口配置

IE2 |= 0x80;                                    //使能USB中断
  Timer0_Init();									//定时器初始化

EA = 1;											//IE |= 0X80;



while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
        usb_OUT_done();							//
    }


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_init();                                     //USB CDC 接口配置

IE2 |= 0x80;                                    //使能USB中断
  Timer0_Init();									//定时器初始化

EA = 1;											//IE |= 0X80;



while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
        usb_OUT_done();							//
    }


​//         任务3:按下p32按钮,led依次右移一位			if(count_ms[0]>=1000){                     //第一个任务每一秒打印一个hellow world
			count_ms[0]=0;
			printf("当前第%d个led点亮\r\n",(u16)numb);
			}
	
	
			P2=state[numb]; 

			if(count_ms[2]>=10){                  //第二个任务按下p32按钮,led依次右移一位
			count_ms[2]=0;
				if(P32==0){   
				key_vol ++;
				if(key_vol==5){
				numb ++;
					if(numb>=8){
					numb=0;
					}
					}
				}
				else{
				key_vol=0;
				}
				}
		
			}


	
}
			if(count_ms[0]>=1000){                     //第一个任务每一秒打印一个hellow world
			count_ms[0]=0;
			printf("当前第%d个led点亮\r\n",(u16)numb);
			}
	
	
			P2=state[numb]; 

			if(count_ms[2]>=10){                  //第二个任务按下p32按钮,led依次右移一位
			count_ms[2]=0;
				if(P32==0){   
				key_vol ++;
				if(key_vol==5){
				numb ++;
					if(numb>=8){
					numb=0;
					}
					}
				}
				else{
				key_vol=0;
				}
				}
		
			}


	
}
​void Timer0_Isr(void) interrupt 1
{
u8 i;
for(i=0;i<3;i++){
count_ms[i]++;}

}
}

}
​void Timer0_Init(void)		//1毫秒@24.000MHz
{
TM0PS = 0x00;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F;			//定时器时钟12T模式
TMOD &= 0xF0;			//设置定时器模式
TL0 = 0x30;				//设置定时初始值
TH0 = 0xF8;				//设置定时初始值
TF0 = 0;				//清除TF0标志
TR0 = 1;				//定时器0开始计时
ET0 = 1;				//使能定时器0中断
}

这里补充一下,冲哥教程里没有

if(numb>=8){
numb=0;

}这一段,会导致超过8之后会出现莫名其妙的问题,这个地方需要给numb重置一下。

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 17:21:42 | 显示全部楼层

文件的创建

这一节介绍了利用.c和.h文件,简化main.c中的代码,使代码看上去更加整洁。

.c文件用来存放函数,并且函数需要在.h文件中声明。.h文件用来放定义的变量,函数声明

config.c文件

#include "config.h"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;
}
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;
}
​void Timer0_Init(void)		//1毫秒@24.000MHz
{
TM0PS = 0x00;			//设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F;			//定时器时钟12T模式
TMOD &= 0xF0;			//设置定时器模式
TL0 = 0x30;				//设置定时初始值
TH0 = 0xF8;				//设置定时初始值
TF0 = 0;				//清除TF0标志
TR0 = 1;				//定时器0开始计时
ET0 = 1;				//使能定时器0中断
}void Delay20ms(void)	//@24.000MHz  Delay20ms();
{
unsigned long edata i;_nop_();
_nop_();
i = 119998UL;
while (i) i--;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
​}

config.h文件

#ifndef __CONFIG_H
#define __CONFIG_H#include "ai8051u.h"			//调用头文件
#include "stc32_stc8_usb.h"		//调用头文件
#include "intrins.h"			//d调用头文件#define u8  unsigned char		//8位无符号变量(0-255)
#define u16 unsigned int		//16位无符号变量(0-65535)void sys_init(void);         //函数声明void Timer0_Init(void);void Delay20ms(void);#endif

main.c

#include "config.h"u16 count_ms[3] = {0,0,0};					//三个计时变量
u8 numb=0;
u16 key_vol=0;
u8 state[8]={0xFE,  // 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,  // 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,  // 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F};
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";void main(void)
{sys_init(); 
usb_init();                                     //USB CDC 接口配置
sys_init(); 
usb_init();                                     //USB CDC 接口配置
​IE2 |= 0x80;                                    //使能USB中断
Timer0_Init();									//定时器初始化EA = 1;											//IE |= 0X80;while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
        usb_OUT_done();							//
    }


while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

while(1)
{

    if (bUsbOutReady)							//如果接收到了数据
    {
        //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
    
        usb_OUT_done();							//
    }


​//         任务3:按下p32按钮,led依次右移一位			if(count_ms[0]>=1000){                     //第一个任务每一秒打印一个hellow world
			count_ms[0]=0;
			printf("当前第%d个led点亮\r\n",(u16)numb);
			}
	
	
			P2=state[numb]; 

			if(count_ms[2]>=10){                  //第二个任务按下p32按钮,led依次右移一位
			count_ms[2]=0;
				if(P32==0){   
				key_vol ++;
				if(key_vol==5){
				numb ++;
					if(numb>=8){
					numb=0;
					}
					}
				}
				else{
				key_vol=0;
				}
				}
		
			}


	
}
			if(count_ms[0]>=1000){                     //第一个任务每一秒打印一个hellow world
			count_ms[0]=0;
			printf("当前第%d个led点亮\r\n",(u16)numb);
			}
	
	
			P2=state[numb]; 

			if(count_ms[2]>=10){                  //第二个任务按下p32按钮,led依次右移一位
			count_ms[2]=0;
				if(P32==0){   
				key_vol ++;
				if(key_vol==5){
				numb ++;
					if(numb>=8){
					numb=0;
					}
					}
				}
				else{
				key_vol=0;
				}
				}
		
			}


	
}
​void Timer0_Isr(void) interrupt 1
{
u8 i;
for(i=0;i<3;i++){
count_ms[i]++;}

}
}

}
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-13 17:23:56 | 显示全部楼层

结构体数组的周期性调度

本节知识点还是挺多的,而且不是那么容易理解。

1.首先创建了task.c和task.h的文件

在task.c文件中,定义一个结构体:




static TASK_COMPONENTS Task_Comps[]=
{
   //状态  计数  周期  函数
    {0, 300,   300,   Led0_Blink},  /* 300ms执行一次 */
		{0, 600,   600,   Led1_Blink},  /* 600ms执行一次 */
		{0, 900,   900,   Led2_Blink},  /* 900ms执行一次 */
		{0, 10,   10,   key_task},  /* 10ms执行一次 */

	/* Add new task here */
};

任务标记回调函数:void Task_Marks_Handler_Callback(void),此函数的作用是对结构体中的每一组元素根据计数值进行倒数,当倒数到0时,计数重置为计数周期,并且第一位run置1,从而调用相应的任务执行函数。

void Task_Marks_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].TIMCount)      /* If the time is not 0 */
        {
            Task_Comps[i].TIMCount--;   /* Time counter decrement */
            if(Task_Comps[i].TIMCount == 0) /* If time arrives */
            {
                /*Resume the timer value and try again */
                Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
                Task_Comps[i].Run = 1;      /* The task can be run */
            }
        }
    }
}

任务处理回调函数:void Task_Marks_Handler_Callback(void),此函数的作用是当run置一后,执行相应的函数。

void Task_Pro_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].Run) /* If task can be run */
        {
            Task_Comps[i].Run = 0;      /* Flag clear 0 */
            Task_Comps[i].TaskHook();   /* Run task */
        }
    }
}

完整的task.c

#include "task.h"
#include "io.h"

 

static TASK_COMPONENTS Task_Comps[]=
{
   //状态  计数  周期  函数
    {0, 300,   300,   Led0_Blink},  /* 300ms执行一次 */
		{0, 600,   600,   Led1_Blink},  /* 600ms执行一次 */
		{0, 900,   900,   Led2_Blink},  /* 900ms执行一次 */
		{0, 10,   10,   key_task},  /* 10ms执行一次 */

	/* Add new task here */
};



u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]);



//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
void Task_Marks_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].TIMCount)      /* If the time is not 0 */
        {
            Task_Comps[i].TIMCount--;   /* Time counter decrement */
            if(Task_Comps[i].TIMCount == 0) /* If time arrives */
            {
                /*Resume the timer value and try again */
                Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
                Task_Comps[i].Run = 1;      /* The task can be run */
            }
        }
    }
}

//========================================================================
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
void Task_Pro_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].Run) /* If task can be run */
        {
            Task_Comps[i].Run = 0;      /* Flag clear 0 */
            Task_Comps[i].TaskHook();   /* Run task */
        }
    }
}


在task.h文件中,有结构体声明及回调函数的声明

#ifndef __TASK_H
#define __TASK_H


#include "config.h"

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


void Task_Marks_Handler_Callback(void);
void Task_Pro_Handler_Callback(void);
#endif

那之前p00每300ms闪烁一次,p01每600ms闪烁一次,p02每900ms闪烁一次为例

增加了io.c和io.h文件,将io口的配置转移到这里。

io.c文件

#include "io.h"


u16 key_vol=0;
void Led0_Blink(void){
  
	P00=!P00;
}

void Led1_Blink(void){
P01=!P01;
}

void Led2_Blink(void){
P02=!P02;
}

void key_task(void){
 if(P32==0){
   key_vol++;
	 if(key_vol==5){
	 //按键按下的任务
		 printf("按键单机\r\n");
	 }
	 }
 else{
 key_vol=0;
	 
 }
}

io.h文件

#ifndef __IO_H
#define __IO_H


#include "config.h"

void Led0_Blink(void);
void Led1_Blink(void);
void Led2_Blink(void);
void key_task(void);

#endif

main.c


#include "config.h"
#include "task.h"



char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";



void main(void)
{

	sys_init(); 
	usb_init();                                     //USB CDC 接口配置

  IE2 |= 0x80;                                    //使能USB中断
	Timer0_Init();									//定时器初始化

  EA = 1;											//IE |= 0X80;

	P40=0;

	while (DeviceState != DEVSTATE_CONFIGURED);     //等待USB完成配置

	while(1)
	{
	
        if (bUsbOutReady)							//如果接收到了数据
        {
            //USB_SendData(UsbOutBuffer,OutNumber);   //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
          
            usb_OUT_done();							//
        }
	
		Task_Pro_Handler_Callback();        //执行函数


				
				}
	
		
			
	}




void Timer0_Isr(void) interrupt 1
{
	Task_Marks_Handler_Callback();      //系统计时

	}



回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-14 15:36:17 | 显示全部楼层

第九集 数码管

本节使用两个hc595控制8位8段数码管

1.首先,在io.c中创建两个数组,分别存放数码管的段码和位码:

u8 SEG_NUM[]=                             /////数码管位码0-9,A-F,
{
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
};

其中,数码管的位码可以用isp软件-工具-字库生成工具 中找到数码管,直接就可以生成

然后是移位寄存器595端口初始化函数:void init_595(void),先把数据写到引脚上void Send_595(u8 dat),数码管段码输出,数码管位码输出void Display_Seg(u8 HC595_1,HC595_2)

需要显示的数据用void Seg_Task(void)显示。在task.c中,用结构体显示,

任务2:8位数码管显示12345678

u8 Seg_no=0;
void Seg_Task(void){


	Display_Seg( SEG_NUMB[Seg_no+1], ~T_NUM[Seg_no] );       //数码管刷新断码和位码
	Seg_no++;
	if(Seg_no>7){
	Seg_no=0;
	}

task.c中每1ms执行一次Seg_Task

static TASK_COMPONENTS Task_Comps[]=
{
{0, 1,   1,   Seg_Task},  / 1ms执行一次 /
};

这里容易错的地方是,容易在Seg_Task函数中使用for循环来依次显示1-8位数码管数值,这里不需要再用到for循环,因为在{0, 1, 1, Seg_Task},中就是每1ms执行一次循环

回复 支持 反对

使用道具 举报 送花

  • 打卡等级:常住居民II
  • 打卡总天数:85
  • 最近打卡:2025-03-06 07:45:38

7

主题

49

回帖

534

积分

高级会员

积分
534
发表于 2024-12-14 15:59:20 | 显示全部楼层

数码管任务3:动态显示时分秒00-00-00

用到两个函数,Seg_Task用来显示数码管,时间间隔位1ms,CountUp用来每一秒将miao加一,时间间隔为1秒,都是通过任务调度实现


u8 Seg_no=0;
u8 shi=0;
u8 fen =0;
u8 miao=0;

void Seg_Task(void){


	switch(Seg_no){
		case 0: Display_Seg( SEG_NUMB[shi/10], ~T_NUM[0] );   break;//小时十位
		case 1: Display_Seg( SEG_NUMB[shi%10], ~T_NUM[1] );   break;
		case 2: Display_Seg( SEG_NUMB[16], ~T_NUM[2] );   break;
		case 3: Display_Seg( SEG_NUMB[fen/10], ~T_NUM[3] );   break;
		case 4: Display_Seg( SEG_NUMB[fen%10], ~T_NUM[4] );   break;
		case 5: Display_Seg( SEG_NUMB[16], ~T_NUM[5] );   break;
		case 6: Display_Seg( SEG_NUMB[miao/10], ~T_NUM[6] );   break;
		case 7: Display_Seg( SEG_NUMB[miao%10], ~T_NUM[7] );   break;
	}
   
	Seg_no++;
	if(Seg_no>7){
	Seg_no=0;
	}

	}


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


}



冲哥用的if esle if语句,我这里用了switch case,实现效果一样

微信图片_20241214160144.jpg

回复 支持 反对

使用道具 举报 送花

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

本版积分规则

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

GMT+8, 2025-5-5 14:49 , Processed in 0.132312 second(s), 116 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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