gentleman
发表于 2023-8-14 10:22:57
本帖最后由 gentleman 于 2023-8-14 10:24 编辑
2023/08/08 第十集
汇编一般步骤和基本框架
规范 有序正确
一般步骤
分析 确定算法
画流程图
分配资源 RAMTIMERUART等
目前主要时外设资源/IO分配问题 ,最好列个表
写程序
.asm
调试
软件模拟
在线调试
有些问题必须要硬件仿真才能展现
软仿永远跟不上硬件的发展
模块化编程
框架
建议不用的中断放复位
可以提高系统的可靠性
程序流程图
典型的例子
分支
JZJNZCJNEDJNZ
JCJNCJBJNBJBC
自己尝试写一下
$NOMOD51
$INCLUDE (STC8H.H)
ORG 0000H
LJMP MAIN
ORG 0003H
MAIN:
MOV SP,#80H
MOV R0,#10; 给R0赋值20H
CLR C
CJNE R0,#37,CMP
MOV R1,0
CMP:
JNC BIG
MOV R1,#0FFH; -1补码0xff
RET
BIG:
MOV R1,#1
END
给R0 赋值10 10<37 得出结果为+1 存入寄存器R1中
老师的例程中使用EQU 定义了SIGN 这个我没有考虑到
老师的例程
查表
MOVCA,@A+DPTR查找
MOVDPTR,#SEGTAB ;移动倒字模表首地址
MOVA,@A+DPTR ;查表
SEGTAB:DB 0C0H ;子模表
DB 0F9H
.
.
.
DB 90H
循环
延时
参考stc-isp 生成
PUSH/POP 保护/释放 30H/31H
DJNZ双重循环
定点数
BCD 减法9AH(100)减去 减数
多字节乘法
数据排序
冒泡排序
代码转换
A/DD/A BCD 转ascii
4BIT二进制转ASCII
<10+ 30H
>10+ 37H
bcd 转二进制
低位 R1 个/十
高位 R2 百/千
汇编的课程到此应该结束了
正如老师所说,目前只是掌握了如何去学汇编,而不是掌握了汇编。后面复杂的例程,目前我是无法完成编写的。
汇编语言的重要性老师都强调过。不管以后是否会用到,感觉还是有必要掌握的。
汇编的价值不仅仅在程序设计上,更多的是对于底层原理的理解。
想用单片机,可以不学汇编。想懂单片机,汇编是不可或缺的。
不管是兼容8051 还是80251 还是arm 指令集(或者哪一天国人搞了一套自研的指令集)单片机的学习,你使用汇编对其底层进行分析,都可以加快对其的理解于掌握。
gentleman
发表于 2023-8-16 07:18:02
本帖最后由 gentleman 于 2023-8-16 10:30 编辑
2023/08/14第十一集
c的内容就轻松多了,相信各位都是c语言大神{:4_165:}
C51 对ANSI C的扩展
加了19个关键字 常用的有_at_ (这个常用吗?)
sbit 常用于定义引脚
sfr 特殊功能寄存器
bit 位变量
xdata 拓展RAM
code code FLASH 区域
interrupt 中断
变量类型
新增bit 位型 1bit 0/1
sbit 1 0/1
sfr 8 0x80~0xff
sfr16 8 0x80~0xff
bit 不能声明指针 不能做数组
sbit 在可独立访问位寻址的位
sfr 用来控制定时/计数器 串口 io
sfr P0=0x80;
sbit cy=psw^7
内存区域的制定
code常数,可用于保存数码管码表
RAM
data 直接寻址低 128 BYTE
idata间接寻址RAM 256 BYTE
bdata位寻址 20H~FH
外部数据存储器
xdata外部RAM 大数组可用,
pdata 别用!!! 8032时代选页的,pdata与xdata有冲突
指针
一般和ANSI C一样
很重要,很灵活
硬件相关寄存器指针需要使用volatile 保证不被优化
例
#define P0PU (*(unsigned char volatile xdata *)0xfe10)
中断
声明
void UART_ISR(void) interrupt 4
{
//中断服务程序代码
} 使用中断号 说明哪个中断
加using 指定工作寄存器区
加small/large 说明存储模式
c的运算
算数运算
逻辑运算
&&||!
不太认同老师说的 !不常用,感觉!还是很常用的,我经常写 if(!a) 而不是 if(a==0);
位运算符
& |^~ << >>
&类似 ANL
| 常用于 给莫一位置1psw2 |=0x80最高位置1
<< RLC
>> RRC
STC8H C51 框架
#inlcude "stc8h.h"
void delay(long delaytime)
void main(void)
{
while(1)
{
delay(100);
}
}
gentleman
发表于 2023-8-16 10:29:39
2023/08/14 第十二集
c语言程序设计实例
例1 闪灯程序
stc8h.h 没必要拷贝
#include <stc8h.h>
void Delay500ms();
void main(void)
{
P4M0= 0x00;
P4M1= 0x00;
P6M0= 0x00;
P6M1= 0x00;
P4 = 0;
while(1)
{
P60=1;
Delay500ms();
P60=1;
Delay500ms();
}
}
void Delay500ms() //@11.0592MHz
{
unsigned char data i, j, k;
i = 29;
j = 14;
k = 54;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
注意 这里do while在编译器被翻译为DJNE 便于计算循环次数
while 被翻译为 CJNE于 SJMP 由于存在 是否跳转的问题不容易计算
使用 data 强制定义数据区域,避免编译器设为large 等影响延时时间
使用移位可以简单的实现流水灯/跑马灯
定义变量
LEDdata = 0x01;
在主循环中左移
LEDdata <<= 1;
判断
if(!LEDdata)
LEDdata = 0x01;
给IO赋值
P6 = LEDdata;
例2 flash操作
#include <stc8h.h>
#include "intrins.h"
void IapIdle()
{
IAP_CONTR = 0;
IAP_CMD = 0;
IAP_TRIG = 0;
IAP_ADDRH = 0x80;
IAP_ADDRL = 0;
}
char IapRead(int addr)
{
char dat;
IAP_CONTR = 0x80; //使能IAP
IAP_CMD = 1; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
dat = IAP_DATA; //读ISP/IAP/EEPROM数据
IapIdle(); //关闭IAP功能
return dat; //返回
}
void IapProgram(int addr, char dat)
{
IAP_CONTR = 0x80; //使能IAP
IAP_CMD = 2; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_DATA = dat; //写ISP/IAP/EEPROM数据
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
void IapErase(int addr)
{
IAP_CONTR = 0x80; //使能IAP
IAP_CMD = 3; //设置IAP命令
IAP_ADDRL = addr; //设置IAP低地址
IAP_ADDRH = addr >> 8; //设置IAP高地址
IAP_TRIG = 0x5a; //写触发命令(0x5a)
IAP_TRIG = 0xa5; //写触发命令(0xa5)
_nop_(); //等待ISP/IAP/EEPROM操作完成
IapIdle();
}
void main()
{
unsigned char dataread=0,datawrite=0;
IapErase(0x0400);
dataread = IapRead(0x400);
IapProgram(0x400,0x12);
datawrite = IapRead(0x400);
while(1);
}
基本的eeprom 操作,在需要掉电存储的场合下很常用。 而且不需要外挂24/25系列存储器,易用性于安全性都有很大提升
放在这里应该是让大家熟悉一下寄存器的操作。
记得写eeprom之前要先擦除,每次擦一个扇区。
这里没有判断LVDS,有些场合需要判断,保证低压时不要进行eeprom操作。
gentleman
发表于 2023-8-21 06:56:18
2023/08/16 第十三集
中断
单片机执行其他程序 , 出现某些异常事件或请求,cpu 中止,转去执行异常事件,执行完毕,返回继续执行。
中断源
cpu中断请求源
中断响应
cpu暂停工作转去处理中断源事件
中断服务
处理工程
中断返回
处理完返回中断的地方
中断优先级
低优先级会挂起LCALL
保护现场
PUSHPOP 指令 堆栈操作实现
堆栈
先进后出
SP 指向栈顶
中断撤除
中断请求标志位擦除,有的会自己擦除
中断源
stc8h44个
注意 中断号是keil 的c51
查询次序是8051的 先有查询次序
中断标志位
TCON
IT0 触发方式 0上升/下降沿触发
1 仅下降沿触发
IE0 INT0 请求标志
IT1 INT1 触发方式
IE1 INT1请求标志
AUXINTIF
管定时器2~4的
所有定时器外部中断标志位自动清0
串口
TI/(SX)TI RI/(SX)RI
不能自动请0
可以直接写TI=0 ,因为地址可以被8整除
gentleman
发表于 2023-8-21 07:16:49
本帖最后由 gentleman 于 2023-8-21 07:48 编辑
2023/08/16 第十四集
中断标志位
PCON
LVDF 低电压检测 不开中断也可以读
SPI
SPSTAT 传输完成置位
ADC
ADC_CONTR A/D转换结束标志位
需要软件清0
中断允许,禁止,优先级
IE 0A8H
EA总中断
0关闭
1开放
ELVD 低压检测
EADC A/D
ES 串口1
ET1 定时器1
EX1 INT1
ET0 T0
EX0 INT0
优先级
00 01 1011对应1234 级
先高后低 停低转高 高不睬低
即使关闭EA/分中断开关 标志位也改变,只是不响应,可以查
RETI 不能写成RET 结果只执行一次
不响应中断的情况
EA/ 不开不响应
cpu执行更高一级的中断
执行的机器周期不是指令周期的最后一个机器周期
执行的是RETI 或 访问IP/IPcpu再执行一条指令才响应中断
终端服务
入口 到 RETI
保护现场 终端服务恢复现场中断返回
中断可以临时关。避免高优先级中断
实例:通过INT0 改变灯状态
基于原来的点灯程序修改
IT0 =1;
EX0 =1;
EA=1;
这样中断1和总中断就打开了
再写个中断函数,把引脚取反
void INT0_ISR interrupt中断号忘了,好像是0
{
P60=~P60;
}
INT0 放个小电阻 100R或者300R 保护一下
用汇编写要在头部写好外部中断的跳转地址
ORG 0003H
LJMP INT0_ISR
给开关置0,选触发模式
SETB IT0
SETB EX0
SETB EA
在最后写上中断子程序
INT0_ISR:
CPI P6.0
RETI
END
中断在程序设计中是很常用的。基本稍微复杂一点的程序就会用到。
如果没有中断,cpu就只能靠轮询。对程序设计的影响还是很大的。
stc8/32 相对于传统8051添加了很多中断,更加简化了对程序的设计。
比如原来的定时器就经常不够用,要用其他的方式去处理,stc的有4~5个,还能自动重载,随便用。
gentleman
发表于 2023-8-27 08:38:40
2023/08/27 第 15 集
定时器结构
功能 : 定时或计数
分频
可编程输出
串口波特率发生器
一般结构
系统/外部来的时钟 让计数器加1 加满了就中断
工作模式 4种
常用16位自动重载/不可中断的16位自动重载
下图是模式0 16位自动重载
非常清晰,图看懂了,就会用定时器了。
T2 模式固定 16位自动重载
寄存器不用背 会查就行
定时器寄存器
TMOD
GATEC/TM1M0 GATEC/T M1M0
M1M0
0 0 模式0 16位自动重载
1 1 模式3 最高中断优先级的16位自动重载
记住这两个模式就行
C/T 1 计数器
0 定时器
GATE 1~INTn搞 且 TRn置位 启动
0 TRn 置位 启动
TCON
TF1TR1 TF0TR0IE1IT1IE0IT0
TF1/0 中断标志位 自动清
TR1/0 开关 置1启动
后面4位是外部中断的
AUXR
T0x12T1x12UART_M0x6T2RT2_C/TT2x12EXTRAMS1T2
T0x12/T1x12 0 12分频
1 不分频
TR2 定时器 2开关 置1启动
T2_C/T 0T2 12个时钟计数1次
1 T21个时钟计数1次
UART_M0x6 串口速度
S1ST2 波特率发生器选择位
T4T3M
T4RT4_C/T T4x12T4CLKOT3RT3_C/TT3x12T3CLKO
T4/T3CLKO时钟输出开关置1开启
其他参考上面
gentleman
发表于 2023-8-27 08:58:43
2023/08/21第16集
定时器应用
最大能力
fosc = 11.0592m
12分频
1计数周期 = 12/晶振频率-12/110592 =1us
N = M-TC/TP
TC/TP = 1000;
T = 71.111ms
T2/T4
T = 18s
量程拓展
一般用软件
多次中断后处理一次时间就行
例程
汇编例程看的懂就好,也不太复杂
后面再尝试写。
c:
void Timer0_Isr(void) interrupt 1
{
i--;
if(!i)
{
P60 = ~P60;
i = 0;
}
}
void Timer0_Init(void) //50微秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xD7; //设置定时初始值
TH0 = 0xFD; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA = 1;
}
然后再main 调用初始化函数 就行了
记得设置
PXMX = 0x00;
P40 =0;
运行程序就会看到 试验箱的灯在闪 500ms 亮一次
后面还有操作其他定时器的例程
注意T2 T3 T4 的许多寄存器都不能位寻址
定时器是很常用的应用,大家一定要多多练习
gentleman
发表于 2023-9-3 11:49:30
2023/08/23第十七集
STC8H8K64U
4 UART
1 SPI
1 I2C
1 USB
并行 -同时传送
串行 -一位移位传脉冲
TTL<10M
RS232 15M
RS485 100M多机
异步通讯0/1 作为字符标志
约定编码形式 奇偶校验 起始位停止位
通讯速率9600,n,8,1
单工/半双工 / 全双工
串行接口的内部结构
时序图
stc补充多机通信
gentleman
发表于 2023-9-3 11:58:19
2023/08/23 第十八集
STC8H8K64U
4个串口全双工 支持DMA
两个SBUF独立
SFR
SCON
常用模式1 3
工作模式1示意图
可以由定时器作为波特率发生器
gentleman
发表于 2023-9-3 12:09:11
2023/08/28 第十九集
使用定时器作为波特率发生器时要 TR1/TR2=1
中断不用开
常用stc-isp 工具计算定时器初值
实时任务需要 中断
编程要点
SCON 设置
TMOD/SMOD 设置
ES/EA 设置
data -> SBUF
清TR/RI 编程序
其他串口类似,注意寄存器不同
流程图
发送
接收
注意波特率要相同,才能进行通信
多机通信
SM2 标志位区分
流程图
注意TB8=1 发地址
发完清TB8