Ai8051U 实验箱 学习开始打卡
<p><strong>感谢官方提供的Ai8051U擎天柱开发板和实验箱,到手后立马迫不及待的烧录学习了</strong></p><p><img src="data/attachment/forum/202412/14/103244olsesteobbe6l76w.jpg" alt="1000001595.jpg" title="1000001595.jpg" /></p>
<p><strong>学习了冲哥的《8051U深度入门到32位51大型实战教学视频》,收获颇多,对于我这种小白来说简直是手把手教学。</strong></p>
<h2>第三集 点亮第一颗LED</h2>
<h3>已成功烧录代码点亮,代码比较简单,容易理解</h3>
<pre><code>#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
}
}
</code></pre>
<h2>第四集 USB不停电下载</h2>
<p>实现了开发板不停电下载,免去了反复按电源键和p3.2按键的麻烦,非常实用。</p>
<pre><code>#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
}
}
</code></pre>
<h2>第五集 C语言基础</h2>
<p>使用print语句,可以随时查看程序运行情况,方便调试及与单片机交互</p>
<pre><code>#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,本节课程的其余内容(C语言测试)均通用!
#define u8unsigned 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(); //
}
}
}
</code></pre>
<h2>第六集 I/O输入输出</h2>
<p>冲哥用了3个小任务,以实例的形式,介绍了通过检测按键,控制led的亮和灭</p>
<pre><code>#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件
//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!
#define u8unsigned 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.000MHzDelay20ms();
{
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松开
}
}
}
}
</code></pre>
<h2>第七集 定时器中断</h2>
<p>也是通过3个小任务,介绍了定时器的使用。</p>
<pre><code>#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件
//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!
#define u8unsigned 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.000MHzDelay20ms();
{
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;
}
</code></pre>
<p>在此基础上,我想实现10秒的倒计时,修改了一下代码,顺利实现:</p>
<pre><code>#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件
//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!
#define u8unsigned 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.000MHzDelay20ms();
{
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中断
}
</code></pre>
<p><img src="data/attachment/forum/202412/12/232517mtgt5m9hb9ih5k3l.png" alt="image.png" title="image.png" /></p>
<p>目前就学到这里,后期继续学习。</p>
<p>加油...</p>
<h2>第八集 定时器周期性调度任务</h2>
<h3>任务1: p32按下,led1循环300ms闪烁,led2循环600ms闪烁,led3循环900ms闪烁</h3>
<p>1.定义了三个计时变量 u16 count_ms = {0,0,0};</p>
<p>2.在定时器void Timer0_Init(void) //1毫秒@24.000MHz</p>
<p>中断执行函数中,判断计时变量是否到设定时间</p>
<pre><code>for(i=0;i<3;i++){
count_ms++;
if(count_ms>=300){
count_ms=0;
P20 = !P20;
}
if(count_ms>=600){
count_ms=0;
P21 = !P21;
}
if(count_ms>=900){
count_ms=0;
P22 = !P22;
}
}
</code></pre>
<h3>任务2:p2流水灯</h3>
<p>1.首先定义一个p2口8个led状态的数组:</p>
<pre><code>u8 state={0xFE,// 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,// 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,// 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F};
</code></pre>
<p>2.和任务1不同的是,任务1是按下p32按键的时候才启动定时器中断,所以任务1的定时器初始化在if(){XXXX;</p>
<p>Timer0_Init();</p>
<p>}中,此处直接在开机就执行流水灯,所以Timer0_Init();在main函数刚开始</p>
<pre><code>
#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!#define u8unsigned char //8位无符号变量(0-255)
#define u16 unsigned int //16位无符号变量(0-65535)u16 count_ms = {0,0,0}; //三个计时变量
u8 numb=0;u8 state={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.000MHzDelay20ms();
{
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>=300){
count_ms=0; P2=state;
numb++;
if(numb>=8){
numb=0;
}
}
}
P2=state;
numb++;
if(numb>=8){
numb=0;
}
}
}
}void Timer0_Isr(void) interrupt 1
{
u8 i;
for(i=0;i<3;i++){
count_ms++;}
}
}
}
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中断
}
</code></pre>
<h3>在流水灯的基础上,将led状态的数组数据再反过来,就是先led流水灯左右左右的流动</h3>
<pre><code>u8 state={0xFE,// 1111 1110 ,最低位(P20控制的LED)点亮,其余熄灭
0xFD,// 1111 1101 ,次低位(P21控制的LED)点亮,其余熄灭
0xFB,// 1111 1011 ,依次类推
0xF7,
0xEF,
0xDF,
0xBF,
0x7F,0xBF,0xDF, 0xEF,0xF7,0xFB,0xFD};
</code></pre>
<h3>任务3:按下p32按钮,led依次右移一位</h3>
<p>这里学习了不使用while(P32==0)这个语句,这样会造成按键按下期间其他任务不执行的情况;</p>
<p>而是改为定时器每10ms执行一次判断p32是否按下,如果按下,则把按键技术变量key_vol加1,当加到5时,即50ms按键都按下,则为按键按下,否则key_vol=0;</p>
<p><img src="data/attachment/forum/202412/13/165343i8x8dg6f0v2glng3.png" alt="videoframe_2561396.png" title="videoframe_2561396.png" /></p>
<p>这样就实现了不同任务互不影响。</p>
<pre><code>#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!#define u8unsigned char //8位无符号变量(0-255)
#define u16 unsigned int //16位无符号变量(0-65535)u16 count_ms = {0,0,0}; //三个计时变量
u8 numb=0;
u16 key_vol=0;
u8 state={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.000MHzDelay20ms();
{
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>=1000){ //第一个任务每一秒打印一个hellow world
count_ms=0;
printf("当前第%d个led点亮\r\n",(u16)numb);
}
P2=state;
if(count_ms>=10){ //第二个任务按下p32按钮,led依次右移一位
count_ms=0;
if(P32==0){
key_vol ++;
if(key_vol==5){
numb ++;
if(numb>=8){
numb=0;
}
}
}
else{
key_vol=0;
}
}
}
}
if(count_ms>=1000){ //第一个任务每一秒打印一个hellow world
count_ms=0;
printf("当前第%d个led点亮\r\n",(u16)numb);
}
P2=state;
if(count_ms>=10){ //第二个任务按下p32按钮,led依次右移一位
count_ms=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++;}
}
}
}
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中断
}
</code></pre>
<p>这里补充一下,冲哥教程里没有</p>
<pre><code>if(numb>=8){
numb=0;
</code></pre>
<p>}这一段,会导致超过8之后会出现莫名其妙的问题,这个地方需要给numb重置一下。</p>
<h3>文件的创建</h3>
<p>这一节介绍了利用.c和.h文件,简化main.c中的代码,使代码看上去更加整洁。</p>
<p>.c文件用来存放函数,并且函数需要在.h文件中声明。.h文件用来放定义的变量,函数声明</p>
<h4><strong>config.c文件</strong></h4>
<pre><code>#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.000MHzDelay20ms();
{
unsigned long edata i;_nop_();
_nop_();
i = 119998UL;
while (i) i--;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}
</code></pre>
<p>config.h文件</p>
<pre><code>#ifndef __CONFIG_H
#define __CONFIG_H#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件#define u8unsigned char //8位无符号变量(0-255)
#define u16 unsigned int //16位无符号变量(0-65535)void sys_init(void); //函数声明void Timer0_Init(void);void Delay20ms(void);#endif
</code></pre>
<p>main.c</p>
<pre><code>#include "config.h"u16 count_ms = {0,0,0}; //三个计时变量
u8 numb=0;
u16 key_vol=0;
u8 state={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>=1000){ //第一个任务每一秒打印一个hellow world
count_ms=0;
printf("当前第%d个led点亮\r\n",(u16)numb);
}
P2=state;
if(count_ms>=10){ //第二个任务按下p32按钮,led依次右移一位
count_ms=0;
if(P32==0){
key_vol ++;
if(key_vol==5){
numb ++;
if(numb>=8){
numb=0;
}
}
}
else{
key_vol=0;
}
}
}
}
if(count_ms>=1000){ //第一个任务每一秒打印一个hellow world
count_ms=0;
printf("当前第%d个led点亮\r\n",(u16)numb);
}
P2=state;
if(count_ms>=10){ //第二个任务按下p32按钮,led依次右移一位
count_ms=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++;}
}
}
}
</code></pre>
<h3>结构体数组的周期性调度</h3>
<p>本节知识点还是挺多的,而且不是那么容易理解。</p>
<p>1.首先创建了task.c和task.h的文件</p>
<p>在task.c文件中,定义一个结构体:</p>
<pre><code>
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 */
};
</code></pre>
<p>任务标记回调函数:void Task_Marks_Handler_Callback(void),此函数的作用是对结构体中的每一组元素根据计数值进行倒数,当倒数到0时,计数重置为计数周期,并且第一位run置1,从而调用相应的任务执行函数。</p>
<pre><code>void Task_Marks_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.TIMCount) /* If the time is not 0 */
{
Task_Comps.TIMCount--; /* Time counter decrement */
if(Task_Comps.TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps.TIMCount = Task_Comps.TRITime;
Task_Comps.Run = 1; /* The task can be run */
}
}
}
}
</code></pre>
<p>任务处理回调函数:void Task_Marks_Handler_Callback(void),此函数的作用是当run置一后,执行相应的函数。</p>
<pre><code>void Task_Pro_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps.Run) /* If task can be run */
{
Task_Comps.Run = 0; /* Flag clear 0 */
Task_Comps.TaskHook(); /* Run task */
}
}
}
</code></pre>
<p>完整的task.c</p>
<pre><code>#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);
//========================================================================
// 函数: 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.TIMCount) /* If the time is not 0 */
{
Task_Comps.TIMCount--; /* Time counter decrement */
if(Task_Comps.TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps.TIMCount = Task_Comps.TRITime;
Task_Comps.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.Run) /* If task can be run */
{
Task_Comps.Run = 0; /* Flag clear 0 */
Task_Comps.TaskHook(); /* Run task */
}
}
}
</code></pre>
<p>在task.h文件中,有结构体声明及回调函数的声明</p>
<pre><code>#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
</code></pre>
<p>那之前p00每300ms闪烁一次,p01每600ms闪烁一次,p02每900ms闪烁一次为例</p>
<p>增加了io.c和io.h文件,将io口的配置转移到这里。</p>
<p>io.c文件</p>
<pre><code>#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;
}
}
</code></pre>
<p>io.h文件</p>
<pre><code>#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
</code></pre>
<p>main.c</p>
<pre><code>
#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(); //系统计时
}
</code></pre>
<h3>第九集 数码管</h3>
<p>本节使用两个hc595控制8位8段数码管</p>
<p>1.首先,在io.c中创建两个数组,分别存放数码管的段码和位码:</p>
<pre><code>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 = //数码管位码
{
0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80
};
</code></pre>
<p>其中,数码管的位码可以用isp软件-工具-字库生成工具 中找到数码管,直接就可以生成</p>
<p>然后是移位寄存器595端口初始化函数:void init_595(void),先把数据写到引脚上void Send_595(u8 dat),数码管段码输出,数码管位码输出void Display_Seg(u8 HC595_1,HC595_2)</p>
<p>需要显示的数据用void Seg_Task(void)显示。在task.c中,用结构体显示,</p>
<h4>任务2:8位数码管显示12345678</h4>
<pre><code>u8 Seg_no=0;
void Seg_Task(void){
Display_Seg( SEG_NUMB, ~T_NUM ); //数码管刷新断码和位码
Seg_no++;
if(Seg_no>7){
Seg_no=0;
}
</code></pre>
<p>task.c中每1ms执行一次Seg_Task</p>
<pre><code>static TASK_COMPONENTS Task_Comps[]=
{
{0, 1, 1, Seg_Task},/ 1ms执行一次 /
};
</code></pre>
<p>这里容易错的地方是,容易在Seg_Task函数中使用for循环来依次显示1-8位数码管数值,这里不需要再用到for循环,因为在{0, 1, 1, Seg_Task},中就是每1ms执行一次循环</p>
<h3>数码管任务3:动态显示时分秒00-00-00</h3>
<p>用到两个函数,Seg_Task用来显示数码管,时间间隔位1ms,CountUp用来每一秒将miao加一,时间间隔为1秒,都是通过任务调度实现</p>
<pre><code>
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, ~T_NUM ); break;//小时十位
case 1: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 2: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 3: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 4: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 5: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 6: Display_Seg( SEG_NUMB, ~T_NUM ); break;
case 7: Display_Seg( SEG_NUMB, ~T_NUM ); 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;
}
}
}
}
</code></pre>
<p>冲哥用的if esle if语句,我这里用了switch case,实现效果一样</p>
<p><img src="data/attachment/forum/202412/14/160200jl58112w755m42e1.jpg" alt="微信图片_20241214160144.jpg" title="微信图片_20241214160144.jpg" /></p>