admin
发表于 2025-4-14 15:13:34
汇编语言 实验
2.18.6 深大-点灯实验4(使能Ai8051U内置 USB直接 仿真流水灯)
实验程序汇编语言代码
;以下是控制P6.0~P6.7引脚上一个LED灯循环亮灭跑马灯的汇编语言程序代码。
;特别注意延时子程序汇编语言写法。试通过每一条指令的所花的机器时钟周期计算一下软件延时时间。
;251汇编程序
$INCLUDE(AI8051U.INC)
STACK_POIRTER EQU 0D0H ;堆栈开始地址
ORG 0000H
LJMP MAIN
ORG 0100H
MAIN:
SETB EAXFR ;允许访问扩展的特殊寄存器,XFR
MOV WTST,#00H ;设置取程序代码等待时间,赋值为 0 表示不等待,程序以最快速度运行
MOV CKCON,#00H ;设置访问片内的 xdata 速度,赋值为 0 表示用最快速度访问,不增加额外的等待时间
MOV SP, #STACK_POIRTER ;堆栈栈顶设置
;端口初始化
CLR A
MOV P0M1, A ;设置P0为准双向口
MOV P0M0, A
MOV P1M1, A ;设置P1为准双向口
MOV P1M0, A
MOV P2M1, A ;设置P2为准双向口
MOV P2M0, A
MOV P3M1, A
MOV P3M0, A
MOV P4M1, A
MOV P4M0, A
MOV P5M1, A
MOV P5M0, A
MOV P6M1, A
MOV P6M0, A
MOV P7M1, A
MOV P7M0, A
MOV P0, #0FFH
;变量初始化
CLR P40 ;流水灯总开关打开
LOOP:
MOV P0,#00H
ACALL DELAY500MS ;调用延时子程序
MOV P0,#0FFH
ACALL DELAY500MS ;调用延时子程序
CLR P00 ;给P6^0清零,低电平时led亮
ACALL DELAY500MS ;调用延时子程序
SETB P00 ;给P6^0置1,高电平时led灭
CLR P01
ACALL DELAY500MS ;调用延时子程序
SETB P01
CLR P02
ACALL DELAY500MS ;调用延时子程序
SETB P02
CLR P03
ACALL DELAY500MS ;500MS;调用延时子程序
SETB P03
CLR P04
ACALL DELAY500MS ;调用延时子程序
SETB P04
CLR P05
ACALL DELAY500MS ;调用延时子程序
SETB P05
CLR P06
ACALL DELAY500MS ;调用延时子程序
SETB P06
CLR P07
ACALL DELAY500MS
SETB P07
SJMP LOOP ;无条件跳转至LOOP处
DELAY500MS: ;延时0.5S子程序@40.000MHz
PUSH DR0 ;保护现场,将双字寄存器DR0(即R0~R3)推入堆栈保存
MOV WR0,#76 ;将WR0赋值76(思考R0=?R1=?)
MOV WR2,#19262 ;将WR2赋值19262(思考R2=?R3=?)
NEXT:
DEC DR0,#1 ;DR0<-(DR0)-1
JNE NEXT ;(DR0)≠0,则跳至NEXT标号处即继续-1,知道等于0则执行下一条语句
POP DR0 ;恢复现场
RET ;子程序返回
;-----------------
END ;汇编程序结束伪指令语句
神农鼎
发表于 2025-4-14 15:33:51
C51语言如何嵌入汇编
嗯,我现在需要了解如何在C51语言中嵌入汇编代码。这个问题我之前没有接触过,所以得从头开始学。首先,我得回忆一下C51是什么。C51应该是指用于8051系列单片机的C语言编译器,对吧?那嵌入汇编通常是为了优化性能或者直接操作硬件,这在单片机编程中很常见。
那问题来了,如何在C51中嵌入汇编呢?根据我之前学过的其他编程语言,比如在GCC中使用内联汇编,可能C51也有类似的方法。不过不同的编译器可能有不同的语法,所以我得查一下C51的具体实现方式。
首先,我记得在C语言中嵌入汇编通常有两种方法:内联汇编和单独的汇编模块。在C51中,可能也是这两种方式。不过可能具体的语法会有所不同。比如,在Keil C51中,可能会有特定的关键字或者预处理指令来嵌入汇编代码。
然后,我需要考虑如何在函数内部插入汇编指令。比如,是否有类似于__asm关键字,或者需要用#asm和#endasm这样的预处理指令?比如,在Keil的文档中,可能会提到使用#pragma asm和#pragma endasm来包裹汇编代码,但需要设置编译器选项,比如在项目设置中启用SRC选项生成汇编源文件,然后再编译。
另外,嵌入汇编的时候需要注意寄存器的使用,是否会影响C代码的变量。例如,可能需要保存和恢复某些寄存器,或者使用特定的语法将C变量与汇编操作数绑定。例如,使用_ACC_来表示累加器A,或者直接通过变量名来访问。
另外,单独的汇编文件可能更复杂一些,需要将汇编代码写在独立的.asm文件中,然后在C代码中声明这些函数的原型,通过链接器将它们链接在一起。这可能涉及到调用约定、参数传递等问题,比如参数是通过寄存器还是堆栈传递,返回值如何存放等等。
举个例子,如果我想在C51的函数中插入一段汇编代码来操作端口,可能需要这样写:
void my_function() {#pragma asmMOV A, #0FFhMOV P1, A#pragma endasm}
但可能需要在编译器选项中启用生成SRC文件,或者在项目设置里允许内联汇编。否则编译器可能会报错。
不过,我之前可能混淆了不同的编译器的语法。比如,有些编译器可能使用__asm { ... }这样的结构,而C51可能使用不同的方式。所以可能需要查阅Keil C51的手册或者相关资料,确认正确的语法。
另外,嵌入汇编时还需要考虑中断处理的问题,如果在中断服务例程中嵌入汇编,可能需要特别处理寄存器的保存,或者使用特定的编译器扩展。
还有,变量的访问,比如如何在汇编代码中使用C中定义的变量。可能需要使用特定的符号或者语法,例如用变量名前加下划线,或者通过DPTR来访问数据存储器中的变量。
比如,在汇编中访问C变量var,可能需要这样:
MOV A, _var
或者,如果var位于特定的存储区域,可能需要使用不同的指令。
总的来说,在C51中嵌入汇编的步骤可能包括:
1. 在C函数中使用特定的预处理指令包裹汇编代码,比如#pragma asm和#pragma endasm。2. 配置编译器选项,允许生成汇编源文件,并正确编译。3. 在汇编代码中正确使用寄存器和访问C变量,注意不要破坏C环境。4. 处理函数调用时的参数传递和返回值。
不过具体的细节可能需要查阅C51的编译器手册,或者寻找相关的示例代码。例如,是否需要为嵌入的汇编代码保存和恢复某些寄存器,或者编译器是否自动处理这些。
另外,可能有一些限制,比如不能在所有的函数中嵌入汇编,或者某些优化选项可能与内联汇编冲突。
现在,我应该总结这些可能的步骤,并且给出一个具体的例子,说明如何在C51中嵌入汇编代码,同时提到需要注意的事项,比如编译器设置、寄存器使用、变量访问等。
比如,可能的步骤是:
1. 在C文件中使用#pragma asm和#pragma endasm包裹汇编代码。2. 在Keil中,需要将源文件添加到工程,并右键点击该文件,设置Options,勾选Generate Assembler SRC File和Assemble SRC File选项。3. 在汇编代码中,可以通过_ACC_访问累加器,通过变量名前加下划线访问C变量。4. 注意寄存器的使用,避免影响C代码的执行。
例如,一个具体的例子:
#include <reg51.h>
void delay() {#pragma asmMOV R0, #100LOOP:DJNZ R0, LOOP#pragma endasm}
然后在项目设置中启用生成SRC文件,并确保编译器能够正确编译。
不过,可能还需要在文件开头包含某些头文件,或者声明寄存器的地址,比如reg51.h定义了P1等端口。
总结的话,嵌入汇编需要特定的编译器指令和项目设置,正确使用寄存器和变量,以及注意不要破坏C环境的状态。
在C51语言中嵌入汇编代码可以通过以下步骤实现,主要用于优化关键代码或直接硬件操作:方法一:使用预处理指令内联汇编
[*]编写C函数并嵌入汇编代码
使用#pragma asm和#pragma endasm指令包裹汇编代码:c
复制
#include <reg51.h>// 包含寄存器定义void port_control() { #pragma asm MOV A, #0FFh ; 将立即数0xFF加载到累加器A MOV P1, A ; 将A的值输出到P1端口 #pragma endasm}
[*]配置编译器选项(Keil环境)
[*]右键点击C文件,选择“Options for File”。
[*]勾选“Generate Assembler SRC File”和“Assemble SRC File”,以允许生成并编译汇编代码。
[*]注意事项
[*]寄存器使用:避免随意修改可能被C代码使用的寄存器(如R0-R7),必要时保存恢复。
[*]变量访问:C变量在汇编中通过名称加下划线访问,如MOV A, _myVar。
[*]中断处理:在中断服务例程中嵌入汇编时,需手动保存受影响寄存器。
方法二:调用独立汇编模块
[*]编写独立汇编文件
创建.asm文件并定义函数:assembly
复制
; delay.asmPUBLIC DELAY; 声明函数为公共DELAY SEGMENT CODERSEG DELAY MOV R2, #100LOOP: DJNZ R2, LOOP RET
[*]在C中声明并调用函数c
复制
extern void DELAY(void);// 声明外部汇编函数void main() { DELAY();// 调用汇编函数}
关键注意事项
[*]参数传递:参数通过寄存器(如R7、R5)传递,返回值在ACC或寄存器中。
[*]内存模型:确保汇编代码与C代码的内存模型(SMALL、COMPACT、LARGE)匹配。
[*]优化冲突:高优化级别可能导致内联汇编错误,建议调试阶段关闭优化。
示例:混合C与汇编实现延时c
复制
#include <reg51.h>void asm_delay(unsigned char count) { #pragma asm MOV R0, _count; 使用C参数countLOOP: DJNZ R0, LOOP #pragma endasm}void main() { while(1) { P1 = 0x00; // C代码控制端口 asm_delay(255); // 调用内联汇编延时 P1 = 0xFF; asm_delay(255); }}
常见问题解决
[*]编译错误:未启用SRC生成选项导致#pragma asm不被识别。
[*]变量未定义:汇编中访问C变量时需确保已正确声明且作用域可见。
[*]寄存器冲突:在嵌入代码前后使用PUSH/POP保护关键寄存器(如PSW、ACC)。
通过上述方法,可在C51项目中高效结合C与汇编,兼顾开发效率与性能需求。
神农鼎
发表于 2025-4-16 10:36:19
AiCube-可视化 图形 I/O配置 体验
https://v.stcai.com/sv/e40e76b-1963e22a9cc/e40e76b-1963e22a9cc.mp4
利用 AiCube@AIapp-ISP V6.95M,
智能配置开发工具启动程序框架自动生成器,实现如下的实验:
32位8051单片机入门的第一个完整的C语言程序,printf_usb("Hello World !\r\n")
https://v.stcai.com/sv/426d0d7d-1963d548feb/426d0d7d-1963d548feb.mp4
AiCube 实验一:直接 printf_usb("Hello World !\r\n")
32位8051单片机入门的第一个完整的C语言程序
AiCube 实验二:查询方式,查询到电脑命令后,
printf_usb("Hello World !\r\n")或其他
AiCube 实验三:中断方式,单片机USB中断接收服务函数收到命令后,
printf_usb("Hello World !\r\n")或其他
乘风飞扬
发表于 2025-4-21 14:46:35
通过IO口控制74HC595驱动8个共阴极数码管
C语言:
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/*---------------------------------------------------------------------*/
/*************功能说明 **************
本例程基于AI8051U为主控芯片的实验箱进行编写测试.
使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
用STC的MCU的IO方式控制74HC595驱动8位数码管。
显示效果为: 数码时钟.
使用Timer0的16位自动重装来产生1ms节拍,程序运行于这个节拍下, 用户修改MCU主时钟频率时,自动定时于1ms.
下载时, 选择时钟 24MHZ (用户可自行修改频率).
******************************************/
#include "..\..\comm\AI8051U.h"
#include "stdio.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define MAIN_Fosc 24000000UL
//==========================================================================
#define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率, 1000次/秒
#define DIS_DOT 0x20
#define DIS_BLACK 0x10
#define DIS_ 0x11
/*************本地常量声明 **************/
u8 code t_display[]={ //标准字库
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black- H J K L N o P U t G Q r M y
0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码
/*************IO口定义 **************/
sbit P_HC595_SER = P3^4; //pin 14 SER data input
sbit P_HC595_RCLK= P3^5; //pin 12 RCLk store (latch) clock
sbit P_HC595_SRCLK = P3^2; //pin 11 SRCLK Shift data clock
/*************本地变量声明 **************/
u8LED8; //显示缓冲
u8display_index;//显示位索引
bit B_1ms; //1ms标志
u8hour,minute,second;
u16 msecond;
/*************本地函数声明 **************/
/****************外部函数声明和外部变量声明 *****************/
/********************** 显示时钟函数 ************************/
void DisplayRTC(void)
{
if(hour >= 10)LED8 = hour / 10;
else LED8 = DIS_BLACK;
LED8 = hour % 10;
LED8 = DIS_;
LED8 = minute / 10;
LED8 = minute % 10;
LED8 = DIS_;
LED8 = second / 10;
LED8 = second % 10;
}
/********************** RTC演示函数 ************************/
void RTC(void)
{
if(++second >= 60)
{
second = 0;
if(++minute >= 60)
{
minute = 0;
if(++hour >= 24) hour = 0;
}
}
}
/******************** 主函数 **************************/
void main(void)
{
u8i,k;
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; //设置为准双向口
T0x12 = 1;//Timer0 set as 1T, 16 bits timer auto-reload,
TH0 = (u8)(Timer0_Reload / 256);
TL0 = (u8)(Timer0_Reload % 256);
ET0 = 1; //Timer0 interrupt enable
TR0 = 1; //Tiner0 run
EA = 1; //打开总中断
display_index = 0;
hour = 11; //初始化时间值
minute = 59;
second = 58;
RTC();
DisplayRTC();
//for(i=0; i<8; i++)LED8 = DIS_BLACK; //上电消隐
for(i=0; i<8; i++)LED8 = i; //显示01234567
k = 0;
while(1)
{
if(B_1ms) //1ms到
{
B_1ms = 0;
if(++msecond >= 1000) //1秒到
{
// P42 = !P42;
msecond = 0;
RTC();
DisplayRTC();
}
}
}
}
/**************** 向HC595发送一个字节函数 ******************/
void Send_595(u8 dat)
{
u8i;
for(i=0; i<8; i++)
{
dat <<= 1;
P_HC595_SER = CY;
P_HC595_SRCLK = 1;
P_HC595_SRCLK = 0;
}
}
/********************** 显示扫描函数 ************************/
void DisplayScan(void)
{
Send_595(t_display]); //输出段码
Send_595(~T_COM); //输出位码
P_HC595_RCLK = 1;
P_HC595_RCLK = 0;
if(++display_index >= 8) display_index = 0;//8位结束回0
}
/********************** Timer0 1ms中断函数 ************************/
void timer0 (void) interrupt 1
{
DisplayScan();//1ms扫描显示一位
B_1ms = 1; //1ms标志
}
汇编:
;/*---------------------------------------------------------------------*/
;/* --- Web: www.STCAI.com ---------------------------------------------*/
;/*---------------------------------------------------------------------*/
;*************功能说明 **************
;本例程基于AI8051U为主控芯片的实验箱进行编写测试。
;使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
;data建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
;用STC的MCU的IO方式控制74HC595驱动8位数码管。
;显示效果为: 数码时钟.
;使用Timer0的16位自动重装来产生1ms节拍,程序运行于这个节拍下, 用户修改MCU主时钟频率时,自动定时于1ms.
;下载时, 选择时钟 24MHZ (用户可自行修改频率).
;******************************************/
$include (../../comm/AI8051U.INC)
;/****************************** 用户定义宏 ***********************************/
Fosc_KHZ EQU 24000 ;24000KHZ
STACK_POIRTER EQU 0D0H ;堆栈开始地址
Timer0_Reload EQU (65536 - Fosc_KHZ); Timer 0 中断频率, 1000次/秒
DIS_DOT EQU 020H
DIS_BLACK EQU 010H
DIS_ EQU 011H
;*******************************************************************
;*******************************************************************
;*************IO口定义 **************/
P_HC595_SER BIT P3.4; //pin 14 SER data input
P_HC595_RCLK BIT P3.5; //pin 12 RCLk store (latch) clock
P_HC595_SRCLK BIT P3.2; //pin 11 SRCLK Shift data clock
;*************本地变量声明 **************/
Flag0 DATA 20H
B_1ms BIT Flag0.0 ; 1ms标志
LED8 DATA 30H ; 显示缓冲 30H ~ 37H
display_index DATA 38H ; 显示位索引
usrhour DATA 39H ;
usrminute DATA 3AH
usrsecond DATA 3BH ;
msecond DATA 3CH ; 2 byte
;*******************************************************************
;*******************************************************************
ORG 0000H ;程序复位入口,编译器自动定义到 0FF0000H 地址
LJMP F_Main
ORG 000BH ;1Timer0 interrupt
LJMP F_Timer0_Interrupt
;*******************************************************************
;*******************************************************************
;******************** 主程序 **************************/
ORG 0100H ;编译器自动定义到 0FF0100H 地址
F_Main:
MOV WTST, #00H ;设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
MOV CKCON,#00H ;提高访问XRAM速度
ORL P_SW2,#080H ;使能访问XFR
MOV P0M1, #00H ;设置为准双向口
MOV P0M0, #00H
MOV P1M1, #00H ;设置为准双向口
MOV P1M0, #00H
MOV P2M1, #00H ;设置为准双向口
MOV P2M0, #00H
MOV P3M1, #00H ;设置为准双向口
MOV P3M0, #00H
MOV P4M1, #00H ;设置为准双向口
MOV P4M0, #00H
MOV P5M1, #00H ;设置为准双向口
MOV P5M0, #00H
MOV P6M1, #00H ;设置为准双向口
MOV P6M0, #00H
MOV P7M1, #00H ;设置为准双向口
MOV P7M0, #00H
MOV SP, #STACK_POIRTER
MOV PSW, #0
USING 0 ;选择第0组R0~R7
;================= 用户初始化程序 ====================================
MOV display_index, #0
MOV R0, #LED8
MOV R2, #8
L_ClearLoop:
MOV @R0, #DIS_BLACK ;上电消隐
INC R0
DJNZ R2, L_ClearLoop
CLR TR0
ORL AUXR, #(1 SHL 7) ; Timer0_1T();
ANL TMOD, #NOT 04H ; Timer0_AsTimer();
ANL TMOD, #NOT 03H ; Timer0_16bitAutoReload();
MOV TH0, #Timer0_Reload / 256 ;Timer0_Load(Timer0_Reload);
MOV TL0, #Timer0_Reload MOD 256
SETB ET0 ; Timer0_InterruptEnable();
SETB TR0 ; Timer0_Run();
SETB EA ; 打开总中断
MOV usrhour, #12 ; 初始化时间值
MOV usrminute, #0
MOV usrsecond, #0
LCALL F_DisplayRTC
;=================== 主循环 ==================================
L_Main_Loop:
JNB B_1ms,L_Main_Loop ;1ms未到
CLR B_1ms
MOV WR6, msecond
INC WR6, #1 ;msecond + 1
MOV msecond, WR6
CMP WR6, #1000
JC L_Main_Loop ;if(msecond < 1000), jmp
MOV WR6, #0
MOV msecond, WR6 ;msecond = 0
LCALL F_RTC
LCALL F_DisplayRTC
LJMP L_Main_Loop
;========================================================================
; 函数: F_DisplayRTC
; 描述: 显示时钟子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_DisplayRTC:
PUSH B ;B入栈
MOV A, usrhour
MOV B, #10
DIV AB
MOV LED8, A
MOV LED8+1, B
MOV LED8+2, #DIS_;
MOV A, usrminute
MOV B, #10
DIV AB
MOV LED8+3, A;
MOV LED8+4, B;
MOV LED8+5, #DIS_;
MOV A, usrsecond
MOV B, #10
DIV AB
MOV LED8+6, A;
MOV LED8+7, B;
POP B ;B出栈
RET
;========================================================================
; 函数: F_RTC
; 描述: RTC演示子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_RTC:
INC usrsecond ; usrsecond + 1
MOV A, usrsecond
CLR C
SUBB A,#60
JC L_QuitRTC ; usrsecond >= 60?
MOV usrsecond, #0;
INC usrminute ; usrminute + 1
MOV A, usrminute
CLR C
SUBB A,#60
JC L_QuitRTC ; usrminute >= 60?
MOV usrminute, #0
INC usrhour ; usrhour + 1
MOV A, usrhour
CLR C
SUBB A,#24
JC L_QuitRTC ; usrhour >= 24?
MOV usrhour, #0
L_QuitRTC:
RET
; *********************** 显示相关程序 ****************************************
T_Display: ;标准字库
; 0 1 2 3 4 5 6 7 8 9 A B C D E F
DB03FH,006H,05BH,04FH,066H,06DH,07DH,007H,07FH,06FH,077H,07CH,039H,05EH,079H,071H
;black- H J K L N o P U t G Q r M y
DB000H,040H,076H,01EH,070H,038H,037H,05CH,073H,03EH,078H,03dH,067H,050H,037H,06EH
; 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
DB0BFH,086H,0DBH,0CFH,0E6H,0EDH,0FDH,087H,0FFH,0EFH,046H
T_COM:
DB001H,002H,004H,008H,010H,020H,040H,080H ; 位码
;========================================================================
; 函数: F_Send_595
; 描述: 向HC595发送一个字节子程序。
; 参数: ACC: 要发送的字节数据.
; 返回: none.
; 版本: VER1.0
; 日期: 2024-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_Send_595:
PUSH 02H ;R2入栈
MOV R2, #8
L_Send_595_Loop:
RLC A
MOV P_HC595_SER,C
SETB P_HC595_SRCLK
CLR P_HC595_SRCLK
DJNZ R2, L_Send_595_Loop
POP 02H ;R2出栈
RET
;========================================================================
; 函数: F_DisplayScan
; 描述: 显示扫描子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_DisplayScan:
PUSH DPH ;DPH入栈
PUSH DPL ;DPL入栈
PUSH 00H ;R0 入栈
MOV DPTR, #T_Display
MOV A, display_index
ADD A, #LED8
MOV R0, A
MOV A, @R0
MOVC A, @A+DPTR
LCALL F_Send_595 ;输出段码
MOV DPTR, #T_COM
MOV A, display_index
MOVC A, @A+DPTR
CPL A
LCALL F_Send_595 ;输出位码
SETB P_HC595_RCLK
CLR P_HC595_RCLK ; 锁存输出数据
INC display_index
MOV A, display_index
ANL A, #0F8H ; if(display_index >= 8)
JZ L_QuitDisplayScan
MOV display_index, #0;;8位结束回0
L_QuitDisplayScan:
POP 00H ;R0 出栈
POP DPL ;DPL出栈
POP DPH ;DPH出栈
RET
;*******************************************************************
;**************** 中断函数 ***************************************************
F_Timer0_Interrupt: ;Timer0 1ms中断函数
PUSH PSW ;PSW入栈
PUSH ACC ;ACC入栈
LCALL F_DisplayScan ; 1ms扫描显示一位
SETB B_1ms ; 1ms标志
POP ACC ;ACC出栈
POP PSW ;PSW出栈
RETI
END
乘风飞扬
发表于 2025-4-21 14:48:33
通过硬件SPI控制74HC595驱动8个共阴极数码管
C语言:
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/*---------------------------------------------------------------------*/
/*************功能说明 **************
本例程基于AI8051U为主控芯片的实验箱进行编写测试.
使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
用STC的MCU的SPI接口控制74HC595驱动8位数码管。
显示效果为: 数码时钟.
使用Timer0的16位自动重装来产生1ms节拍,程序运行于这个节拍下, 用户修改MCU主时钟频率时,自动定时于1ms.
下载时, 选择时钟 24MHZ (用户可自行修改频率).
******************************************/
#include "..\..\comm\AI8051U.h"
#include "stdio.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define MAIN_Fosc 24000000UL
//==========================================================================
#define Timer0_Reload (65536UL -(MAIN_Fosc / 1000)) //Timer 0 中断频率, 1000次/秒
#define DIS_DOT 0x20
#define DIS_BLACK 0x10
#define DIS_ 0x11
/*************本地常量声明 **************/
u8 code t_display[]={ //标准字库
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,
//black- H J K L N o P U t G Q r M y
0x00,0x40,0x76,0x1E,0x70,0x38,0x37,0x5C,0x73,0x3E,0x78,0x3d,0x67,0x50,0x37,0x6e,
0xBF,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xEF,0x46}; //0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
u8 code T_COM[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}; //位码
/*************IO口定义 **************/
sbit P_HC595_SER = P3^4; //pin 14 SER data input
sbit P_HC595_RCLK= P3^5; //pin 12 RCLk store (latch) clock
sbit P_HC595_SRCLK = P3^2; //pin 11 SRCLK Shift data clock
/*************本地变量声明 **************/
u8LED8; //显示缓冲
u8display_index;//显示位索引
bit B_1ms; //1ms标志
u8hour,minute,second;
u16 msecond;
/*************本地函数声明 **************/
void SPI_init(void);
/****************外部函数声明和外部变量声明 *****************/
/********************** 显示时钟函数 ************************/
void DisplayRTC(void)
{
if(hour >= 10)LED8 = hour / 10;
else LED8 = DIS_BLACK;
LED8 = hour % 10;
LED8 = DIS_;
LED8 = minute / 10;
LED8 = minute % 10;
LED8 = DIS_;
LED8 = second / 10;
LED8 = second % 10;
}
/********************** RTC演示函数 ************************/
void RTC(void)
{
if(++second >= 60)
{
second = 0;
if(++minute >= 60)
{
minute = 0;
if(++hour >= 24) hour = 0;
}
}
}
/******************** 主函数 **************************/
void main(void)
{
u8i;
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; //设置为准双向口
SPI_init();
T0x12 = 1;//Timer0 set as 1T, 16 bits timer auto-reload,
TH0 = (u8)(Timer0_Reload / 256);
TL0 = (u8)(Timer0_Reload % 256);
ET0 = 1; //Timer0 interrupt enable
TR0 = 1; //Tiner0 run
EA = 1; //打开总中断
display_index = 0;
hour = 11; //初始化时间值
minute = 59;
second = 58;
RTC();
DisplayRTC();
//for(i=0; i<8; i++)LED8 = DIS_BLACK; //上电消隐
for(i=0; i<8; i++)LED8 = i; //显示01234567
while(1)
{
if(B_1ms) //1ms到
{
B_1ms = 0;
if(++msecond >= 1000) //1秒到
{
// P42 = !P42;
msecond = 0;
RTC();
DisplayRTC();
}
}
}
}
/**************** SPI初始化程序 ******************/
void SPI_init(void)
{
SSIG = 1; //忽略 SS 引脚功能,使用 MSTR 确定器件是主机还是从机
SPEN = 1; //使能 SPI 功能
DORD = 0; //先发送/接收数据的高位( MSB)
MSTR = 1; //设置主机模式
CPOL = 1; //SCLK 空闲时为高电平,SCLK 的前时钟沿为下降沿,后时钟沿为上升沿
CPHA = 1; //数据在 SCLK 前时钟沿驱动,后时钟沿采样
//采用漏极开路+上拉电阻方式驱动,需要调低SPI频率才能正常与Flash通信
SPCTL = (SPCTL & ~3) | 2; //SPI 时钟频率选择, 0: 4T, 1: 8T,2: 16T,3: 2T
SPI_S1 = 1; //00: P1.4 P1.5 P1.6 P1.7, 01: P2.4 P2.5 P2.6 P2.7, 10: P4.0 P4.1 P4.2 P4.3, 11: P3.5 P3.4 P3.3 P3.2
SPI_S0 = 1;
HSCLKDIV = 0x01; //高速时钟1分频,默认2分频
P_HC595_SRCLK = 0;// set clock to low initial state
P_HC595_SER = 1;
SPIF = 1; //清SPIF标志
WCOL = 1; //清WCOL标志
}
/**************** 向HC595发送一个字节函数 ******************/
void Send_595(u8 dat)
{
P_HC595_RCLK = 0;
SPDAT = dat;
while(SPIF == 0) ;
P_HC595_RCLK = 1;
SPIF = 1; //清SPIF标志
WCOL = 1; //清WCOL标志
}
/********************** 显示扫描函数 ************************/
void DisplayScan(void)
{
Send_595(t_display]); //输出段码
Send_595(~T_COM); //输出位码
if(++display_index >= 8) display_index = 0;//8位结束回0
}
/********************** Timer0 1ms中断函数 ************************/
void timer0 (void) interrupt 1
{
DisplayScan();//1ms扫描显示一位
B_1ms = 1; //1ms标志
}
汇编:
;/*---------------------------------------------------------------------*/
;/* --- Web: www.STCAI.com ---------------------------------------------*/
;/*---------------------------------------------------------------------*/
;*************功能说明 **************
;本例程基于AI8051U为主控芯片的实验箱进行编写测试。
;使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
;data建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
;用STC的MCU的SPI接口控制74HC595驱动8位数码管。
;显示效果为: 数码时钟.
;使用Timer0的16位自动重装来产生1ms节拍,程序运行于这个节拍下, 用户修改MCU主时钟频率时,自动定时于1ms.
;下载时, 选择时钟 24MHZ (用户可自行修改频率).
;******************************************/
$include (../../comm/AI8051U.INC)
;/****************************** 用户定义宏 ***********************************/
Fosc_KHZ EQU 24000 ;24000KHZ
STACK_POIRTER EQU 0D0H ;堆栈开始地址
Timer0_Reload EQU (65536 - Fosc_KHZ); Timer 0 中断频率, 1000次/秒
DIS_DOT EQU 020H
DIS_BLACK EQU 010H
DIS_ EQU 011H
;*******************************************************************
;*******************************************************************
;*************IO口定义 **************/
P_HC595_SER BIT P3.4; //pin 14 SER data input
P_HC595_RCLK BIT P3.5; //pin 12 RCLk store (latch) clock
P_HC595_SRCLK BIT P3.2; //pin 11 SRCLK Shift data clock
;*************本地变量声明 **************/
Flag0 DATA 20H
B_1ms BIT Flag0.0 ; 1ms标志
LED8 DATA 30H ; 显示缓冲 30H ~ 37H
display_index DATA 38H ; 显示位索引
usrhour DATA 39H ;
usrminute DATA 3AH
usrsecond DATA 3BH ;
msecond DATA 3CH ; 2 byte
;*******************************************************************
;*******************************************************************
ORG 0000H ;程序复位入口,编译器自动定义到 0FF0000H 地址
LJMP F_Main
ORG 000BH ;1Timer0 interrupt
LJMP F_Timer0_Interrupt
;*******************************************************************
;*******************************************************************
;******************** 主程序 **************************/
ORG 0100H ;编译器自动定义到 0FF0100H 地址
F_Main:
MOV WTST, #00H ;设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
MOV CKCON,#00H ;提高访问XRAM速度
ORL P_SW2,#080H ;使能访问XFR
MOV P0M1, #00H ;设置为准双向口
MOV P0M0, #00H
MOV P1M1, #00H ;设置为准双向口
MOV P1M0, #00H
MOV P2M1, #00H ;设置为准双向口
MOV P2M0, #00H
MOV P3M1, #00H ;设置为准双向口
MOV P3M0, #00H
MOV P4M1, #00H ;设置为准双向口
MOV P4M0, #00H
MOV P5M1, #00H ;设置为准双向口
MOV P5M0, #00H
MOV P6M1, #00H ;设置为准双向口
MOV P6M0, #00H
MOV P7M1, #00H ;设置为准双向口
MOV P7M0, #00H
MOV SP, #STACK_POIRTER
MOV PSW, #0
USING 0 ;选择第0组R0~R7
;================= 用户初始化程序 ====================================
MOV display_index, #0
MOV R0, #LED8
MOV R2, #8
L_ClearLoop:
MOV @R0, #DIS_BLACK ;上电消隐
INC R0
DJNZ R2, L_ClearLoop
MOV SPCTL,#0DEH
ORL P_SW1, #(3 SHL 2) ; 0: P1.4 P1.5 P1.6 P1.7, 1: P2.4 P2.5 P2.6 P2.7, 2: P4.0 P4.1 P4.2 P4.3, 3: P3.5 P3.4 P3.3 P3.2
MOV SPSTAT,#0C0H ; Clear SPIF,WCOL
CLR TR0
ORL AUXR, #(1 SHL 7) ; Timer0_1T();
ANL TMOD, #NOT 04H ; Timer0_AsTimer();
ANL TMOD, #NOT 03H ; Timer0_16bitAutoReload();
MOV TH0, #Timer0_Reload / 256 ;Timer0_Load(Timer0_Reload);
MOV TL0, #Timer0_Reload MOD 256
SETB ET0 ; Timer0_InterruptEnable();
SETB TR0 ; Timer0_Run();
SETB EA ; 打开总中断
MOV usrhour, #12 ; 初始化时间值
MOV usrminute, #0
MOV usrsecond, #0
LCALL F_DisplayRTC
;=================== 主循环 ==================================
L_Main_Loop:
JNB B_1ms,L_Main_Loop ;1ms未到
CLR B_1ms
MOV WR6, msecond
INC WR6, #1 ;msecond + 1
MOV msecond, WR6
CMP WR6, #1000
JC L_Main_Loop ;if(msecond < 1000), jmp
MOV WR6, #0
MOV msecond, WR6 ;msecond = 0
LCALL F_RTC
LCALL F_DisplayRTC
LJMP L_Main_Loop
;========================================================================
; 函数: F_DisplayRTC
; 描述: 显示时钟子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_DisplayRTC:
PUSH B ;B入栈
MOV A, usrhour
MOV B, #10
DIV AB
MOV LED8, A
MOV LED8+1, B
MOV LED8+2, #DIS_;
MOV A, usrminute
MOV B, #10
DIV AB
MOV LED8+3, A;
MOV LED8+4, B;
MOV LED8+5, #DIS_;
MOV A, usrsecond
MOV B, #10
DIV AB
MOV LED8+6, A;
MOV LED8+7, B;
POP B ;B出栈
RET
;========================================================================
; 函数: F_RTC
; 描述: RTC演示子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_RTC:
INC usrsecond ; usrsecond + 1
MOV A, usrsecond
CLR C
SUBB A,#60
JC L_QuitRTC ; usrsecond >= 60?
MOV usrsecond, #0;
INC usrminute ; usrminute + 1
MOV A, usrminute
CLR C
SUBB A,#60
JC L_QuitRTC ; usrminute >= 60?
MOV usrminute, #0
INC usrhour ; usrhour + 1
MOV A, usrhour
CLR C
SUBB A,#24
JC L_QuitRTC ; usrhour >= 24?
MOV usrhour, #0
L_QuitRTC:
RET
; *********************** 显示相关程序 ****************************************
T_Display: ;标准字库
; 0 1 2 3 4 5 6 7 8 9 A B C D E F
DB03FH,006H,05BH,04FH,066H,06DH,07DH,007H,07FH,06FH,077H,07CH,039H,05EH,079H,071H
;black- H J K L N o P U t G Q r M y
DB000H,040H,076H,01EH,070H,038H,037H,05CH,073H,03EH,078H,03dH,067H,050H,037H,06EH
; 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. -1
DB0BFH,086H,0DBH,0CFH,0E6H,0EDH,0FDH,087H,0FFH,0EFH,046H
T_COM:
DB001H,002H,004H,008H,010H,020H,040H,080H ; 位码
;========================================================================
; 函数: F_Send_595
; 描述: 向HC595发送一个字节子程序。
; 参数: ACC: 要发送的字节数据.
; 返回: none.
; 版本: VER1.0
; 日期: 2024-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_Send_595:
CLR P_HC595_RCLK
MOV SPDAT,A
L_Wait:
JNB SPIF, L_Wait
CLR SPIF
CLR WCOL
SETB P_HC595_RCLK
RET
;========================================================================
; 函数: F_DisplayScan
; 描述: 显示扫描子程序。
; 参数: none.
; 返回: none.
; 版本: VER1.0
; 日期: 2013-4-1
; 备注: 除了ACCC和PSW外, 所用到的通用寄存器都入栈
;========================================================================
F_DisplayScan:
PUSH DPH ;DPH入栈
PUSH DPL ;DPL入栈
PUSH 00H ;R0 入栈
MOV DPTR, #T_Display
MOV A, display_index
ADD A, #LED8
MOV R0, A
MOV A, @R0
MOVC A, @A+DPTR
LCALL F_Send_595 ;输出段码
MOV DPTR, #T_COM
MOV A, display_index
MOVC A, @A+DPTR
CPL A
LCALL F_Send_595 ;输出位码
INC display_index
MOV A, display_index
ANL A, #0F8H ; if(display_index >= 8)
JZ L_QuitDisplayScan
MOV display_index, #0;;8位结束回0
L_QuitDisplayScan:
POP 00H ;R0 出栈
POP DPL ;DPL出栈
POP DPH ;DPH出栈
RET
;**************** 中断函数 ***************************************************
F_Timer0_Interrupt: ;Timer0 1ms中断函数
PUSH PSW ;PSW入栈
PUSH ACC ;ACC入栈
LCALL F_DisplayScan ; 1ms扫描显示一位
SETB B_1ms ; 1ms标志
POP ACC ;ACC出栈
POP PSW ;PSW出栈
RETI
;*******************************************************************
END
zhp
发表于 2025-4-23 12:01:41
深圳大学 Ai8051U《单片机原理及应用》实验课,大型综合实战:
定时器做RTC,按键(去抖,长按,短按),发声
实验目的:
1、本项目主要是帮助同学们熟悉如何管理 多文件项目
2、熟悉矩阵式按键扫描方法,含去抖动,短按,长按
3、了解使用74HC595控制 LED数码管
4、普通定时器软件模拟RTC,实现RTC的 时、分、秒控制
5、I/O控制无源蜂鸣器发声,
按键 Key0 ~ Key7 按下时,发出对应的 Do/Re/Mi/Fa/So/La/Si/Do音符
6、扩展部分:如何实现长按键
实验现象:
将本项目的HEX文件下载到Ai8051U的实验箱后
1、在LED数码管上面低6位会动态显示当前的RTC时间
2、按下两行4列的8个矩阵按键后
在LED数码管的最高位会显示当前的按键键码,并会发出对应的声音
同时会根据不同的按键发出不同按键音
(特别注意:需要将实验箱上“跳线3:蜂鸣器”的跳线连上,蜂鸣器才会发声)
3、矩阵按键中的按键0,是RTC模式设置键
上电后RTC为正常工作模式,此时数码管动态显示RTC时间
按一次按键0,RTC会切换为小时设置模式,此时RTC的小时值会0.5秒闪烁一次
再按一次按键0,RTC会切换为分钟设置模式,此时RTC的分钟值会0.5秒闪烁一次
再按一次按键0,RTC会切换为秒钟设置模式,此时RTC的秒钟值会0.5秒闪烁一次
在按一次按键0,恢复为RTC正常工作模式
4、矩阵按键中的按键4和按键5分别是上调键和下调键
只有在RTC的时/分/秒设置模式式,上调键和下调键才有效
5、时钟设置模式时,长按按键4和按键5,可实现快速设置功能
神农鼎
发表于 2025-4-23 13:22:45
sbk100
发表于 2025-4-23 14:03:23
深圳大学的单片机水平这么强吗
神农鼎
发表于 2025-4-23 19:37:08
sbk100 发表于 2025-4-23 14:03
深圳大学的单片机水平这么强吗
深圳大学,代表我们深圳的水平,当然可以
深圳,全球 科技 中心
神农鼎
发表于 2025-4-24 10:55:00
74HC595资料,使用说明
74HC595_百度百科