一.编程知识-进制

10进制里,每一位的权重,从右往左数:个十百千万,也就是:10^0, 10^1, 10^2,10^3……

16进制里,每一位的权重,从右往左数,分别是:16^0, 16^1, 16^2, 16^3, 16^4, ……  

8进制里,每一位的权重,从右往左数,分别是:8^0, 8^1, 8^2, 8^3, 8^4, ……

进制 输出(printf) 输入(scanf)
十进制 %d 或 %i %d
八进制 %o(输出无前缀) %o(输入时可带可不带0)
十六进制 %x(小写字母)或 %X(大写) %x(输入时可带可不带0x)
二进制 标准C没有原生占位符,需自定义函数或使用 itoa()

二.编程知识-字节序

1.大小字节序(大小端)

假设int a = 0x12345678;
16进制数中每位数值占据4 bit;在内存中,是以8个bit作为1byte。
因此0x12345678中每两位作为1byte, 其中0x78是低byte,0x12是高byte。
在内存中的存储方式有两种:

判断大小字节序的方法:指针强转类型

2.位操作

1.逻辑左移

int a = 0x123;  int b = a<<2;  // b=0x48C

2.逻辑右移

int a = 0x123;  int b = a>>2; // b=0x48

3.取反

unsigned int   a = 0x123;
unsigned int   b = ~a;   // b的每一位,都是a对应位的取反

4.位与

unsigned int a = 0x123;
unsigned int b = 0x456;
unsigned int c = a & b; // c等于a位与b,即:a,b的每一位进行与操作


1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

5.位或

unsigned int a = 0x123;
unsigned int b = 0x456;
unsigned int c = a | b; // c等于a位或b,即:a,b的每一位进行或操作

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

6.置位(重要)

置位公式: x |= (1<<n)

unsigned int a = 0x123;
unsigned int b = a | (1<<7) | (1<<8);  // 设置a的bit7, bit8, 赋给b

7.清位(重要)

清位公式: x &=  ~(1<<n)

unsigned int a = 0x123;
unsigned int b = a & ~((1<<7) | (1<<8));  // 清除a的bit7, bit8, 赋给b

8.把某几位设置位某值

比如要把bit7设置为1,把bit8清除为0,这可以分两步操作:先设置bit7,再清除bit8。 还有一种情况:bit[8:7]= val, 不知道val的取值是多少,怎么办? 先清除bit8、bit7,再或上val,代码如下:

unsigned int a = 0x123;
unsigned int b = a & ~(3<<7);  /* 清除bit7, bit8 */
b = b | (val << 7);            /* 设置bit7, bit8为val */

三、汇编知识-汇编、反汇编、机器码

1.程序处理4个步骤

步骤 输入 输出 核心动作 常见报错类型
1. 预处理 .c .i 宏替换、头文件展开、删注释 无(不检查语法)
2. 编译 .i .s 语法分析、生成汇编代码 语法错误(Syntax Error)
3. 汇编 .s .o / .obj 生成二进制机器码(目标文件) 极少数低级错误
4. 链接 .o + 库 .exe / a.out 合并目标文件、解析外部地址 未定义引用(Undefined reference)

2.反汇编

文件格式 推荐写法 (命令) 说明

 .bin

 文件

fromelf.exe --bin -o "$L@L.bin" "#L" $L@L.bin 意思是,在输出文件 .axf 的目录下,生成一个同名的 .bin 文件。
fromelf.exe --bin -o .\Objects\你的工程名.bin .\Objects\你的工程名.axf 这种是直接写明路径和文件名。需要你把 Objects 和 你的工程名 换成自己工程对应的实际目录和名字。
.dis 文件 fromelf.exe --text -a -c -o "$L@L.dis" "#L" --text -a -c 是输出反汇编信息的标准参数组合。
fromelf.exe --text -a -c -o .\Objects\你的工程名.dis .\Objects\你的工程名.axf 同样,可以替换为你的实际路径和文件名。

生成 .bin 文件(二进制固件)
在 Run #1 的输入框中添加以下命令:
fromelf --bin -o "$L@L.bin" "$L@L.axf"

生成 .dis 文件(反汇编文件)
在 Run #2 的输入框中添加以下命令:
fromelf --text -a -c --output="$L@L.dis" "$L@L.axf"

3.汇编深入分析

1.汇编怎么调用C函数?

直接调用

2.寄存器的作用

3.栈的作用

假设A函数把变量val放在了R4寄存器里。如果这时候调用B函数,而B函数也要用R4,就会把A函数的值覆盖掉。为了解决这个问题,B函数在开头会先把R4原来的值临时存到栈里(压栈),等B函数执行完了,在返回前再从栈里把值取回来放回R4(出栈)。这样A函数回来后,R4里的val还是原来的值,一切正常。

4.解析汇编:

四.汇编点灯

                PRESERVE8
                THUMB

; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
				EXPORT	__Vectors
					
__Vectors       DCD     0
                DCD     Reset_Handler              ; Reset Handler


                AREA    |.text|, CODE, READONLY
                
; Reset handler
Reset_Handler   PROC
				EXPORT 	Reset_Handler			[WEAK]
;                IMPORT  main
;				LDR     SP, =0x20000000 + 0x20000
;                BL      main

;				/* 使能GPIOF */
				LDR R0, =(0x40023800 + 0x30)
				LDR R1, [R0]
				ORR R1, R1, #(1<<5)
				STR R1, [R0]

;				/* 设置GPIOF9为输出引脚 */
				LDR R0, =(0x40021400 + 0x00)
				LDR R1, [R0]
				ORR R1, R1, #(1<<18)
				STR R1, [R0]
	
;				/* 设置GPIOF9为推挽输出 */	
				LDR R0, =(0x40021400 + 0x04)
				LDR R1, [R0]
				BIC R1, R1, #(1<<9)
				STR R1, [R0]	
	
;				/* 设置GPIOF9为低速输出 */
				LDR R0, =(0x40021400 + 0x08)
				LDR R1, [R0]
				BIC R1, R1, #(3<<18)
				STR R1, [R0]

				LDR R2, =(0x40021400 + 0x18)
Loop
				
;				/* 设置GPIOF9输出1 */
				LDR R1, [R2]
				ORR R1, R7, #(1<<9)
				STR R1, [R2]	
				
				LDR R0, =100000
				BL delay
				
				
;				/* 设置GPIOF9输出0 */
				LDR R1, [R2]
				ORR R1, R8, #(1<<25)
				STR R1, [R2]	
				
				LDR R0, =100000
				BL delay
				
				B Loop
				ENDP
				
delay
				SUBS R0, R0, #1
				BNE delay
				MOV PC, LR
				
				END

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐