51单片机封装库HML_FwLib_STC89/STC11
HML_FwLib_STC89/11
项目地址
- https://github.com/MCU-ZHISHAN-IoT/HML_FwLib_STC89
- https://github.com/MCU-ZHISHAN-IoT/HML_FwLib_STC11
这些项目主要是封装了8051和STC89, STC11的寄存器配置信息, 提供接口方法给上层调用. 因为传统的代码都是直接用八进制值给寄存器赋值进行操作, 不便于记忆, 用这个封装库就可以使用类似于STM的高级语言方式进行开发, 解决了开发过程极度依赖手册的问题. 如果使用STC12C5A60S2系列, 可以用STC11封装库.
目录结构
HML_FwLib_STC89
├─doc # store related documents about HML_FwLib_STC89
├─example # 示例代码, 每个c文件代表一个功能示例
├─inc # 头文件, 里面 macro.h 有这个库支持的芯片型号列表
├─obj # 编译输出
├─src # 这个库的c文件, 有部分代码是用汇编写的
├─usr # 这个目录用于存储用户代码, 以及Makefile, 项目带了一个很完整的示例
├─LICENSE # license of HML_FwLib_STC89
└─VERSION # version code of HML_FwLib_STC89
这个库实现的类STM32的方法定义, 都在inc/hml/下的.h文件中, 可以对照参考.
快速开始
前置条件
- 首先要安装编译器, sdcc, Ubuntu20.04自带的为3.8.0, 最新的为4.1.0, 可以自己编译安装
执行sdcc --print-search-dirs查看库文件的位置. 默认的8051的库文件位置为/usr/local/share/sdcc/include/mcs51/ - 安装烧录工具, stcgal, 直接用
pip3 install stcgal安装然后执行stcgal -h查看输出
使用
- 将代码clone到本地后, cd进入usr目录
- 执行
make help查看帮助, 这里有很详细的说明 - 执行
make -j编译
如果过程没问题, 你应当看到这样的输出
somewhere:~$ make -j
- Collect MCU config information
[mcu-model] STC89C52RC (code=8192B, iram=256B, xram=256B)
[mcu-clock] 11.059200 MHz
[prescaler] 12T mode
- Start to build!
CC ../src/mem.c
CC ../src/tim.c
CC ../src/exti.c
CC ../src/uart.c
CC ../src/gpio.c
CC ../src/rst.c
CC ../src/util.c
CC ../src/isp.c
CC ../src/tim2.c
CC ../src/wdt.c
- Make static link library libhml_stc89.lib
AR ../obj/libhml_stc89.lib
- Compile user source code
CC test.c
- Generate .ihx file
CC ../obj/output.ihx
- Generate .hex file
packihx: read 89 lines, wrote 160: OK.
===================================================
Make done!
---------------------------------------------------
completed at 2021-08-06 14:18:35
===================================================
如果需要使用其他编译选项, 例如指定芯片型号, 可以用
make -j MCU=stc89c52rc"
make rebuild MCU=stc89c52rc JOBS=4
用stcgal烧录
stcgal -P stc89 -b 115200 output.ihx
Makefile 分析
GNU Make 使用手册: https://www.gnu.org/software/make/manual/
在用户代码目录里有多个Makefile相关文件
Makefile # make命令入口文件
Makefile.config # 默认的make配置, 如果make时指定了CONF参数, 则使用CONF对应的配置文件
Makefile.help
Makefile.mcu
Makefile.version
分析一下主文件Makefile
#!/usr/bin/make
# ------------------------------------------------------------------------
# Author : Weilun Fong | wlf@zhishan-iot.tk
# Date : 2020-02-06
# Description: project Makefile
# E-mail : mcu@zhishan-iot.tk
# Make-tool : GNU Make (http://www.gnu.org/software/make/manual/make.html)
# Page : https://hw.zhishan-iot.tk/page/hml/detail/fwlib_stc89.html
# Project : HML_FwLib_STC89
# Version : v0.3.1
# ------------------------------------------------------------------------
# Package Bash shell command 统一基础命令, 可以复用
export SHELL := /bin/bash
export AWK := awk
export BASENAME := basename
export CAT := cat
export CD := cd
export DATE := date
export ECHO := echo
export EECHO := $(ECHO) -e
export GREP := grep
export LS := ls
export RM := rm -f
export TR := tr
export TRUE := true
export XARGS := xargs
# Definition of toolchain 统一编译工具, 可以复用
CROSS_COMPILE := sd
AR := $(CROSS_COMPILE)ar
CC := $(CROSS_COMPILE)cc
MAKE := make --no-print-directory
PACKIHX := packihx
# Mark special phony targets 定义编译目标, 可以复用
PHONY_LIST_IN := clean distclean help rebuild version
# Definition of project basic path 项目路径定义, 可以复用
DIR_ROOT := ..
DIR_INC := $(DIR_ROOT)/inc
DIR_OUTPUT := $(DIR_ROOT)/obj
DIR_SRC := $(DIR_ROOT)/src
# Configure all custom parameters 这里处理自定义参数 CONF,
SPACE := $(empty) $(empty)
TITLE_COLOR := \033[36m
ifeq ($(findstring $(MAKECMDGOALS), $(PHONY_LIST_IN)),) # 符合条件的才去包含,
# 如果不在 PHONY_LIST_IN 中, 后面会提示 *** No rule to make target
ifneq ($(CONF),) # 如果 CONF 不为空则包含自定义的文件, 这里第二个参数为空值
include $(CONF)
else
include Makefile.config
endif
include Makefile.mcu # 这里包含了芯片型号对应的定义
endif
# Definition of of print format 定义输出, 如果VERBOSE为1则输出命令, 且输出提示
ifeq ("$(VERBOSE)", "1")
Q :=
VECHO := @$(TRUE)
else
Q := @
VECHO := @$(ECHO)
endif
# Important file 组织编译中的目标文件, 输入文件, 输入参数等
FILE_HML_FWLIB := libhml_stc89.lib # 这是最后生成的库文件名称
HML_SRC_FILES := $(wildcard $(DIR_SRC)/*.c) # wildcard会列出符合这个文件名格式的文件, 产生一个以空格分隔的字符串列表.
HML_REL_FILES := $(patsubst $(DIR_SRC)/%.c, $(DIR_OUTPUT)/%.rel, $(HML_SRC_FILES)) # patsubst 在字符串中替换匹配的串
# 将c文件名列表变为rel文件列表
MYFILE ?= test.c # := 是覆盖之前的值, ?= 是如果没有被赋值过就赋予等号后面的值, += 是添加等号后面的值
# file check
ifeq ($(findstring $(MAKECMDGOALS),$(PHONY_LIST_IN)),)
ifneq ($(wildcard $(MYFILE)),$(MYFILE))
$(error no such file $(CURDIR)/$(MYFILE))
endif
endif
MYFILE_NAME := $(shell $(BASENAME) $(MYFILE) .c) # 取出MYFILE不带扩展名的文件名
MYFILE_REL := $(DIR_OUTPUT)/$(MYFILE_NAME).rel
# Target file
TARGET := output # 编译输出结果文件名
TARGET_FWLIB := $(DIR_OUTPUT)/$(FILE_HML_FWLIB)
all: $(DIR_OUTPUT)/$(TARGET).hex
@$(ECHO) ===================================================
@$(ECHO) Make $(MAKECMDGOALS) done!
@$(ECHO) ---------------------------------------------------
@$(ECHO) completed at `$(DATE) "+%Y-%m-%d %H:%M:%S"`
@$(ECHO) ===================================================
# Startup
startup:
@$(EECHO) "$(TITLE_COLOR) - Start to build!\033[0m"
# 下面的格式就是标准的Makefile编译
# 目标 : 需要的文件
# CC命令
# 自动变量, 参考 https://www.gnu.org/software/make/manual/make.html#Automatic-Variables
# ‘$<’ 第一个需要的文件名
# ‘$^‘ 所有需要的文件名, 用空格分隔
# ‘$@’ 目标文件名
# Compile HML source file(*.c) 先编译HML源文件
$(HML_SRC_FILES): startup
$(HML_REL_FILES): $(DIR_OUTPUT)/%.rel:$(DIR_SRC)/%.c
$(VECHO) "CC $<"
$(Q)$(CC) $< $(CFLAGS) -o $@
# Generate static library
$(TARGET_FWLIB): $(HML_REL_FILES)
@$(EECHO) "$(TITLE_COLOR) - Make static link library `basename $@` \033[0m"
$(VECHO) "AR $@"
$(Q)$(AR) $(AFLAGS) $@ $^
# Compile user file
$(MYFILE_REL): $(MYFILE) $(TARGET_FWLIB)
@$(EECHO) "$(TITLE_COLOR) - Compile user source code \033[0m"
$(VECHO) "CC $<"
$(Q)$(CC) $< $(CFLAGS) -L$(DIR_OUTPUT) -lhml_stc89 -o $(DIR_OUTPUT)/`$(BASENAME) $@`
# Generate .hex file
$(DIR_OUTPUT)/$(TARGET).ihx: $(MYFILE_REL)
@$(EECHO) "$(TITLE_COLOR) - Generate .ihx file \033[0m"
$(VECHO) "CC $@"
$(Q)$(CC) $^ $(DIR_OUTPUT)/$(FILE_HML_FWLIB) -o $@
$(DIR_OUTPUT)/$(TARGET).hex: $(DIR_OUTPUT)/$(TARGET).ihx
@$(EECHO) "\033[36m - Generate .hex file \033[0m"
$(Q)$(PACKIHX) $< > $@
# Phony targets 定义虚目标
# .PHONY用于定义一个虚目标. 正常情况, 按上面的格式, 冒号前面是编译中间产生的文件, 如果这个文件不存在, 那么这个编译在执行
# make的时候就会被执行, 比如下面的clean, 如果目录里没有clean这个文件, 那么每次都会执行这个clean. 而另一个问题就是如果
# 目录中已经有一个clean了, 那么在make clean的时候实际上就会不执行. 将其定义为.PHONY就可以避免这个问题, 格式:
# .PHONY: clean
# clean:
# rm *.o temp
# [+] clean
.PHONY: clean
clean:
$(CD) $(DIR_OUTPUT) && $(LS) | $(GREP) -vE -e ".gitkeep" -e ^$(MYFILE_NAME)* -e *.lib$$ -e *.hex$$ | $(XARGS) $(RM)
# [+] distclean
.PHONY: distclean
distclean:
$(CD) $(DIR_OUTPUT) && $(LS) | $(GREP) -v ".gitkeep" | $(XARGS) $(RM)
# [+] help
.PHONY: help
help:
@$(MAKE) -s -f Makefile.help
# [+] library
.PHONY: library
library: $(TARGET_FWLIB)
@$(ECHO) ===================================================
@$(ECHO) Make $(MAKECMDGOALS) done!
@$(ECHO) ---------------------------------------------------
@$(ECHO) completed at `$(DATE) "+%Y-%m-%d %H:%M:%S"`
@$(ECHO) ===================================================
# [+] rebuild
.PHONY: rebuild
rebuild:
@$(EECHO) "$(TITLE_COLOR) - Clean previous files \033[0m"
@$(MAKE) distclean
@$(MAKE) -f Makefile -j$(JOBS) $(MAKEFLAGS)
# [+] version
.PHONY: version
version:
@$(MAKE) -s -f Makefile.version
代码分析
src/util.c
这里混合了汇编语言和C语言代码
void sleep(uint16_t t)在ASM中调用了前面的两个函数lcall __sleep_getInitValue,lcall __sleep_1ms, 注意这里的函数名相对于C语言的函数, 前面都增加了下划线.传参使用DPL和DPH, 以及B和ACC, 根据SDCC用户参考 http://sdcc.sourceforge.net/doc/sdccman.pdf 其中的
Notes on supported Processors->MCS51 variants->Interfacing with Assembler Code, The compiler always uses the global registers DPL, DPH, B and ACC to pass the first (non-bit) parameter to a function, and also to pass the return value of function; according to the following scheme: one byte return value in DPL, two byte value in DPL (LSB) and DPH (MSB). three byte values (generic pointers) in DPH, DPL and B, and four byte values in DPH, DPL, B and ACC.
代码
/*****************************************************************************/
/**
* \author Qiyuan Chen & Jiabin Hsu
* \date 2020/01/28
* \brief get _sleep_1ms initial value
* \param[in] none
* \return none
* \ingroup UTIL
* \remarks private function, don' use it
******************************************************************************/
uint16_t _sleep_getInitValue(void)
{
return (uint16_t)(MCU_FRE_CLK/(float)12000/8) - 2;
}
/*****************************************************************************/
/**
* \author Qiyuan Chen
* \date 2020/01/28
* \brief sleep 1 ms
* \param[in] none
* \return none
* \ingroup UTIL
* \remarks private function, don' use it
******************************************************************************/
void _sleep_1ms(void)
{
__asm
mov ar5, r6 ;#2
delay1ms_loop$:
nop ;#1
nop ;#1
nop ;#1
nop ;#1
nop ;#1
nop ;#1
djnz r5, delay1ms_loop$ ;#2
ret ;#2
__endasm;
}
/*****************************************************************************/
/**
* \author Jiabin Hsu
* \date 2020/01/28
* \brief software delay according to MCU clock frequency
* \param[in] t: how many one ms you want to delay
* \return none
* \ingroup UTIL
* \remarks
******************************************************************************/
void sleep(uint16_t t)
{
__asm
push ar5 ; 当前ar5,ar6,ar7的值入栈保存
push ar6
push ar7
push dph ; 将入参入栈保存
push dpl
; freq -> r6,r7
lcall __sleep_getInitValue ; 根据当前的频率, 计算得到1ms对应的周期数
mov ar6,dpl ; 将结果赋值给ar6, ar7
mov ar7,dph
; t -> dptr
pop dpl ; 恢复入参到dpl,dph
pop dph
; 0xFFFF - t
clr c ; 清理借位进位 carry
mov a,#0xFF ; 带借位的减法, 用ff减去dpl, 然后将结果存入dpl
subb a,dpl
mov dpl,a
mov a,#0xFF ; 带借位的减法, 用ff减去dph, 结果存入dph
subb a,dph
mov dph,a
; return if time equals 0
mov a,dpl ; dpl存入a
anl a,dph ; 与dph按位做AND运算
cpl a ; 按位进行取反,原先是1就变为0,原先是0就变为1
jz ENDL$ ; 为0则跳到结束
; 对上面的代码分析一下:
; 先用FFFF减去入参, 只要入参不为0, 那么产生的16个bit中一定会有0
; 将上下8位做与运算, 一定会把0保留
; 再按位取反, 一定会带1, 所以一定不为0
; loop for sleep
; loop from (0xFFFF - t) to (0xFFFF)
LOOP$:
lcall __sleep_1ms ;#8*(frep/12000) - 10
inc dptr ;#2 在上面的操作之后, dptr里存的就是 ffff减去原入参的值
mov a,dpl ;#1 每次循环加1之后, 做一次判断是否为0, 如果不为0则继续循环
anl a,dph ;#1 这样循环的次数就是入参的值
cpl a ;#1
nop ;#1
nop ;#1
nop ;#1
jnz LOOP$ ;#2
ENDL$:
pop ar7
pop ar6
pop ar5
ret
__endasm;
/**
* \note disable SDCC warning
*/
t = 0;
}
HML_FwLib_STC11项目下的util.c代码和STC89是一样的, 在1T模式下, 时钟速度明显快很多, 需要做一些调整, 如果是11.0592晶振, 可以将_sleep_1ms方法替换为
void _sleep_1ms(void)
{
__asm
nop
nop
nop
nop
push ar5
push ar6
mov ar5,#9
mov ar6,#148
NEXT:
djnz ar6,NEXT
djnz ar5,NEXT
pop ar6
pop ar5
ret
__endasm;
}
这样延迟就明显准确多了. 因为晶振个体会有一些误差, 有些晶振是11.03xxMHz, 比标称值小, 如果需要更准确的定时, 可以通过减小mov ar6,#148的值进行微调.
参考
- GNU Make 使用手册: https://www.gnu.org/software/make/manual/
- A51/AX51汇编语言手册 https://web.engr.uky.edu/~jel/course/587/datasheets/A51.pdf
- SDCC用户参考 http://sdcc.sourceforge.net/doc/sdccman.pdf
51单片机封装库HML_FwLib_STC89/STC11的更多相关文章
- [新概念51单片机C语言教程·郭天祥] 1、 基础知识必备
目录: 单片机的大致介绍 1-1.通俗定义 1-2.51系列产品 1-3.标号意思 1-4.引脚介绍 1-5.用C语言开 ...
- 基于51单片机IIC通信的PCF8591学习笔记
引言 PCF8591 是单电源,低功耗8 位CMOS 数据采集器件,具有4 个模拟输入.一个输出和一个串行I2C 总线接口.3 个地址引脚A0.A1 和A2 用于编程硬件地址,允许将最多8 个器件连接 ...
- 51单片机创建PDF文件
PDF文件有特定的格式要求,本以为.TXT与.PDF之间可以相互转换,只需要修改后缀名就可以了,然而事实并非如此. 如下为.PDF文件打开的编码显示. 如果需要创建PDF文件,只需要按照PDF的编码格 ...
- 第一百五十五节,封装库--JavaScript,轮播器
封装库--JavaScript,轮播器 html <div id="banner"> <img src="img/banner1.jpg" a ...
- 【JavaScript 封装库】BETA 5.0 测试版发布!
JavaScript 前端框架(封装库) BETA 5.0 已于10月10日正式发布,今天开始提供 BETA 5.0 的 API 参考文献.相较于之前 5 个版本的发布都是草草的提供源代码,并没有很多 ...
- Altium Designer16绘制51单片机的一些经验总结
制作这块51单片机的还是蛮艰辛的,应该是我水平太差,现在这块51板已经稳定了,也把这块板子制作过程中的一些问题及经验总结记录下来.这块板子制作出了很大问题很大原因是因为我对Altium Designe ...
- ESP8266 AT指令开发(基于STC89C52单片机): 测试下诱人的程序(51单片机,8266,MQTT远程通信控制)
前言 实现的功能,APP通过SmartConfig给Wi-Fi模块配网并绑定设备,然后通过MQTT远程控制开发板的继电器, 简而言之: 51单片机+ESP8266用AT指令实现实现MQTT,(连接的本 ...
- MOS管、PCB、H桥、步进电机驱动电路、51单片机的IO口驱动能力、灌电流、拉电流、上拉电阻的选择
嵌入式开发PCB设计几点体会(转载):http://bbs.ednchina.com/BLOG_ARTICLE_3021801.HTMCollector-Emitter Saturation Volt ...
- STC8H开发(四): FwLib_STC8 封装库的介绍和注意事项
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- Windows 通用应用尝试开发 “51单片机汇编”总结
一.前言 终于完成windows通用应用“51单片机汇编”,半年前开始玩WindowsPhone开发的第一个真正意义上的App(还很多缺点=_=).开发从1月中旬考完试到今天,期间实习了半个月,玩了几 ...
随机推荐
- Python追踪内存占用
技术背景 当我们需要对python代码所占用的内存进行管理时,首先就需要有一个工具可以对当前的内存占用情况进行一个追踪.虽然在Top界面或者一些异步的工具中也能够看到实时的内存变化,还有一些工具可以统 ...
- Vue-自定义icon实现
在项目中引入了element-ui之后,发现其内置的icon有限,无法满足项目的需求,因此需自定义icon来实现需求. 在vue项目的components下新建SvgIcon目录,在SvgIcon目录 ...
- Python注释是什么东东
注释 标注解释,目的是帮助读者理解的文本 也就是说,注释首先是文本,其二是说明,其三是思路,其四是例子注释有两种形式 1. # ... 单行注释 用于对某句语句或语句块进行解释 放在语句块的头行或一个 ...
- springboot入参下划线转驼峰出参驼峰转下划线
springboot入参出参下划线转驼峰 前言 因为历史原因前端入参和出参都为下划线,下划线对有亿点强迫症的我来说是不可接受的.因此就有了下面这篇. 本篇基于之前的一篇springboot封装统一返回 ...
- [转帖]深入浅出分析LSM树(日志结构合并树)
https://zhuanlan.zhihu.com/p/415799237 目录 收起 零.前言 一.LSM树数据结构定义 二.插入操作 三.删除操作 四.修改操作 五.查询操作 六.合并操作 ...
- 【转帖】python 安装whl文件
前言 WHL文件是以Wheel格式保存的Python安装包,Wheel是Python发行版的标准内置包格式.在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的p ...
- [转帖]Linux系统top命令中的io使用率,很多人都误解了它的具体含义
https://baijiahao.baidu.com/s?id=1641356547223820839&wfr=spider&for=pc 最近在做连续数据流的缓冲系统,C语言代 ...
- [转帖]Windows自带硬盘测试工具使用教程
本教程主要讲解Windows自带的硬盘测试工具的使用,不用再安装第三方软件了.到底准不准就不知道啦,下面我们来看看如何使用吧~ 1. 进入cmd 快速进入cmd 主要如果进入后,使用命令直接闪退,就是 ...
- [转帖]记录一次spring-boot程序内存泄露排查
现象 spring boot项目jvm启动配置-Xms4g -Xmx4g,然而很不幸的是程序所占的内存越来越高,都达到了12个多G,只能临时重启服务 常用命令 jstat -class PID jst ...
- [知乎]聊一聊threadlocal
作者:李二狗链接:https://www.zhihu.com/question/341005993/answer/1996544027来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...