为什么要学习单片机汇编语言?

常见的单片机包括:8051, STM32, STC32G, ATMega328P (Arduino)。

学习单片机汇编语言有多个好处,包括但不限于以下几点:

  1. 成本较低:单片机通常价格便宜,适合学生和初学者进行实验和学习。
  2. 风险较小:若发生故障,损失相对较小。单片机的损坏不会像PC系统那样带来重大的经济损失。
  3. 硬体要求低:单片机的硬体要求相对简单,不需要高性能的计算机,只需基本的开发板即可。
  4. 简单的软体设置:开发环境设置较易,通常只需安装简单的编辑器和编译器,无需复杂的配置。
  5. 安全性:失败时只需关掉电源即可重启,避免了系统崩溃的风险。
  6. 指令集简单:单片机的汇编语言指令相对较少,更容易理解其目的和限制,有助于学习基本的编程概念。
  7. 程序执行速度更快:单片机的汇编语言指令执行速度相对较快,对一些需要位运算的指令尤为重要,比其他编译程序 (例如C/C++)表现更有效率。
  8. 实时性:单片机通常用于实时系统,学习汇编语言能帮助理解如何实现实时控制和响应。
  9. 深入了解电子元件:学习汇编语言可以使学生对底层硬体和电子元件有更深入的理解,提升问题解决能力。
  10. 实际操作经验:透过实际编写和调试程式,学生能获得宝贵的实践经验,这对未来的工程师职业生涯非常重要。

总体来说,单片机学习汇编语言是一个理想的选择,尤其适合初学者和想要深入了解电子技术的学习者。

以本人之前编写的一文“STC32G12K128计算e常数精确到小数点后19,720位的汇编程序”为例,如果改用C语言会遇到以下两大难题:

  1. 在单片机内运用C 语言位运算 (bitwise operations) 处理任意精度计算 (arbitrary-precision arithmetic)
  2. 把常数e 由2进制小数转为10进制小数

所以决定改用传统类似笔算的方法求 e 常数的值,每一个字节代表一个10进制位,故此8K只能够处理 8192个10进制位,比起原先的19,720位少了很多。

先用PC 写一个C 程序测试一下:

/* e8191.c  : 计算e常数精确到小数点后8191位 */

#include <stdio.h>
#include <stdlib.h>

#define SIZE  8192          // 阵列大小, 8K xdata

unsigned char e[SIZE];      // 阵列存储常数 e

void clear(unsigned char digits[]) {
    for (int i = 0; i < SIZE; i++) {
        digits[i] = 0; 	   // 阵列清零
    }
}

// digits[] = digits[] / divisor
void divide(unsigned char digits[], int divisor) {
    unsigned char *cptr;
    int quotient, remainder=0;

    for (cptr = digits; cptr < (digits + SIZE); cptr++) {
        remainder = remainder * 10 + *cptr;     // 右移一位 
        quotient = remainder / divisor;         // 计算商
        remainder = remainder % divisor;        // 计算余数
        *cptr = quotient;                       // 商存回阵列內
    }
}

void print_result() {
    unsigned char *cptr;
    printf("2.");                               // 列印 e 常数整数部分
    for (cptr = e + 1; cptr < e + SIZE; cptr++) {
        printf("%01d", *cptr);        // 把阵列内 e 常数小数部分逐一列出
    }
    printf("\n");
}

int main() {
    int divisor;

    clear(e);                // 阵列清零
    e[0] = 1;                // 初始化e = 1
    for (divisor = 2730; divisor >= 2; divisor--) {   // 1/2730! = 6.063x10^-8198
        divide(e, divisor);  // 不断除 divisor
        e[0]++;              // 加 1
    }
    print_result();          // 输出 e 常数
    return 0;
}

测试成功!现在改在 STC32G12K128 单片机运行C程序:

// STC32G_e8191.c : STC32G12K128计算e常数精确到小数点后8191位
#include "STC32G.h"                        // 头文件见下载软件
#include "intrins.h"

#define FOSC    24000000UL                 // FOSC = 24.000 MHz
#define BRT     (256-(FOSC/14400+16)/32)   // 加16 操作是为了让Keil 编译器
                                           // 自动实现四舍五入运算

#define SIZE 8192	  	                   // 8K xdata
#define DIV_START 2730                     // 1/2730! = 6.06x10^-8198

unsigned char xdata e[SIZE];               // 声明关键字为 xdata 

bit busy;  			                       // 发送忙标志
unsigned int shouldBreak = 0;
char wptr;                                 // 写缓冲区指针
char rptr;                                 // 读缓冲区指针
char buffer[16];                           // 缓冲区长度为16个字符

// 串口1中断函数
void UartIsr() interrupt 4
{
	if (TI)
	{
		TI = 0;                            // 清中断标志
		busy = 0;                          // 清发送忙标志
	}
	if (RI)
	{
		RI = 0;                            // 清中断标志
		buffer[wptr++] = SBUF;             // 接收串口数据
		wptr &= 0x0f;                      // 环形缓冲
	}
}

// 串口1 初始化
void UartInit()
{
	SCON = 0x50;    	                   // 串口1 mode 1
	S1BRT = 0;      	                   // 选取Timer1 作为波特率产生器
	TMOD = 0x20;                           // Timer1 mode 2
	TL1 = TH1 = BRT;                       // 设立自动重载值
	TR1 = 1;			                   // 启动Timer 1
	T1x12 = 1;		                       // Timer1 1T mode (FOSC 不分频)
	wptr = 0x00;                           // 清缓冲区指针
  	rptr = 0x00;
  	busy = 0;                              // 清发送忙标志
}

void UartSend(unsigned char dat)
{
	while (busy);   	                   // 等待发送完成
	busy = 1;		                       // 设置发送忙标志
	SBUF = dat;		                       // 数据写入缓冲器
}

void UartSendStr(char *p)
{
	while (*p)		                       // 字串未完全送出
	{
		UartSend(*p++);	                   // 送出字符
	}
}

void clear(unsigned char digits[]) {
    int i;
	for (i = 0; i < SIZE; i++) {
        digits[i] = 0; 	                   // 清 digits 阵列
    }
}

// digits[] = digits[] / divisor
void divide(unsigned char digits[], int divisor) {
    unsigned char *cptr;
    int quotient, remainder=0;

    for (cptr = digits; cptr < (digits + SIZE); cptr++) {
        remainder = remainder * 10 + *cptr;     // 右移一位即是 x10
        quotient = remainder / divisor;         // 求商
        remainder = remainder % divisor;        // 求余数
   		*cptr = quotient;                       // 商存回 e 阵列         
    }
}

//  输出 e
void print_result(unsigned char digits[]) {
    unsigned char *cptr;
	
    UartSendStr("\r\n");
    UartSendStr("2.");                          // e 整数部分
    for (cptr = digits + 1; cptr < (digits + SIZE); cptr++) {
        UartSend((unsigned char)(*cptr+'0'));   // e 分数部分
    }
	UartSendStr("\r\n");
}

// 主程序
int main() {
    int divisor;
    
    clear(e);       // 清 e 阵列
	EAXFR = 1;      // 使能访问XFR
	CKCON = 0x00;   // 设置外部数据总线速度为最快
	WTST = 0x00;    // 赋值为0 可将CPU 执行程序的速度设置为最快
	P3M0 = 0x00;    // P3 设定为准双向口
	P3M1 = 0x00;  
	
    UartInit();     // 串口1 初始化
    ES=1;	        // 使能串口中断
    EA=1;		    // 启动总中断
	
    UartSendStr("\r\nCompute e up to 8191 decimals\r\nWorking, please wait");
    e[0] = 1;      	                // 初始化e = 1
    for (divisor = DIV_START; divisor >= 2; divisor--) {
	    UartSend('.');              // 每一个循环输出小圆点, 代表工作中请等候
        divide(e, divisor);  		// 不断除以 divisor
        e[0]++;                     // 加 1
    }
    print_result(e);                // 输出 e
	while(1);                       // 停止
    return 0;
}

PC 串口3 接收单片机数据的画面:

总而言之,虽然C 语言比较容易编写和维护,但对单片机来说,C语言在低级操作上可能不如汇编语言灵活,特别是在直接控制硬件时;另外,C语言也较难执行位运算,譬如在任意精度计算程序中,需要移动几千个字节内的某一个位元。

总的来说,选择使用C语言还是汇编语言,通常取决于具体的应用需求和开发环境。对于大多数嵌入式硬件开发,较多人选择C语言,因为它提供了更多的函数库和更好的可读性,但本人却独爱汇编语言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值