C51 单片机编程模板及编码规范

文章目录

  • 一、C51 单片机模板创建
    • 1. 新建工程及选型
    • 2. 创建主程序文件
    • 3. 创建主程序的头文件
    • 4. 编译配置
    • 5. 其他
  • 二、C51 的编码规范

在这里插入图片描述

在查阅了很多关于 C51 单片机的程序后,个人感觉目前网上有关 C51 单片机程序的质量参差不齐,很多程序的代码风格及其糟糕,可读性也很差1。除了新手如此,很多写了多年程序的程序员老手也如此,也包括笔者还处于新手期的时候,在在乎程序是否可以正常运行,而忽略了程序的可读性、可维护性、可复用性以及可扩展性。

由于工作时,笔者所在的公司在编码风格和规范上有及其严格的规定,所以决定总结一下我对我在编写 C51 单片机程序时的编码规范。本文以猿学社上官一号开发板(核心为 STC89C52RC)为基础而编写,编码风格为 K&R(笔者之前干的是 Linux 内核驱动开发,工作用的就是这个风格),变量和函数使用下划线命名法。

在这里插入图片描述

本文前置要求:

  1. 已安装了 C51 的开发环境 Keil uVision5,未安装的建议参考博客《Keil MDK 与 Keil C51 共存的方法》,该文同时安装了 STM32 等主流 32 位单片机的开发环境,并解决了两个软件的共存问题。
  2. 下载 C51 单片机的烧录工具,软件下载地址:工具软件 STC-ISP

一、C51 单片机模板创建

模板的好处在于新建项目时,可以省去选型、添加必要文件等步骤,只要是同一个芯片的项目,复制模板后就可以直接开始编码工作。

模板已同步到 GitHub 和 Gitee,欢迎指正和查阅。

GitHub 链接

Gitee 链接

1. 新建工程及选型

运行 Keil uVision5,在最上面的菜单找到 Project 并点击,弹出下拉菜单后,选择第一个 New μVision Project

在这里插入图片描述

随后的弹窗中,为存放项目的文件夹,我个人的习惯是将所有的代码都放在一个名为 Code 的文件夹中,在里面再具体分类是什么类型的代码,如果是 C51 的代码会专门新建一个文件夹来存放,这里为了演示,直接在 D 盘新建了 C51 的文件夹。然后在文件夹中再新建一个文件夹,名为 template,也就是模板的意思,双击进入文件夹,在下方的文件名再次输入 template,点击 保存

在这里插入图片描述

接着是选型弹窗,由于我的 Keil 包含了 MDK 版本,所以在选型时要先切换到 Legacy Device Database(意思是旧设备数据库),然后按下图操作即可。这里解释一下为什么选择 89C52,而不是 89C51,首先这两者的架构是一样的,都是 Intel 8051 架构,而上官一号采用的芯片是 STC89C52RC,故选 89C52;其次,C52 比 C51 多个更多的外设,例如,C51 拥有 4KB 的 ROM 作为程序存储空间,而 C52 是更大的 8KB,C51 只有两个 16 位定时器/计数器(T0 和 T1),而 C52 多一个额外的 16 位定时器/计数器(T2)等等。当然了,选择 89C51 也可能正常编译程序,并烧录进单片机运行,只是通常会选择与芯片相同的型号最佳。

在这里插入图片描述

不过这里也有人注意到了,现在的 C51 单片机基本都是 STC 生产的(深圳宏晶科技),为什么可以用 AT89C52 来代替 STC89C52 呢?其实这个无所谓选哪个,只要是 89C52 都可以,反正架构是一样的就行。

选好芯片型号后,在随后的弹窗中选择 ,不然程序不能正常编译,这是 C51 的启动文件。

在这里插入图片描述

2. 创建主程序文件

在左边的项目侧边栏中,逐级点开 Target 1 -> Source Group 1,然后鼠标右键点击 Source Group 1,在弹出的菜单中选择 Add New Item to Group 'Source Group 1'

在这里插入图片描述

在随后的弹窗中,按下图所示操作,目的是创建 main.c 作为主程序文件。

在这里插入图片描述

创建成功后,输入下面的代码作为基础模板。

/********************************************************************* File: * Description: * Version: * Date: * Author: * ---------- Revision History ----------* <version> <date> <author> <desc>* ********************************************************************/#include "main.h"/* User Code Includes *//* User Code Define *//* User Code Global Variables *//* User Code Function prototypes *//* ------------------------------ Division Line ------------------------------ *//*** @brief Initialize all configured.*/
void setup(void)
{/* put your setup code here, to run once: */}/*** @brief The application entry point.*/
void main(void)
{/* Private variables *//* Initialize */setup();/* Infinite loop */while (TRUE){/* put your main code here, to run repeatedly: */}
}

简单说明以下这个代码框架的含义,熟悉 Arduino 的同学应该一眼就看出我是抄了 Arduino IDE 的默认程序框架,setup() 是用于做初始化的函数,这个函数只运行一次,后面主要用于循环执行的程序则放在 mian() 函数中的 while(TRUE) 里。其他的注释基本是参考 CubeMX 生成的 Keil 代码,具体解释如下:

  • /* User Code Includes */:在这个注释下可以添加其他头文件。
  • /* User Code Define */:在这个注释下添加程序需要的宏定义。
  • /* User Code Global Variables */:在这个注释下添加全局变量。
  • /* User Code Function prototypes */:在这个注释下添加函数原型。
  • /* Private variables */main() 函数中还有 /* Private variables */,由于 C51 采用的是 C90 的编码标准,所有局部变量都只能写在函数最前端,故设此注释,可在此注释下添加 main() 函数的局部变量。

最前面的一大块注释是文件的描述,这个后面再做具体的解释,当前效果如下图:

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=.%2Fassets%2F在这里插入图片描述
pos_id=img-OJgPGQ6k-1715657159591)

3. 创建主程序的头文件

相信大家也都注意到了,在主文件中的 main.h 头文件目前是不存在的,以及 while 循环的条件是 TRUE,这个也是目前不存在的变量或宏定义,所以接下来就要创建对应的头文件。

与创建 .c 文件差不多,创建头文件只需要选择 Header File(.h) 即可。

在这里插入图片描述

然后在 main.h 文件中输入以下代码:

/********************************************************************* File: bit.h* Description: 89C51/89C52 main header files.* Version: 1.0* Date: 2024-04-25* Author: zhengxinyu13@gmail.com* ---------- Revision History ----------* <version> <date> <author> <desc>* ********************************************************************/#ifndef __MAIN_H__
#define __MAIN_H__#include "STC89C5xRC-rdp.h"#define HIGH        1
#define LOW         0
#define TRUE        1
#define FALSE       0
#define TURN_ON     1
#define TURN_OFF    0#define SET(pin)    (pin) = 1
#define RESET(pin)  (pin) = 0typedef unsigned char   uint8_t;
typedef signed char     int8_t;
typedef unsigned int    uint16_t;
typedef signed int      int16_t;
typedef unsigned long   uint32_t;
typedef signed long     int32_t;#include "delay.h"#endif // __MAIN_H__

整个头文件用 #ifndef #define #endif 的结构,可以防止重复定义。__MAIN_H__ 这个宏的命名方式也是行业约定俗成的命名方法,两个下划线开头,然后就是头文件名字的大写,中间的点用下划线代替,再两个下划线结尾。#endif 最后都会习惯用双斜杠注释最开始定义的宏,表示这个 #endif 是从哪个 #ifndef 开始的。

中间是一些常用的宏定义,typedef 关键字后面重定义了一些数据类型,这些类型别名使得代码更加可读,并且有助于提升代码的可移植性。这些数据类型取自 <stdint.h> 这个头文件,但是因为 C51 单片机使用的还是 C90 的标准,该头文件为 C99 标准时引入的头文件,因此没办法在这插入,所以才使用了typedef 关键字进行重定义。

regx51.h 头文件是 Keil 安装时就已经引入,不需要用户自定义,但是这个头文件并不完整,例如像特殊寄存器 AUXR、WDT_CONTR 等都未定义,也没有定义 P4 引脚,所以 STC89C5xRC-rdp.h 头文件对此进行了补充,该文件非笔者编写,而是由烧录软件 STC-ISP 自动生成。

在这里插入图片描述

具体代码如下:

/*--------------------------------------------------------------------------
STC89C5xRC-rdp.hSupplemental header files for Low Voltage Flash Atmel AT89C52 and AT89LV52.
--------------------------------------------------------------------------*/#ifndef __STC89C5xRC_RDP_H__
#define __STC89C5xRC_RDP_H__///包含本头文件后,不用另外再包含"REG51.H"sfr         P0          =           0x80;sbit    P00         =           P0^0;sbit    P01         =           P0^1;sbit    P02         =           P0^2;sbit    P03         =           P0^3;sbit    P04         =           P0^4;sbit    P05         =           P0^5;sbit    P06         =           P0^6;sbit    P07         =           P0^7;sfr         SP          =           0x81;
sfr         DPL         =           0x82;
sfr         DPH         =           0x83;
sfr         PCON        =           0x87;sfr         TCON        =           0x88;sbit    TF1         =           TCON^7;sbit    TR1         =           TCON^6;sbit    TF0         =           TCON^5;sbit    TR0         =           TCON^4;sbit    IE1         =           TCON^3;sbit    IT1         =           TCON^2;sbit    IE0         =           TCON^1;sbit    IT0         =           TCON^0;sfr         TMOD        =           0x89;
sfr         TL0         =           0x8A;
sfr         TL1         =           0x8B;
sfr         TH0         =           0x8C;
sfr         TH1         =           0x8D;
sfr         AUXR        =           0x8E;sfr         P1          =           0x90;sbit    P10         =           P1^0;sbit    P11         =           P1^1;sbit    P12         =           P1^2;sbit    P13         =           P1^3;sbit    P14         =           P1^4;sbit    P15         =           P1^5;sbit    P16         =           P1^6;sbit    P17         =           P1^7;sbit    T2EX        =           P1^1;sbit    T2          =           P1^0;sfr         SCON        =           0x98;sbit    SM0         =           SCON^7;sbit    SM1         =           SCON^6;sbit    SM2         =           SCON^5;sbit    REN         =           SCON^4;sbit    TB8         =           SCON^3;sbit    RB8         =           SCON^2;sbit    TI          =           SCON^1;sbit    RI          =           SCON^0;sfr         SBUF        =           0x99;sfr         P2          =           0xA0;sbit    P20         =           P2^0;sbit    P21         =           P2^1;sbit    P22         =           P2^2;sbit    P23         =           P2^3;sbit    P24         =           P2^4;sbit    P25         =           P2^5;sbit    P26         =           P2^6;sbit    P27         =           P2^7;sfr         AUXR1       =           0xA2;sfr         IE          =           0xA8;sbit    EA          =           IE^7;sbit    EC          =           IE^6;sbit    ET2         =           IE^5;sbit    ES          =           IE^4;sbit    ET1         =           IE^3;sbit    EX1         =           IE^2;sbit    ET0         =           IE^1;sbit    EX0         =           IE^0;sfr         SADDR       =           0xA9;sfr         P3          =           0xB0;sbit    P30         =           P3^0;sbit    P31         =           P3^1;sbit    P32         =           P3^2;sbit    P33         =           P3^3;sbit    P34         =           P3^4;sbit    P35         =           P3^5;sbit    P36         =           P3^6;sbit    P37         =           P3^7;sbit    RD          =           P3^7;sbit    WR          =           P3^6;sbit    T1          =           P3^5;sbit    T0          =           P3^4;sbit    INT1        =           P3^3;sbit    INT0        =           P3^2;sbit    TXD         =           P3^1;sbit    RXD         =           P3^0;sfr         IPH         =           0xB7;
sfr         IP          =           0xB8;sbit    PT2         =           IP^5;sbit    PS          =           IP^4;sbit    PT1         =           IP^3;sbit    PX1         =           IP^2;sbit    PT0         =           IP^1;sbit    PX0         =           IP^0;sfr         SADEN       =           0xB9;sfr         XICON       =           0xC0;sbit    PX3         =           XICON^7;sbit    EX3         =           XICON^6;sbit    IE3         =           XICON^5;sbit    IT3         =           XICON^4;sbit    PX2         =           XICON^3;sbit    EX2         =           XICON^2;sbit    IE2         =           XICON^1;sbit    IT2         =           XICON^0;sfr         T2CON       =           0xC8;sbit    TF2         =           T2CON^7;sbit    EXF2        =           T2CON^6;sbit    RCLK        =           T2CON^5;sbit    TCLK        =           T2CON^4;sbit    EXEN2       =           T2CON^3;sbit    TR2         =           T2CON^2;sbit    C_T2        =           T2CON^1;sbit    CP_RL2      =           T2CON^0;sfr         T2MOD       =           0xC9;
sfr         RCAP2L      =           0xCA;
sfr         RCAP2H      =           0xCB;
sfr         TL2         =           0xCC;
sfr         TH2         =           0xCD;sfr         PSW         =           0xD0;sbit    CY          =           PSW^7;sbit    AC          =           PSW^6;sbit    F0          =           PSW^5;sbit    RS1         =           PSW^4;sbit    RS0         =           PSW^3;sbit    OV          =           PSW^2;sbit    F1          =           PSW^1;sbit    P           =           PSW^0;sfr         ACC         =           0xE0;sfr         WDT_CONTR   =           0xE1;
sfr         ISP_DATA    =           0xE2;
sfr         ISP_ADDRH   =           0xE3;
sfr         ISP_ADDRL   =           0xE4;
sfr         ISP_CMD     =           0xE5;
sfr         ISP_TRIG    =           0xE6;
sfr         ISP_CONTR   =           0xE7;sfr         P4          =           0xE8;sbit    P40         =           P4^0;sbit    P41         =           P4^1;sbit    P42         =           P4^2;sbit    P43         =           P4^3;sbit    P44         =           P4^4;sbit    P45         =           P4^5;sbit    P46         =           P4^6;sbit    P47         =           P4^7;sfr         B           =           0xF0;/#endif

代码也提示了,不需要再添加regx51.h 头文件了。

最后还有一个 delay.h 头文件,需要同时在工程中新建一个 delay.h 和一个 delay.c,内容如下:

/********************************************************************* File: delay.h* Description: Time delay function based on 11.0592MHz crystal* oscillator.* crystal oscillator.* Version: 1.0* Date: 2023-07-17* Author: zhengxinyu13@gmail.com* ---------- Revision History ----------* <version> <date> <author> <desc>* ********************************************************************/#ifndef __DELAY_H__
#define __DELAY_H__#include "main.h"
#include "intrins.h"void delay_ms(uint16_t xms);
void delay_1s(void);#endif // __DELAY_H__
/********************************************************************* File: delay.h* Description: Delay function library based on 11.0592MHz* crystal oscillator.* Version: 1.0* Date: 2023-07-17* Author: zhengxinyu13@gmail.com* ---------- Revision History ----------* <version> <date> <author> <desc>* ********************************************************************/#include "delay.h"/*** @brief Delay function in milliseconds.* @param Enter the delay time in milliseconds.*/
void delay_ms(uint16_t xms)
{uint16_t i,j;for (i = 0; i < xms; i++)for (j = 0; j < 112; j++);
}/*** @brief Delay by one second.*/
void delay_1s(void)
{uint8_t i, j, k;_nop_();i = 8;j = 1;k = 243;do {do {while (--k);} while (--j);} while (--i);
}

这里特殊说明一下,这个延时函数的文件不是必要的,只是延时函数在 C51 的开发过程中太常见了,所以加上去方便后面的开发。

[!NOTE]

实际项目的单片机开发不会用在这种软件延时,太占用 CPU 了,通常是用定时中断计时,考虑到 C51 的资源太匮乏,在学习开发阶段,可以使用软件延时。

4. 编译配置

最后需要配置编译,否则编译后也不会生成 .hex 文件,在菜单栏中找打类似魔术棒一样的图标并点击。

在这里插入图片描述

这里建议把晶振频率设置成实际开发的频率,一般都是 11.0592MHz。

在这里插入图片描述

然后点击选项卡 Output,把 Create HEX File 勾上,Name of Executable 是编译后的 .hex 文件的文件名,我习惯使用 build。最后点击 OK 完成配置。

在这里插入图片描述

点击编译试一下,如果在下面的 Bulid Output 中出现 creating hex file from ".\Objects\build"... 的字样,就表示编译通过,并生成了 .hex 文件。有两个警告是因为 delay.c 中写了两个延时函数没有调用导致,知道原因可以忽略。

在这里插入图片描述

5. 其他

考虑到很多英文水平不好的同学写英文注释比较难受,可以使用中文注释,建议修改成 UTF-8。先在菜单栏找到小扳手的图标并打开,在 Encoding 中选择 Encode in UTF-8 without signature,不建议选择 GB2312 或者 BIG5,因为有些字符无法显示。

在这里插入图片描述

如果有同学对我的 Keil 主题比较感兴趣的话,我可以单独出一篇博客细说一下。

二、C51 的编码规范

关于编码规范,我基本都有比较统一的标准,可以直接参考我的博客《关于我个人的编码规范(C/C++)》,这里只补充一点,这规范中,我提到要将 tab 键设置成 4 个空格,统一用 tab 键缩进,这里教大家怎么把 Keil 的 tab 设置成 4 个空格。

按图设置即可。

在这里插入图片描述


  1. 个人感觉郭天祥老师有一定责任,当年他培养出新世纪以来第一批嵌入式工程师,他的十天学会 C51 的课程以及那本新概念的书功不可没,但是其课程和书的代码风格都很乱,这也可能是后来很多单片机工程师代码风格很差的一个原因。 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/701675.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YOLOv9改进策略 | 低照度图像篇 | 2024最新改进CPA-Enhancer链式思考网络(适用低照度、图像去雾、雨天、雪天)

一、本文介绍 本文给大家带来的2024.3月份最新改进机制&#xff0c;由CPA-Enhancer: Chain-of-Thought Prompted Adaptive Enhancer for Object Detection under Unknown Degradations论文提出的CPA-Enhancer链式思考网络&#xff0c;CPA-Enhancer通过引入链式思考提示机制&am…

详解xlswriter 操作Excel的高级应用conditional_format

在文章详解xlsxwriter 操作Excel的常用API-CSDN博客 我们介绍了xlswriter 基础api的使用情况&#xff0c;在实际工作中我们经常会遇到下面的需求&#xff0c;cell满足某某条件时&#xff0c;进行对应的格式化处理。这时候我们可以使用conditional_format的函数&#xff0c;他允…

C语言----斐波那契数列(附源代码)

各位看官们好&#xff0c;当我写了上一篇博客杨辉三角后&#xff0c;有一些看官叫我讲一下斐波那契数列。对于这个大家应该是有了解的。最简单的规律就是f(n)f(n-2)f(n-1)。就是当前是前两项之和&#xff0c;然后下标1和0都是1.从第三项开始计算的。那么我们知道规律&#xff0…

echarts map地图添加背景图

给map地图添加了一个阴影3d的效果&#xff0c;添加一张背景图&#xff0c;给人感觉有3d的效果 具体配置如下&#xff1a; html代码模块&#xff1a; <div class"echart_img" style"position: fixed; visibility: hidden;"></div><div id&q…

【正则表达式】1、元字符的认识与分类

1、元字符的概念 正则表达式的常见功能&#xff0c;分别是校验数据的有效性、查找符合要求的文本以及对文本进行切割和替换等操作。 我想你一定在办公软件&#xff0c;比如 Word、Excel 中用过这个功能。你可以使用查找功能快速定位关注的内容&#xff0c;然后使用替换&#xf…

【谷粒商城】04.快速开发

1.克隆代码 https://gitee.com/renrenio DELLLJL MINGW64 ~/Desktop $ git clone https://gitee.com/renrenio/renren-fast.git Cloning into renren-fast... remote: Enumerating objects: 1965, done. remote: Counting objects: 100% (1965/1965), done. remote: Compress…

查看Linux系统是Ubuntu还是CentOS

要查看Linux系统是Ubuntu还是CentOS&#xff0c;可以通过多种方式进行确认&#xff1a; 查看/etc/os-release文件&#xff1a; 在终端中执行以下命令&#xff1a; cat /etc/os-release 如果输出中包含"IDubuntu"&#xff0c;则表示系统是Ubuntu&#xff1b;如果输出中…

基于EKF扩展卡尔曼滤波的一阶环形倒立摆控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于EKF扩展卡尔曼滤波的一阶环形倒立摆控制系统simulink建模与仿真。基于扩展卡尔曼滤波&#xff08;Extended Kalman Filter, EKF&#xff09;的一阶环形倒立摆控制系统&…

SpringBoot多模块项目MybatisPlus配置

项目目录 主模块配置 配置类 Configuration EnableTransactionManagement MapperScan("com.sms.**.mapper") public class MybatisPlugConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor new…

最佳实践 | 用HelpLook构建一体化企业知识中台

企业知识中台是内容与数据的双向交流的重要载体&#xff0c;它不仅能够让企业的内容说话&#xff0c;也能够倾听和分析数据。 你是否因寻找建立企业内部知识库/知识中台和说明文档平台的合适工具而苦恼&#xff1f;HelpLook数字内容平台正是你的理想之选。该平台以其简洁且用户…

JSPfilters过滤技术

1.创建动态web项目 2.创建filters的文件 3.创建主页面 4.配置xml项目 总结构 主页面代码 <% page language"java" contentType"text/html; charsetUTF-8"pageEncoding"UTF-8"%><!DOCTYPE html><html><head><meta cha…

MYDB运行环境的搭建

前言 再将该项目跑起来之前&#xff0c;我想你的环境应该是准备的差不多啦&#xff01;只需要稍稍的配置一下即可。 我在本地已经跑通了&#xff0c;说是兼容JDK8&#xff0c;但我本地刚好有11就用了11啦&#xff0c;跑起来的方法在仓库的 readme 里写的很清楚。 在运行mvn c…