《Ai8051U教学视频》学习心得
开篇从1月1日开始热爱电子技术的人大抵是热爱学习的。做为一个从小就对电子有些许喜好而从事的工作与电子无关的人,应该只能被定义为电子爱好者。那是23年,也许是为了使生活有更多的期许,也许是儿时梦想的延续,开始自学STC单片机。在一次不经意打开STC-ISP准备下载程序的时候,我偶然间发现了STC官方赠送的屠龙刀开发板活动。免费获得STC32G12K128,这对于才刚刚完成STC89C52点灯的老初学者来说诱惑实在太大。但是我既不是学生,又不是从业人员,如果贸然申请会被通过吗?不会是被人歧视吧。尽管有种种顾虑,但还是鼓起勇气联系了STC客服经理。出乎意料的是,客服经理竟然非常友好地通过了我的申请。那一刻,我深刻感受到了电子行业人的谦逊、好学和乐于助人。
随着学习的深入,我逐渐意识到,电子工程师的世界是一个充满挑战与机遇的天地。他们最关注的是自身技术的进步,最开心的事情是解决一个困扰自己多时的问题。在这个领域里,没有人会因为你的身份或背景而轻视你,只要你愿意学习、愿意分享,你就会受到大家的尊重和欢迎。
当得知《Ai8051U教学视频》这一优质学习资源时,我毫不犹豫地加入了学习计划。这套视频不仅内容详实、讲解清晰,而且还赠送实验箱。这怎么能错过。
2025年伊始,开始新的学习篇章,开贴记之。期待与坛友共同学习,共同进步,也鞭策自己可以坚持下去。
目录
1.《第三集 点亮第一颗LED》学习
2.《第四集 USB不停机下载》学习
3.《第六集 I/O输入输出》学习
4.《第七集 定时器中断》学习
5. 《第八集 定时器周期性调度任务》学习
6.《第九集 数码管》学习
7.《第十集 虚拟键盘LED和数码管》学习
8《第十一集 矩阵按键》学习
学习单片机必须要上手,所以从点灯开始
《第三集 点亮第一颗LED》学习
为了点亮一个LED,我们需要做什么?
1、从硬件上确定使用哪个IO
2、配置IO
3、为IO设置电平、延时程序
一、硬件上确定使用哪个IO
根据开发板实际情况,查看管脚定义,确认使用的LED已经与之相邻的IO。我使用的P46。
二、配置IO
对于89C51入门级的51单片机来说,是不用配置IO的,可以直接使用。因为系统已经缺省做好了配置。
但是Ai8051U需要先做配置再使用,这部分涉及寄存器的位操作,幸好STC-ISP提供了IO配置工具可以勾选一下就获得配置程序:
获得的程序片段:
P4M0 = 0x40; P4M1 = 0x00;
三、点灯之旅
1、延时函数
用STC-ISP提供的软件延时计算器生成延时1ms函数
生成的函数,及写个延时ms函数:
void Delay1ms(void) //@24.000MHz
{
unsigned long edata i;
_nop_();
_nop_();
_nop_();
i = 5998UL;
while (i) i--;
}
void delay_ms(unsigned short ms)
{
unsigned short i;
for(i=0;i<ms;i++) Delay1ms();
}
2、main函数
void main(void)
{
P4M0 = 0x40; P4M1 = 0x00;
while(1)
{
P46=1;
delay_ms(1000);
P46=0;
delay_ms(1000);
}
}P46=1;//熄灭
P46=0;//低电平点灯
3、包含头文件
#include "stc8051u.h"//我手头的是STC8051U
#include "INTRINS.h"//_nop_()函数需要
四、运行效果
《第四集 USB不停机下载》学习
STC单片机需要复位才能下载程序:
STC单片机在启动时,会首先执行一段出厂时固化的程序。这段程序的主要作用是检测串口是否有下载程序的需求。如果没有下载需求,单片机将继续执行内部的用户程序。这就是为什么在每次下载程序时需要进行复位。
一般STC单片机具有以下几种复位方式可以选择,每一种复位都可以用来触发下载程序。
1、上电复位
2、低压复位(掉电复位)
3、复位脚复位(RST)
4、看门狗复位
5、软件复位(寄存器IAP_CONTR的SWRST位)
IAP_CONTR = 0x60; //触发软件复位,从ISP开始执行
但是随着单片机3.3v工作电压的普及,导致部分单片机可以通过串口供电而使复位变得困难;还有一些单片机取消了RST复位引脚,从而导致复位的手段越来越少,复位可靠性也较低,传统的下载方式不断接收挑战。
于是不停机下载应运而生。个人理解不停机下载主要是利用软件复位,在不手动接触开发板的情况下,通过各种手段发出软件复位命令,如串口、USB。
因为Ai8051U具有USB功能,可以将USB配置为USB CDC或USB HID进行下载。而CDC可以理解为串口,所以本质上就是ISP通过串口发送了”@STCISP#"指令,单片机串口接收处理函数判断接收成功后,触发软件复位,进而带动程序下载。
教程中给出的程序如下:
#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
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();
}
P40 = 0; //P40端口输出0V
P00 = 0; //P00端口输出0V
P02 = 0; //P02端口输出0V
//P01 = 0; //P01端口输出0V
//
}
}
char *USER_STCISPCMD = "@STCISP#";是可以修改的,对应STC-ISP如下位置也需要修改:
关于USB的驱动和不停机处理程序以静态库方式提供:
当然这个程序还提供串口接收转发的功能,从串口工具上发送什么内容,就接收到什么内容:
关于“当目标文件变化时自动装载并发送下载命令”,看个人喜好吧,因为并不是每次编译都要下载,有的时候会在编译的时候发现逻辑问题,可能不想直接下载。
《第六集 I/O输入输出》学习
点灯和按键是单片机永恒的两大主题,IO输入输出是实现这个主题的具体实现方式
一、先定义一下用到的IO
#define KEY1 P11
#define KEY2 P12
#define KEY3 P13
#define LED2 P46
#define LED3 P14
#define LED4 P20
二、IO初始化
P1M0 = 0x00; P1M1 = 0x00;
P2M0 = 0x00; P2M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00; 都初始化为准双向口。
三、按键轮询控制LED翻转
while(1)
{
if(KEY1==0)
{
delay_ms(10);
if(KEY1==0)
LED2=~LED2;
while(KEY1==0);
}
if(KEY2==0)
{
delay_ms(10);
if(KEY2==0)
LED3=~LED3;
while(KEY2==0);
}
if(KEY3==0)
{
delay_ms(10);
if(KEY3==0)
LED4=~LED4;
while(KEY3==0);
}按键消抖处理:判断低电平、延时、判断低电平、低电平循环、高电平退出。
四、实验效果
五、简单实现下按一键亮一灯
if(KEY1==0)
{
delay_ms(10);
if(KEY1==0)
{
ledcount++;
if(ledcount>3){
ledcount=0;
}
if(ledcount==1)LED2=0;
if(ledcount==2)LED3=0;
if(ledcount==3)LED4=0;
if(ledcount==0){LED2=1;LED3=1;LED4=1;}
}
while(KEY1==0);
}
不错,自己焊接的板子? 《第七集 定时器中断》学习
为了实现练习,需要进一步实现串口驱动(我没有采用USB CDC方式,所以需要单独实现)
一、串口2驱动
本来想用AIapp-ISP中的串口波特率工具生成代码,但是怎么配置出的代码运行起来都是乱码,还是通过从例程中找了一段代码解决
1、主时钟和波特率定义
#define MAIN_Fosc 24000000L //定义主时钟
#define Baudrate2 (65536 - MAIN_Fosc / 115200 / 4)2、UART2初始化
void SetTimer2Baudraye(u32 dat)
{
T2R = 0; //Timer stop
T2_CT = 0; //Timer2 set As Timer
T2x12 = 1; //Timer2 set as 1T mode
T2H = (u8)(dat / 256);
T2L = (u8)(dat % 256);
ET2 = 0; //禁止中断
T2R = 1; //Timer run enable
}
void UART2_config(u8 brt) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 无效.
{
if(brt == 2)
{
SetTimer2Baudraye(Baudrate2);
S2CFG |= 0x01; //使用串口2时,W1位必需设置为1,否则可能会产生不可预期的错误
S2CON = (S2CON & 0x3f) | 0x40; //UART2模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
ES2 = 1; //允许中断
S2REN = 1; //允许接收
S2_S= 0; //UART2 switch to: 0: P1.2 P1.3,1: P4.2 P4.3
B_TX2_Busy = 0;
}
}波特率:115200;串口引脚复用:P1.2\P1.3
3、串口中断
void Uart2_Isr(void) interrupt 8
{
if (S2CON & 0x02) //检测串口2发送中断
{
S2CON &= ~0x02; //清除串口2发送中断请求位
B_TX2_Busy = 0;
}
if (S2CON & 0x01) //检测串口2接收中断
{
S2CON &= ~0x01; //清除串口2接收中断请求位
}
}UART2中断向量为8,这个可以从头文件中查到:
4、printf重定向
char putchar(char c)
{
S2BUF = c;
B_TX2_Busy = 1;
while(B_TX2_Busy);
return c;
}
二、定时器驱动
1、TIMER0初始化
void Timer0_Init(void) //500毫秒@24.000MHz
{
TM0PS = 0xB7; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x3F; //设置定时初始值
TH0 = 0x01; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}2、TIMER0中断处理
void Timer0_Isr(void) interrupt 1
{
out_put_flag=1;
Timer0_count++;
}
三、main函数
Timer0_Init();
UART2_config(2);
EA=1;
while(1)
{
if(out_put_flag==1)
{
out_put_flag=0;
printf("定时器500ms触发一次串口输出,中断触发次数:%d\r\n",Timer0_count);
}
四、运行
《第八集 定时器周期性调度任务》学习
不得不为了这一集拍案叫绝,也许我们都使用过实时操作系统,freertos、rethread、调度器等等,但真正愿意深入了解原理的并不多,本集看完有豁然开朗的感觉。
调度任务可以理解为将MCU的时间进行分时处理,对于优先级高的任务,优先执行。这样使每个任务都可以被mcu执行,而又不独占系统。这样程序也优雅起来了。
一、任务注册
typedef struct
{
u8 Run; //任务状态:Run/Stop
u16 TIMCount; //定时计数器
u16 TRITime; //重载计数器
void (*TaskHook) (void); //任务函数
} TASK_COMPONENTS; 首先定义了一个任务结构体,用于记录任务的状态;执行倒计时(定时);倒计时重新设定值(可以理解为优先级,重载计数器越小优先权越高);任务函数,这是一个函数指针,根据任务声明执行不同的任务函数。
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */
{0, 600, 600, LED1_Blink}, /* task 1 Period: 600ms */
{0, 900, 900, LED2_Blink}, /* task 1 Period: 600ms */
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
};定义任务数组,可以根据需要添加。这里注册了4个任务。
二、轮询调度
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 */
}
}
}
}此函数会在定时器中断回调中调用,依次减少每个任务的定时倒计时,当倒计时到0时,设置任务run状态为1。
三、调度执行函数
此函数需要在main函数while(1)中调用,当任何一个任务处于run==1状态,执行任务中的注册函数
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 */
}
}
}
四、main
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 //1MS执行一次
{
Task_Marks_Handler_Callback(); //系统计时
}
至此,以后增加功能就是增加一个TASK。
《第九集 数码管》学习
因为手头没有595驱动的数码管,用595驱动的8x8点阵屏代替
课后练习完成如下,不知如何@冲哥
一、倒计时任务
void wash_Task(void)
{
if(washing_machine_status==2)
{
washing_machine_times--; //倒计时显示
if(washing_machine_times==0) washing_machine_status=0;
}
}二、显示任务
void matrix_Task(void)
{
//show_matrix8x8(&zimo);
clear_matrix8x8();
if(washing_machine_status==0){
set_matrix8x8(0,1); //设置显示1,清洗模式
}else if(washing_machine_status==1){
if(washing_machine_mode!=0) set_matrix8x8(4,washing_machine_mode); //设置显示模式选择
}else if(washing_machine_status==2){
set_matrix8x8(4,washing_machine_times%10); //设置显示时间倒计时
set_matrix8x8(0,washing_machine_times/10);
}
show_matrix8x8(matrix1); //显示
}
三、key任务
void KEY_Task(void)
{
if(KEY1==0)
{
delay_ms(10);
if(KEY1==0){
LED2=~LED2;
washing_machine_status=0;
}
while(KEY1==0);
}
if(KEY2==0)
{
delay_ms(10);
if(KEY2==0){
LED3=~LED3;
washing_machine_status=1; //mode选择
washing_machine_mode++;
if(washing_machine_mode>5)washing_machine_mode=1;
}
while(KEY2==0);
}
if(KEY3==0)
{
delay_ms(10);
if(KEY3==0){
LED4=~LED4;
washing_machine_times=washing_machine_times_by_mode;
washing_machine_status=2; //确认开始清洗
}
while(KEY3==0);
}
}
四、任务注册
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 500, 500, wash_Task},
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
{0, 200, 200, matrix_Task},
};
文字很少,相信代码自己会说话
《第十集 虚拟键盘LED和数码管》学习
虚拟键盘LED和数码管是Aiapp-ISP提供的功能,可以通过如下菜单打开。
工具很贴心提供了帮助信息
一、实验一:通过串口工具发送数码管协议指令显示数字
1、注册一个CDC_Task任务用于处理串口收发
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 200, 200, CDC_Task},
{0, 500, 500, wash_Task},
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
{0, 200, 200, matrix_Task},
};2、CDC_Task任务处理函数
void CDC_Task(void)
{
if (bUsbOutReady) //如果接收到了数据
{
USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done(); //
}
}
将USB配置为CDC虚拟串口,串口在收到数据后,原路返回。
3、按照数码管串口数据协议发送数据
功能1:在数码管上显示字符串
命令格式:37H 53H 45H 47H 53H 00H 00H 00H s1 s2 s3 s4 ...
命令说明:
第1~4字节:命令头
第5字节:功能选择(53H显示字符串)
第6~8字节:保留
第9~n字节:字符的ASCII码,字符串必须以‘\0’结尾
示例:发送37H 53H 45H 47H 53H 00H 00H 00H 31H 32H 2EH 33H 00H
在数码管上会显示“12.3”
库函数声明:int SEG7_ShowString(const char *fmt, ...);
库函数调用:SEG7_ShowString("%08lx", 0x1234abcdL);
1)通过串口工具先MCU发送指令37H 53H 45H 47H 53H 00H 00H 00H 31H 32H 2EH 33H 00H,
2)MCU指令数据后,原路返回
3)窗口工具在收到指令数据后,判定为虚拟数码管驱动指令,解析后在虚拟数码管上显示12.3。
二、实验二:密码锁
完成如下:
491
1、任务注册
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 200, 200, CDC_Task},
{0, 500, 500, SEG7_Task},
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
};
2、CDC_Task负责串口接收虚拟键盘指令
void CDC_Task(void)
{
if (bUsbOutReady) //如果接收到了数据
{
//USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
UART_NUM = UsbOutBuffer;
UART_RX_FLAG=1;
usb_OUT_done(); //
}
}
3、SEG7_Task用于发送SEG显示指令
void SEG7_Task(void)
{
u8 i;
if(UART_RX_FLAG==1)
{
UART_RX_FLAG=0;
SEG7_DATA=UART_NUM;
UART_CUR++;
if(UART_CUR==8){
if(SEG7_DATA==0x31&&SEG7_DATA==0x32&&SEG7_DATA==0x33&&SEG7_DATA==0x34&&
SEG7_DATA==0x35&&SEG7_DATA==0x36&&SEG7_DATA==0x37&&SEG7_DATA==0x38)
{
SEG7_status=2;
}
else{
SEG7_status=0;
for(i=0;i<8;i++)
{
SEG7_DATA=' ';
}
}
UART_CUR=0;
}else
{
SEG7_status=1;
}
if(SEG7_status==0){ //状态1:显示“--------”
SEG7_show_one_line_char('-');
}else if(SEG7_status==1){ //状态2:显示密码
SEG7_ShowString("%s",(char*)SEG7_DATA);
}else if(SEG7_status==2){ //状态3:密码对了显示OPEN
SEG7_show_open();
}
}else if(SEG7_status==0) SEG7_show_one_line_char('-');
}
4、显示OPEN
void SEG7_show_open(void)
{
BYTE cod;
cod = 0x00;
cod = 0x00;
cod = 0x00;
cod = 0x00;
cod = 0x3F;
cod = 0x73;
cod = 0x79;
cod = 0x37;
SEG7_ShowCode(cod);
}5、显示一行指定字符
void SEG7_show_one_line_char(u8 c)
{
u8 i;
u8 str;
for(i=0;i<8;i++) str=c;
SEG7_ShowString("%s",str);
}
《第十一集 矩阵按键》学习
492
一、注册任务
static TASK_COMPONENTS Task_Comps[]=
{
//状态计数周期函数
{0, 500, 500, wash_Task},
{0, 10, 10, KEY_Task},
{0, 10, 10, KEYBOARD_Task}, /*矩阵键盘*/
{0, 200, 200, matrix_Task},
};KEYBOARD_Task是矩阵按键任务
二、矩阵按键处理函数
这部分基本都是抄冲哥的,只是改成了4*4矩阵按键
void KEYBOARD_Task(void)
{
//①第一步:现将P0.0-P0.3输出低电平,P0.6-P0.7输出高电平,如果有按键按下,按下的那一行的IO就会变成低电平,就可以判断出哪一行按下了。
KEY_COL1 = 0;
KEY_COL2 = 0;
KEY_COL3 = 0;
KEY_COL4 = 0;
KEY_ROW1 = 1;
KEY_ROW2 = 1;
KEY_ROW3 = 1;
KEY_ROW4 = 1;
if(( KEY_ROW1 == 0 ) || ( KEY_ROW2 == 0 )|| ( KEY_ROW3 == 0 )|| ( KEY_ROW4 == 0 )) //如果行按键有按下
{
if(( KEY_ROW1 ==0 ) && ( KEY_ROW2 ==0 )&& ( KEY_ROW3 ==0 )&& ( KEY_ROW4 ==0 )) //如果两行都有按键按下,不处理
{
}
else if( KEY_ROW1==0||KEY_ROW2==0||KEY_ROW3 ==0||KEY_ROW4 ==0 ) //如果有按键按下,而且只有一颗
{
if( KEY_ROW1 ==0 ) //判断哪一行,输出行开始的序号
key_num = 0;
else if( KEY_ROW2 ==0 )
key_num = 4;
else if( KEY_ROW3 ==0 )
key_num = 8;
else if( KEY_ROW4 ==0 )
key_num = 12;
//②第二步:现将P0.0-P0.3输出高电平,P0.6-P0.7输出低电平,如果有按键按下,按下的那一列的IO就会变成低电平,就可以判断出哪一列按下了。
KEY_COL1 = 1;
KEY_COL2 = 1;
KEY_COL3 = 1;
KEY_COL4 = 1;
KEY_ROW1 = 0;
KEY_ROW2 = 0;
KEY_ROW3 = 0;
KEY_ROW4 = 0;
if( KEY_COL1 ==0 ) //判断哪一列,叠加按键的序号
{
// key_num = key_num ;
}
else if( KEY_COL2 ==0 )
{
key_num = key_num + 1;
}
else if( KEY_COL3 ==0 )
{
key_num = key_num + 2;
}
else if( KEY_COL4 ==0 )
{
key_num = key_num + 3;
}
}
KEY_COL1 = 0;
KEY_COL2 = 0;
KEY_COL3 = 0;
KEY_COL4 = 0;
KEY_ROW1 = 1;
KEY_ROW2 = 1;
KEY_ROW3 = 1;
KEY_ROW4 = 1;
}
else
{
key_num = 0xff;
}
//③第三步:行列组合一下就可以判断出是哪个按键按下了。
}
三、在点阵屏上显示
void matrix_Task(void)
{
static u8 num=0;
if(key_num!=0xff) num=key_num;
clear_matrix8x8();
if(num!=0xff){
set_matrix8x8(4,num%10);
set_matrix8x8(0,num/10);
}
show_matrix8x8(matrix1); //显示
}
下面是595的驱动控制:
void show_matrix8x8(u8 *pBuff)
{
unsigned char k;
unsigned int m,n;
for(m=0;m<32;m++) //为移动预留
{
for(k=0;k<8;k++) //行扫描
{
for(n=0;n<32;n++)//控制显示速度,防止闪烁
{
HC595(~pos,0);
HC595(pBuff,0);
//delay_us(10);
LED_RCK_CLR;
LED_RCK_SET; //并行输出
delay_us(5);
}
}
}
}
void HC595(u8 c,u8 PN)
{
u8 i;
for(i=0;i<8;i++){
if(PN==0){ //正常显示
if((c>>i) & 0x01) LED_SER_SET;
else LED_SER_CLR;
}
else{ //反显
if((c>>i) & 0x01) LED_SER_CLR;
else LED_SER_SET;
}
LED_SRCK_CLR;
LED_SRCK_SET; // 上升沿进行一次数据移入
}
}
void set_matrix8x8(u8 startPos,u8 num)
{
u8 i;
for(i=0;i<3;i++)
{
matrix1=NUM;
}
}
void clear_matrix8x8()
{
u8 i;
for(i=0;i<8;i++)
{
matrix1=0x00;
}
}
全貌
页:
[1]
2