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月中旬考完试到今天,期间实习了半个月,玩了几 ...
随机推荐
- 完美:C# Blazor中显示Markdown并添加代码高亮
昨天发了一篇介绍这个库:C# Blazor中显示Markdown文件,介绍怎么在Blazor中显示Markdown内容的文章,文章内的代码是没有高亮的,思来相去,还是要做好,于是百度到这篇文章.NET ...
- AHB Matrix
常用的AHB Bus结构 AHB Matrix AHB Bus Matrix,即总线矩阵,其实际上就是一个互连(Interconnect).用于连接满足该总线协议的外设,包括Master和Slave. ...
- 【OpenVINO】基于 OpenVINO Python API 部署 RT-DETR 模型
目录 1. RT-DETR 2. OpenVINO 3. 环境配置 3.1 模型下载环境 3.2 模型部署环境 4. 模型下载与转换 4.1 PaddlePaddle模型下载 4.2 IR模型转换 5 ...
- Oracle数据库统计信息_执行计划_sharedpool等的知识梳理
Oracle数据库统计信息_执行计划_sharedpool等的知识梳理 背景 最近有项目出现了年底业务量增加时卡顿的情况. 同事多次发现执行SQL缓慢. 但是重新执行统计信息更新后问题就优化的现象. ...
- [转帖]Linux cut命令
https://www.runoob.com/linux/linux-comm-cut.html#:~:text=Linux%20cut%E5%91%BD%E4%BB%A4%201%20-b%20%E ...
- iftop的学习与使用
iftop的学习与使用 背景 前段时间一直进行netperf 等网络性能验证工具的学习与使用. 监控很多时候采用了 node-exporter + prometheus + grafana来进行观察 ...
- tiup 工具离线安装与简单导出数据说明
tiup 工具离线安装说明 mirror的创建 能上网的机器上面进行如下操作: curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pi ...
- [转帖]兆芯官方的CPU测试成绩,我复现不了
https://baijiahao.baidu.com/s?id=1734998483605483848 下图是兆芯网官上的公开测试成绩,测试对象是3.0GHz的KX-U6880A. 有几个问题: ...
- 解决Word等打开嵌入的文件提示 包含有害内容 无法打开的问题
最近打开文件时提示: 从网上找了一下 最简单的解决办法是: 新建一个文件, 输入如下内容 导入注册表 每次打开时不进行 文件有效性的检查即可. 为了省事 我多加了几个版本的 如果是excel 将 w ...
- DBLink实现备份文件不落盘的导入其他Oracle数据库实例的方法
DBLink实现备份文件不落盘的导入其他Oracle数据库实例的方法 背景 公司内经常有从其他服务器备份数据库实例的需求 之前的操作一般需要,备份源服务器使用expdp将source导出dump文件. ...