https://raw.githubusercontent.com/DeqingSun/ch55xduino/ch55xduino/package_ch55xduino_mcs51_index.json

/*
Blink
Turns an LED on for one second, then off for one second, repeatedly.
Most Arduinos have an on-board LED you can control. On the simpleCH552
it is attached to digital pin P3.3
If you want to know what pin the on-board LED is connected to on your Arduino
model, check the Technical Specs of your board at:
https://www.arduino.cc/en/Main/Products
modified 8 May 2014
by Scott Fitzgerald
modified 2 Sep 2016
by Arturo Guadalupi
modified 8 Sep 2016
by Colby Newman
modified 13 Jun 2020
by Deqing Sun for use with CH55xduino
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/Blink
*/
#define LED_BUILTIN 33
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
LED_BUILTIN的值为 33,即P3.3管脚。你可以随意修改其它值。

菜单--Sketch--Export Compiled Binary,导出.hex文件到构建目录菜单--Sketch--Show Sketch Folder,在build/CH55xDuino.mcs51.ch552目录中找到 Blink.ino.hex文件
led.ihx、led.bin为实际文件名。objcopy --input-target=ihex --output-target=binary led.ihx led.binsrec_cat led.ihx -intel -o led.bin -binaryBlink.ino.bin拖拽到IDA Pro中,选择处理器类型为8051、内存架构为默认、设备名称也为默认C517。 没有提到的设置均为默认,这是个很小的项目,不需要复杂的设置。

The initial autoanalysis has been finished.

code:00000000 ; RESET
code:00000000
code:00000000 ; public RESET
code:00000000 RESET:
code:00000000
code:00000000 ; FUNCTION CHUNK AT code:00000066 SIZE 00000003 BYTES
code:00000000 ; FUNCTION CHUNK AT code:0000006B SIZE 00000016 BYTES
code:00000000 ; FUNCTION CHUNK AT code:000002E1 SIZE 0000000B BYTES
code:00000000
code:00000000 ljmp IEX6 ; External interrupt 6
code:00000000 ; End of function RESETRESET 这个标签并不存在。 你可以看到地址0x00000000并没有往前走,只有 ljmp IEX6这一行是真实存在的。IEX6 IDA Pro会带你跳转到这个标签的实际位置。code:0000006B IEX6: ; CODE XREF: RESET↑j
code:0000006B mov SP, #RAM_21 ; External interrupt 6
code:0000006E lcall code_7E4
code:00000071 mov A, DPL ; Data Pointer, Low Byte
code:00000073 jz code_78
code:00000075 ljmp code_66mov SP, #RAM_21将0x21赋值给SP(Stack Pointer,栈指针)。查询芯片手册可见CH554G和通用8051的SP复位值都是0x07。
lcall code_7E4 完成SP初始化后,long call调用函数 code_7E4。code:000007E4 code_7E4: ; CODE XREF: RESET+6E↑p
code:000007E4 mov DPL, #0 ; Data Pointer, Low Byte
code:000007E7 ret
code:000007E7 ; End of function code_7E4mov DPL, #0 将数据指针的低字节 DPL 设置为 0。DPH和DPL共同组成DPTR,DPTR用于访问xRAM。Jump to xref to operand

code:0000006B IEX6: ; CODE XREF: RESET↑j
code:0000006B mov SP, #RAM_21 ; External interrupt 6
code:0000006E lcall code_7E4
code:00000071 mov A, DPL ; Data Pointer, Low Byte
code:00000073 jz code_78
code:00000075 ljmp code_66mov A, DPL将前面我们设为0的DPL,赋值给了累加器A。jz code_78根据累加器A是否为零,决定是否跳转到code_78。因为A必然为零,所以这个跳转必然执行,下一行的ljmp code_66也不会有机会再执行到。code:00000078 code_78: ; CODE XREF: RESET+73↑j
code:00000078 mov R1, #0x10
code:0000007A mov A, R1
code:0000007B orl A, #0
code:0000007D jz code_9A
code:0000007F mov R2, #1
code:0000007F ; END OF FUNCTION CHUNK FOR RESET
code:0000007F ; ---------------------------------------------------------------------------
code:00000081 .byte 0x90
code:00000082 .byte 9mov R1, #0x10 给寄存器R1赋值0x10。CH554G和标准8051单片机一样,有4个寄存器组。根据RS1和RS0的不同,切换不同的寄存器组。 此处刚通电不久,R1指的是寄存器组0偏移为1的寄存器,即实际地址为 0x08 + 0x01 = 0x09mov A, R1 又将给寄存器R1的值0x10复制到累加器A。orl A, #0 将累加器 A 的值与立即数 0 进行按位或操作。这条指令是冗余的,因为任何数与0进行或运算,结果都是其本身。执行后,A 的值仍然是 0x10。jz code_9A 这里累加器A的值为0x10,不会跳转。mov R2, #1 给寄存器R2赋值0x01。.byte 0x90 这里出现了未能正确识别的指令,IDA Pro只能以十六进制 0x90的方式直接展示给我们。出现这样的问题,说明我们选择的处理器型号不够准确。
C515。
code:00000078 code_78: ; CODE XREF: IEX6+8↑j
code:00000078 mov R1, #0x10
code:0000007A mov A, R1
code:0000007B orl A, #0
code:0000007D jz code_9A
code:0000007F mov R2, #1
code:00000081 mov DPTR, #0x975
code:00000084 mov R0, #0xD9
code:00000086 mov P2, #0 ; Port 2mov DPTR, #0x975 前面我们单独把DPL设置为0,这次我们将整个DPTR设置为0x975。mov R0, #0xD9 设置寄存器R0为0xD9。mov P2, #0 CH554G的外部数据存储空间共64KB,其中1KB用于片内扩充xRAM,剩余保留(也就是没做)。 8051单片机访问外部数据存储空间时,自动使用P2寄存器作为高8位,因此在CH554G开发板上只能看到P1和P3的管脚,没有P2。此处清空P2的意义未知,Blink项目也用不到xRAM。code:00000089 code_89: ; CODE XREF: IEX6:code_93↓j
code:00000089 ; IEX6+2A↓j
code:00000089 clr A
code:0000008A movc A, @A+DPTR
code:0000008B movx @R0, A
code:0000008C inc DPTR
code:0000008D inc R0
code:0000008E cjne R0, #0, code_93
code:00000091 inc P2 ; Port 2clr A清空累加器Amovc A, @A+DPTR普通的mov指令用于在寄存器之间搬运数据,movc指令用于在寄存器和程序存储器之间搬运数据。 程序存储器,正是我们此刻在阅读的code:0000xxxx的区域。二进制固件会被烧录到ROM中,ROM就是程序存储器。CH554G上ROM有16KB,采用iFlash制造工艺,只可擦写约200次。我们在学习CH554G过程中要留意这件事,iFlash损坏后就需要购买新的开发板。 此刻DPTR为0x975,累加器A被清空,所以是将0x975的内容搬运到累加器A。code:00000972 .byte 0xFF
code:00000973 .byte 0xFF
code:00000974 .byte 0xFF
code:00000975 .byte 0xE1
code:00000976 .byte 2
code:00000977 .byte 0
code:00000978 .byte 0movx @R0, Amovx指令将累加器A的值写入到xRAM中。结合上面的内容,xRAM的地址高8位由P2提供,P2已清空。R0当前值为0xD9。即,搬运0xE1到累加器A,再搬运到xRAM地址0xD9inc DPTRDPTR向下挪了1个字节。inc R0R0也向下挪了一个字节。cjne R0, #0, code_93 如果R0和0x00不相等,就跳转到code_93。code:00000093 code_93: ; CODE XREF: IEX6+23↑j
code:00000093 djnz R1, code_89
code:00000095 djnz R2, code_89
code:00000097 mov P2, #0xFF ; Port 2djnz R1, code_89 (Decrement and Jump if Not Zero) 将寄存器 R1 的值减1。如果结果不为0,则跳转回 code_89。djnz R2, code_89 如果上一条 djnz 没有跳转(即 R1 减到了0),则执行此指令。将 R2 的值减1。如果结果不为0,则跳转回 code_89。R2 = 0x01,所以执行到此处时R2 - 1一定为0,一定不会跳转 code_89。mov P2, #0xFF 当 R1 和 R2 都减到0后,循环结束,此时DPTR = 0x985,程序执行到这里。将P2端口的所有引脚设置为1。code:0000009A code_9A: ; CODE XREF: IEX6+12↑j
code:0000009A clr A
code:0000009B mov R0, #0xFFclr A 清空累加器A。mov R0, #0xFF 填满寄存器R0。code:0000009D code_9D: ; CODE XREF: IEX6+33↓j
code:0000009D mov @R0, A
code:0000009E djnz R0, code_9D
code:000000A0 mov R0, #0
code:000000A2 mov A, R0
code:000000A3 orl A, #0
code:000000A5 jz code_B1
code:000000A7 mov R1, #0x94
code:000000A9 mov P2, #0 ; Port 2
code:000000AC clr Amov @R0, A 向iRAM地址0xFF的位置写入0x00。djnz R0, code_9D R0 = R0 - 1。重复循环,直到把iRAM完全置零。在CH554G上,iRAM仅256B,这个过程不会持续很久。mov R0, #0 循环结束后,将 R0 重新设置为 0。mov A, R0 让累加器A也变成0。orl A, #0 冗余操作:如前所述,这条指令不改变 A 的值,A 仍为 0。jz code_B1 累加器A是0x00,一定会跳转到code_B1。code:000000B1 code_B1: ; CODE XREF: IEX6+3A↑j
code:000000B1 mov R0, #0x45 ; 'E'
code:000000B3 mov A, R0
code:000000B4 orl A, #0
code:000000B6 jz code_C4
code:000000B8 mov R1, #1
code:000000BA mov DPTR, #0x94
code:000000BD clr Amov R0, #0x45 给R0赋值0x45mov A, R0 给累加器A也赋值0x45orl A, #0 冗余指令。累加器A不变。jz code_C4 A是0x45,一定不会跳转。mov R1, #1 给R1赋值为0x01mov DPTR, #0x94 给DPTR赋值0x94clr A 清空Acode:000000BE code_BE: ; CODE XREF: IEX6+55↓j
code:000000BE ; IEX6+57↓j
code:000000BE movx @DPTR, A
code:000000BF inc DPTR
code:000000C0 djnz R0, code_BE
code:000000C2 djnz R1, code_BEcode:000000C4 code_C4: ; CODE XREF: IEX6+4B↑j
code:000000C4 mov R0, #8
code:000000C6 clr A
code:000000C7 mov @R0, A
code:000000C8 inc R0
code:000000C9 mov @R0, A
code:000000CA inc R0
code:000000CB mov @R0, A
code:000000CC inc R0
code:000000CD mov @R0, A
code:000000CE mov R0, #0xC
code:000000D0 mov @R0, #0
code:000000D2 clr RAM_20.0
code:000000D4 clr RAM_20.1
code:000000D6 ljmp code_66code:00000066 code_66: ; CODE XREF: IEX6+A↓j
code:00000066 ; IEX6+6B↓j
code:00000066 ljmp code_2E1code:000002E1 code_2E1: ; CODE XREF: IEX6:code_66↑j
code:000002E1 lcall code_615
code:000002E4 lcall code_DAcode:00000615 code_615: ; CODE XREF: IEX6:code_2E1↑p
code:00000615 mov RESERVED00A1, #0x55 ; 'U' ; RESERVED
code:00000618 mov RESERVED00A1, #0xAA ; RESERVED
code:0000061B mov A, #0xF8
code:0000061D anl A, IP1 ; Interrupt Priority Register 1
code:0000061F orl A, #6
code:00000621 mov IP1, A ; Interrupt Priority Register 1
code:00000623 mov RESERVED00A1, #0 ; RESERVED
code:00000626 mov DPTR, #0x1388
code:00000629 lcall code_5DA
code:0000062C mov RESERVED009E, #0x5D ; ']' ; RESERVED
code:0000062F mov RESERVED009D, #0 ; RESERVED
code:00000632 mov A, #0xF0
code:00000634 anl A, TMOD ; Timer Mode Register
code:00000636 orl A, #2
code:00000638 mov TMOD, A ; Timer Mode Register
code:0000063A anl RESERVED00C9, #0xEF ; RESERVED
code:0000063D mov TH0, #6 ; Timer 0, High Byte
code:00000640 clr TF0 ; Timer 0/1 Control Register
code:00000642 setb ET0 ; Interrupt Enable Register 0
code:00000644 setb TR0 ; Timer 0/1 Control Register
code:00000646 setb EAL ; Interrupt Enable Register 0
code:00000648 ret
code:00000648 ; End of function code_615RESERVED00A1 留意末尾地址0xA1,查询特殊功能寄存器表,可知它是SAFE_MOD。


code:000005DA code_5DA: ; CODE XREF: code_615+14↓p
code:000005DA mov R6, DPL ; Data Pointer, Low Byte
code:000005DC mov R7, DPH ; Data Pointer, High Byte
code:000005DE clr C
code:000005DF mov A, #1
code:000005E1 subb A, R6
code:000005E2 clr A
code:000005E3 subb A, R7
code:000005E4 jc code_5E8
code:000005E6 retcode:000005E8 code_5E8: ; CODE XREF: code_5DA+A↑j
code:000005E8 clr C
code:000005E9 mov A, #2
code:000005EB subb A, R6
code:000005EC mov R6, A
code:000005ED mov A, #0
code:000005EF subb A, R7
code:000005F0 mov R7, A
code:000005F1 nop
code:000005F2 cjne R6, #0, code_5FA
code:000005F5 cjne R7, #0, code_5FA
code:000005F8 nop
code:000005F9 retcode:000005FA code_5FA: ; CODE XREF: code_5DA+18↑j
code:000005FA ; code_5DA+1B↑j ...
code:000005FA nop
code:000005FB nop
code:000005FC nop
code:000005FD nop
code:000005FE nop
code:000005FF nop
code:00000600
code:00000600 code_600: ; CODE XREF: code_5DA+36↓j
code:00000600 nop
code:00000601 nop
code:00000602 nop
code:00000603 nop
code:00000604 nop
code:00000605 nop
code:00000606 nop
code:00000607 nop
code:00000608 nop
code:00000609 nop
code:0000060A nop
code:0000060B inc R6
code:0000060C cjne R6, #0, code_5FA
code:0000060F inc R7
code:00000610 cjne R7, #0, code_600
code:00000613 nop
code:00000614 retcode:00000615 code_615: ; CODE XREF: IEX6:code_2E1↑p
code:00000615 mov RESERVED00A1, #0x55 ; 'U' ; RESERVED
code:00000618 mov RESERVED00A1, #0xAA ; RESERVED
code:0000061B mov A, #0xF8
code:0000061D anl A, IP1 ; Interrupt Priority Register 1
code:0000061F orl A, #6
code:00000621 mov IP1, A ; Interrupt Priority Register 1
code:00000623 mov RESERVED00A1, #0 ; RESERVED
code:00000626 mov DPTR, #0x1388
code:00000629 lcall code_5DA
code:0000062C mov RESERVED009E, #0x5D ; ']' ; RESERVED
code:0000062F mov RESERVED009D, #0 ; RESERVED
code:00000632 mov A, #0xF0
code:00000634 anl A, TMOD ; Timer Mode Register
code:00000636 orl A, #2
code:00000638 mov TMOD, A ; Timer Mode Register
code:0000063A anl RESERVED00C9, #0xEF ; RESERVED
code:0000063D mov TH0, #6 ; Timer 0, High Byte
code:00000640 clr TF0 ; Timer 0/1 Control Register
code:00000642 setb ET0 ; Interrupt Enable Register 0
code:00000644 setb TR0 ; Timer 0/1 Control Register
code:00000646 setb EAL ; Interrupt Enable Register 0
code:00000648 ret
code:00000648 ; End of function code_615





anl RESERVED00C9, #0xEF清空最高位,选择使用分频时钟。mov TH0, #6 设置定时器0的高字节 TH0 为 6。低字节 TL0 在复位后默认为 0。 定时器初始值为 0x0600 (十进制1536)。此处留意TMOD设置的模式,模式2:8 位重载定时/计数器n,计数单元使用TLn,THn 作为重载计数单元。 计数从8位全为1变成全为0 时,设置溢出标志TFn,并自动从THn 加载初值。clr TF0 清空Timer0的溢出中断标志位。setb ET0 定时器0中断使能位,启用Timer0的中断。setb TR0 Timer0启动/停止位,置1 启动。留意TR0是寄存器TCON的bit 4,此时TCON = 0x10setb EAL EAL寄存器即EA寄存器,全局中断使能控制位,置1 启用。code:000002E1 code_2E1: ; CODE XREF: IEX6:code_66↑j
code:000002E1 lcall code_615
code:000002E4 lcall code_DAcode:000000DA code_DA: ; CODE XREF: IEX6+279↓p
code:000000DA
code:000000DA ; FUNCTION CHUNK AT code:000002F0 SIZE 0000007D BYTES
code:000000DA
code:000000DA mov DPTR, #0x94
code:000000DD mov A, #1
code:000000DF movx @DPTR, A
code:000000E0 mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:000000E3 ljmp code_2F0
code:000000E3 ; End of function code_DAcode:000002F0 code_2F0: ; CODE XREF: code_DA+9↑j
code:000002F0 mov A, DPL ; Data Pointer, Low Byte
code:000002F2 mov R7, A
code:000002F3 mov DPTR, #0x925
code:000002F6 movc A, @A+DPTR
code:000002F7 mov R6, A
code:000002F8 mov A, R7
code:000002F9 mov DPTR, #0x8FD
code:000002FC movc A, @A+DPTR
code:000002FD mov R7, A
code:000002FE jnz code_301
code:00000300 ret0x21 + 0x0925 = 0x0946处加载内容到累加器A。code:00000941 .byte 0
code:00000942 .byte 0
code:00000943 .byte 1
code:00000944 .byte 2
code:00000945 .byte 4
code:00000946 .byte 8
code:00000947 .byte 0x10
code:00000948 .byte 0x20
code:00000949 .byte 0x40 ; @
code:0000094A .byte 0x800x21 + 0x08FD = 0x091E处加载内容到累加器A并保存到寄存器R7。code:0000091B .byte 4
code:0000091C .byte 4
code:0000091D .byte 4
code:0000091E .byte 4
code:0000091F .byte 4
code:00000920 .byte 4
code:00000921 .byte 4
code:00000922 .byte 4code:00000301 code_301: ; CODE XREF: code_DA+224↑j
code:00000301 mov DPTR, #0x94
code:00000304 movx A, @DPTR
code:00000305 mov R5, A
code:00000306 jnz code_323
code:00000308 cjne R7, #2, code_314
code:0000030B mov A, R6
code:0000030C cpl A
code:0000030D mov R4, A
code:0000030E anl RESERVED0092, A ; RESERVED
code:00000310 mov A, R4
code:00000311 anl RESERVED0093, A ; RESERVED
code:00000313 retcode:00000323 code_323: ; CODE XREF: code_DA+22C↑j
code:00000323 cjne R5, #2, code_33A
code:00000326 cjne R7, #2, code_330
code:00000329 mov A, R6
code:0000032A orl RESERVED0092, A ; RESERVED
code:0000032C mov A, R6
code:0000032D orl RESERVED0093, A ; RESERVED
code:0000032F retcode:0000033A code_33A: ; CODE XREF: code_DA:code_323↑j
code:0000033A cjne R5, #1, code_353
code:0000033D cjne R7, #2, code_348
code:00000340 mov A, R6
code:00000341 cpl A
code:00000342 anl RESERVED0092, A ; RESERVED
code:00000344 mov A, R6
code:00000345 orl RESERVED0093, A ; RESERVED
code:00000347 retcode:00000348 code_348: ; CODE XREF: code_DA+263↑j
code:00000348 cjne R7, #4, code_36C
code:0000034B mov A, R6
code:0000034C cpl A
code:0000034D anl RESERVED0096, A ; RESERVED
code:0000034F mov A, R6
code:00000350 orl RESERVED0097, A ; RESERVED
code:00000352 retcpl 指令按位取反,A = 1111_0111b。anl RESERVED0096, A清空P3端口输出模式寄存器(P3_MOD_OC) 的bit 3,作用是将P33管脚设置为推挽输出模式。


code:000000DA code_DA: ; CODE XREF: IEX6+279↓p
code:000000DA
code:000000DA ; FUNCTION CHUNK AT code:000002F0 SIZE 0000007D BYTES
code:000000DA
code:000000DA mov DPTR, #0x94
code:000000DD mov A, #1
code:000000DF movx @DPTR, A
code:000000E0 mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:000000E3 ljmp code_2F0
code:000000E3 ; End of function code_DAcode:000000E6 code_E6: ; CODE XREF: IEX6:code_2E7↓p
code:000000E6 mov DPTR, #0x95
code:000000E9 mov A, #1
code:000000EB movx @DPTR, A
code:000000EC mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:000000EF lcall code_3F7
code:000000F2 mov DPTR, #0x3E8
code:000000F5 clr A
code:000000F6 mov B, A ; B-Register
code:000000F8 lcall code_529
code:000000FB mov DPTR, #0x95
code:000000FE clr A
code:000000FF movx @DPTR, A
code:00000100 mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:00000103 lcall code_3F7
code:00000106 mov DPTR, #0x3E8
code:00000109 clr A
code:0000010A mov B, A ; B-Register
code:0000010C ljmp code_529
code:0000010C ; End of function code_E6code:000003F7 code_3F7: ; CODE XREF: code_E6+9↑p
code:000003F7 ; code_E6+1D↑p
code:000003F7 mov A, DPL ; Data Pointer, Low Byte
code:000003F9 mov R7, A
code:000003FA mov DPTR, #0x8D5
code:000003FD movc A, @A+DPTR
code:000003FE mov R6, A
code:000003FF mov A, R7
code:00000400 mov DPTR, #0x925
code:00000403 movc A, @A+DPTR
code:00000404 mov R5, A
code:00000405 mov A, R7
code:00000406 mov DPTR, #0x8FD
code:00000409 movc A, @A+DPTR
code:0000040A mov R7, A
code:0000040B mov A, R6
code:0000040C jz code_41B
code:0000040E mov DPL, R6 ; Data Pointer, Low Byte
code:00000410 push RAM_7
code:00000412 push RAM_5
code:00000414 lcall code_36D
code:00000417 pop RAM_5
code:00000419 pop RAM_7code:000008F5 .byte 0
code:000008F6 .byte 0
code:000008F7 .byte 2
code:000008F8 .byte 0
code:000008F9 .byte 0
code:000008FA .byte 0
code:000008FB .byte 0
code:000008FC .byte 0code:00000941 .byte 0
code:00000942 .byte 0
code:00000943 .byte 1
code:00000944 .byte 2
code:00000945 .byte 4
code:00000946 .byte 8
code:00000947 .byte 0x10
code:00000948 .byte 0x20
code:00000949 .byte 0x40 ; @
code:0000094A .byte 0x80code:0000091B .byte 4
code:0000091C .byte 4
code:0000091D .byte 4
code:0000091E .byte 4
code:0000091F .byte 4
code:00000920 .byte 4
code:00000921 .byte 4
code:00000922 .byte 4code:0000041B code_41B: ; CODE XREF: code_3F7+15↑j
code:0000041B mov C, EAL ; Interrupt Enable Register 0
code:0000041D clr A
code:0000041E rlc A
code:0000041F mov R6, A
code:00000420 clr EAL ; Interrupt Enable Register 0
code:00000422 cjne R7, #2, code_427
code:00000425 sjmp code_431code:00000427 code_427: ; CODE XREF: code_3F7+2B↑j
code:00000427 cjne R7, #3, code_42C
code:0000042A sjmp code_443ode:0000042C code_42C: ; CODE XREF: code_3F7:code_427↑j
code:0000042C cjne R7, #4, code_465
code:0000042F sjmp code_455code:00000455 code_455: ; CODE XREF: code_3F7+38↑j
code:00000455 mov DPTR, #0x95
code:00000458 movx A, @DPTR
code:00000459 jnz code_462
code:0000045B mov A, R5
code:0000045C cpl A
code:0000045D mov R7, A
code:0000045E anl P3, A ; Port 3
code:00000460 sjmp code_465code:00000462 code_462: ; CODE XREF: code_3F7+62↑j
code:00000462 mov A, R5
code:00000463 orl P3, A ; Port 3digitalWrite(LED_BUILTIN, HIGH);code:00000465 code_465: ; CODE XREF: code_3F7:code_42C↑j
code:00000465 ; code_3F7+45↑j ...
code:00000465 mov A, R6
code:00000466 jz code_46A
code:00000468 setb EAL ; Interrupt Enable Register 0
code:0000046A
code:0000046A code_46A: ; CODE XREF: code_3F7+6F↑j
code:0000046A retcode:000000E6 code_E6: ; CODE XREF: IEX6:code_2E7↓p
code:000000E6 mov DPTR, #0x95
code:000000E9 mov A, #1
code:000000EB movx @DPTR, A
code:000000EC mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:000000EF lcall code_3F7
code:000000F2 mov DPTR, #0x3E8
code:000000F5 clr A
code:000000F6 mov B, A ; B-Register
code:000000F8 lcall code_529
code:000000FB mov DPTR, #0x95
code:000000FE clr A
code:000000FF movx @DPTR, A
code:00000100 mov DPL, #0x21 ; '!' ; Data Pointer, Low Byte
code:00000103 lcall code_3F7
code:00000106 mov DPTR, #0x3E8
code:00000109 clr A
code:0000010A mov B, A ; B-Register
code:0000010C ljmp code_529
code:0000010C ; End of function code_E6delay(1000);的延迟参数1000 ,清空累加器A,清空寄存器B,长调用 code_529code:00000529 code_529: ; CODE XREF: code_E6+12↑p
code:00000529 ; code_E6+26↑j
code:00000529 mov RAM_10, DPL ; Data Pointer, Low Byte
code:0000052C mov RAM_11, DPH ; Data Pointer, High Byte
code:0000052F mov RAM_12, B ; B-Register
code:00000532 mov RAM_13, A
code:00000534 lcall code_485
code:00000537 mov RAM_14, DPL ; Data Pointer, Low Byte
code:0000053A mov RAM_15, DPH ; Data Pointer, High Byte
code:0000053D mov RAM_16, B ; B-Register
code:00000540 mov RAM_17, A
code:00000485 code_485: ; CODE XREF: code_529+B↓p
code:00000485 ; code_529+46↓p
code:00000485 mov C, EAL ; Interrupt Enable Register 0
code:00000487 clr EAL ; Interrupt Enable Register 0
code:00000489 mov R0, RAM_8
code:0000048B mov R1, RAM_9
code:0000048D mov R2, RAM_A
code:0000048F mov R3, RAM_B
code:00000491 mov R4, TL0 ; Timer 0, Low Byte
code:00000493 mov B, TCON ; Timer 0/1 Control Register
code:00000496 mov EAL, C ; Interrupt Enable Register 0
code:00000498 jnb B5, code_4AD ; B-Register
code:0000049B mov A, #1
code:0000049D add A, R4
code:0000049E jz code_4AD
code:000004A0 inc R0
code:000004A1 cjne R0, #0, code_4AD
code:000004A4 inc R1
code:000004A5 cjne R1, #0, code_4AD
code:000004A8 inc R2
code:000004A9 cjne R2, #0, code_4AD
code:000004AC inc R3
code:000004AD code_4AD: ; CODE XREF: code_485+13↑j
code:000004AD ; code_485+19↑j ...
code:000004AD clr C
code:000004AE mov A, R4
code:000004AF subb A, #6
code:000004B1 mov R4, A
code:000004B2 mov A, R4
code:000004B3 clr C
code:000004B4 rrc A
code:000004B5 mov R4, A
code:000004B6 mov B, #0x7D ; '}' ; B-Register
code:000004B9 mov A, R0
code:000004BA mul AB
code:000004BB mov R0, A
code:000004BC mov R5, B ; B-Register
code:000004BE mov B, #0x7D ; '}' ; B-Register
code:000004C1 mov A, R1
code:000004C2 mul AB
code:000004C3 add A, R5
code:000004C4 mov R1, A
code:000004C5 clr A
code:000004C6 addc A, B ; B-Register
code:000004C8 mov R5, A
code:000004C9 mov B, #0x7D ; '}' ; B-Register
code:000004CC mov A, R2
code:000004CD mul AB
code:000004CE add A, R5
code:000004CF mov R2, A
code:000004D0 clr A
code:000004D1 addc A, B ; B-Register
code:000004D3 mov R5, A
code:000004D4 mov B, #0x7D ; '}' ; B-Register
code:000004D7 mov A, R3
code:000004D8 mul AB
code:000004D9 add A, R5
code:000004DA mov R3, A
code:000004DB mov R5, #0
code:000004DD mov A, R4
code:000004DE add A, R0
code:000004DF mov DPL, A ; Data Pointer, Low Byte
code:000004E1 mov A, R1
code:000004E2 addc A, R5
code:000004E3 mov DPH, A ; Data Pointer, High Byte
code:000004E5 mov A, R2
code:000004E6 addc A, R5
code:000004E7 mov B, A ; B-Register
code:000004E9 mov A, R3
code:000004EA addc A, R5
code:000004EB retrrc A ,此时寄存器C为0,A = A >> 1 = 0x28,再保存到寄存器R4中。mul AB,乘法,结果高8位在寄存器B,低8位在累加器A。B = 0x00,A = 0x00。code:00000529 code_529: ; CODE XREF: code_E6+12↑p
code:00000529 ; code_E6+26↑j
code:00000529 mov RAM_10, DPL ; Data Pointer, Low Byte
code:0000052C mov RAM_11, DPH ; Data Pointer, High Byte
code:0000052F mov RAM_12, B ; B-Register
code:00000532 mov RAM_13, A
code:00000534 lcall code_485
code:00000537 mov RAM_14, DPL ; Data Pointer, Low Byte
code:0000053A mov RAM_15, DPH ; Data Pointer, High Byte
code:0000053D mov RAM_16, B ; B-Register
code:00000540 mov RAM_17, Acode:00000542 code_542: ; CODE XREF: code_529+3C↓j
code:00000542 ; code_529+76↓j
code:00000542 mov A, RAM_10
code:00000544 orl A, RAM_11
code:00000546 orl A, RAM_12
code:00000548 orl A, RAM_13
code:0000054A jnz code_54D
code:0000054C retcode:0000054D code_54D: ; CODE XREF: code_529+21↑j
code:0000054D mov R0, RAM_10
code:0000054F mov R1, RAM_11
code:00000551 mov R2, RAM_12
code:00000553 mov R3, RAM_13
code:00000555 mov RAM_18, RAM_14
code:00000558 mov RAM_19, RAM_15
code:0000055B mov RAM_1A, RAM_16
code:0000055E mov RAM_1B, RAM_17code:00000561 code_561: ; CODE XREF: code_529+AF↓j
code:00000561 mov A, R0
code:00000562 orl A, R1
code:00000563 orl A, R2
code:00000564 orl A, R3
code:00000565 jz code_542
code:00000567 push RAM_3
code:00000569 push RAM_2
code:0000056B push RAM_1
code:0000056D push RAM_0
code:0000056F lcall code_485
code:00000572 mov R4, DPL ; Data Pointer, Low Byte
code:00000574 mov R5, DPH ; Data Pointer, High Byte
code:00000576 mov R6, B ; B-Register
code:00000578 mov R7, A
code:00000579 pop RAM_0
code:0000057B pop RAM_1
code:0000057D pop RAM_2
code:0000057F pop RAM_3
code:00000581 mov A, R4
code:00000582 clr C
code:00000583 subb A, RAM_18
code:00000585 mov R4, A
code:00000586 mov A, R5
code:00000587 subb A, RAM_19
code:00000589 mov R5, A
code:0000058A mov A, R6
code:0000058B subb A, RAM_1A
code:0000058D mov R6, A
code:0000058E mov A, R7
code:0000058F subb A, RAM_1B
code:00000591 mov R7, A
code:00000592 clr C
code:00000593 mov A, R4
code:00000594 subb A, #0xE8
code:00000596 mov A, R5
code:00000597 subb A, #3
code:00000599 mov A, R6
code:0000059A subb A, #0
code:0000059C mov A, R7
code:0000059D subb A, #0
code:0000059F jc code_542
code:000005A1 dec R0
code:000005A2 cjne R0, #0xFF, code_5AE
code:000005A5 dec R1
code:000005A6 cjne R1, #0xFF, code_5AE
code:000005A9 dec R2
code:000005AA cjne R2, #0xFF, code_5AE
code:000005AD dec R3
push RAM_3其实是 push 0x03,BANK0_R3,也就是当前的R3。code:00000485 code_485: ; CODE XREF: code_529+B↓p
code:00000485 ; code_529+46↓p
code:00000485 mov C, EAL ; Interrupt Enable Register 0
code:00000487 clr EAL ; Interrupt Enable Register 0
code:00000489 mov R0, RAM_8
code:0000048B mov R1, RAM_9
code:0000048D mov R2, RAM_A
code:0000048F mov R3, RAM_B
code:00000491 mov R4, TL0 ; Timer 0, Low Byte
code:00000493 mov B, TCON ; Timer 0/1 Control Register
code:00000496 mov EAL, C ; Interrupt Enable Register 0
code:00000498 jnb B5, code_4AD ; B-Register
code:0000049B mov A, #1
code:0000049D add A, R4
code:0000049E jz code_4AD
code:000004A0 inc R0
code:000004A1 cjne R0, #0, code_4AD
code:000004A4 inc R1
code:000004A5 cjne R1, #0, code_4AD
code:000004A8 inc R2
code:000004A9 cjne R2, #0, code_4AD
code:000004AC inc R3code:0000000B ; Timer 0 overflow
code:0000000B ; Attributes: thunk
code:0000000B
code:0000000B ; public TF0
code:0000000B TF0:
code:0000000B ljmp TF0_0
code:0000000B ; End of function TF0code:0000046C TF0_0: ; CODE XREF: TF0↑j
code:0000046C push PSW ; Program Status Word Register
code:0000046E mov PSW, #8 ; Program Status Word Register
code:00000471 inc R0
code:00000472 cjne R0, #0, code_482
code:00000475 inc R1
code:00000476 cjne R1, #0, code_482
code:00000479 inc R2
code:0000047A cjne R2, #0, code_482
code:0000047D inc R3
code:0000047E cjne R3, #0, code_482
code:00000481 inc R4
code:00000482
code:00000482 code_482: ; CODE XREF: TF0_0+6↑j
code:00000482 ; TF0_0+A↑j ...
code:00000482 pop PSW ; Program Status Word Register
code:00000484 retimov PSW, #8 RS1=1, RS0=0,这会选择工作寄存器组1,对应物理地址 0x08-0x0Fint pins[8] = {23, 22, 1, 3, 21, 19, 18, 5};
int number_array[][8] = {
//a, b, c, d, e, f, g, dp
{0, 0, 0, 0, 0, 0, 1, 1}, // 0
{1, 0, 0, 1, 1, 1, 1, 1}, // 1
{0, 0, 1, 0, 0, 1, 0, 1}, // 2
{0, 0, 0, 0, 1, 1, 0, 1}, // 3
{1, 0, 0, 1, 1, 0, 0, 1}, // 4
{0, 1, 0, 0, 1, 0, 0, 1}, // 5
{0, 1, 0, 0, 0, 0, 0, 1}, // 6
{0, 0, 0, 1, 1, 1, 1, 1}, // 7
{0, 0, 0, 0, 0, 0, 0, 1}, // 8
{0, 0, 0, 0, 1, 0, 0, 1}, // 9
};
void setup() {
for (int i=0;i<8;i++){
pinMode(pins[i], OUTPUT);
digitalWrite(pins[i], HIGH);
}
}
void loop(){
int num = 6;
for (int i=0;i<8;i++) {
digitalWrite(pins[i], number_array[num][i]);
}
}

int seg_array[4] = {5, 18, 19, 21};
int led_array[8] = {32, 25, 27, 12, 13, 33, 26, 14};
int logic_array[10][8] = {
//a, b, c, d, e, f, g, dp
{1, 1, 1, 1, 1, 1, 0, 0}, // 0
{0, 1, 1, 0, 0, 0, 0, 0}, // 1
{1, 1, 0, 1, 1, 0, 1, 0}, // 2
{1, 1, 1, 1, 0, 0, 1, 0}, // 3
{0, 1, 1, 0, 0, 1, 1, 0}, // 4
{1, 0, 1, 1, 0, 1, 1, 0}, // 5
{1, 0, 1, 1, 1, 1, 1, 0}, // 6
{1, 1, 1, 0, 0, 0, 0, 0}, // 7
{1, 1, 1, 1, 1, 1, 1, 0}, // 8
{1, 1, 1, 1, 0, 1, 1, 0}, // 9
};
void clear(){
for (int i=0;i<4;i++) {
digitalWrite(seg_array[i], HIGH);
}
for (int i=0;i<8;i++){
digitalWrite(led_array[i], LOW);
}
}
void display_number(int order, int number) {
clear();
digitalWrite(seg_array[order], LOW);
for (int i=0;i<8;i++) {
digitalWrite(led_array[i], logic_array[number][i]);
}
}
void display_4_number(int number) {
if (number < 10000){
int number_array[4];
for (int i=3;i>=0;i--) {
number_array[i] = number % 10;
number /= 10;
}
for (int i=0;i<4;i++) {
display_number(i, number_array[i]);
delay(5);
}
}
}
void setup() {
for (int i=0;i<4;i++) {
pinMode(seg_array[i], OUTPUT);
digitalWrite(seg_array[i], HIGH);
}
for (int i=0;i<8;i++){
pinMode(led_array[i], OUTPUT);
digitalWrite(led_array[i], LOW);
}
}
void loop() {
display_4_number(4567);
}
pinMode(button_pin, INPUT_PULLDOWN);int led_pin = 18;
int button_pin = 22;
int led_logic = 0;
bool status = false;
void setup() {
pinMode(led_pin, OUTPUT);
pinMode(button_pin, INPUT_PULLDOWN);
}
void loop() {
if (digitalRead(button_pin)) {
delay(10);
if (digitalRead(button_pin) && !status) {
led_logic = !led_logic;
digitalWrite(led_pin, led_logic);
status = !status;
} else if (!digitalRead(button_pin)) {
status = false;
}
}
}
#define FREQ 2000 // 频率
#define CHANNEL 0 // 通道
#define RESOLUTION 8 // 分辨率
#define LED 18 // LED 引脚
void setup() {
ledcAttach(LED, FREQ, RESOLUTION);
}
void loop() {
for (int i=0;i<pow(2, RESOLUTION);i++) {
ledcWrite(LED, i);
delay(10);
}
for (int i=pow(2,RESOLUTION)-1;i>=0;i--) {
ledcWrite(LED, i);
delay(10);
}
}

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// 定义OLED屏幕参数(0.96寸通常分辨率是128x64)
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1 // 重置引脚,-1表示共享Arduino重置引脚
// 初始化SSD1306对象,使用I2C通信
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(115200);
// 初始化OLED显示屏,I2C地址通常为0x3C或0x3D
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306分配失败"));
for(;;); // 卡在这里
}
Serial.println("OLED初始化成功!");
// 清空缓冲区
display.clearDisplay();
display.setTextSize(2); // 设置文字大小
display.setTextColor(SSD1306_WHITE); // 设置文字颜色(白色)
display.setCursor(0, 0); // 设置光标位置(x, y)
display.println("Hello"); // 显示英文
display.setCursor(0, 20); // 下移光标
display.println("World!");
display.setCursor(0, 40); // 继续下移
display.setTextSize(1); // 使用小号字体显示中文
display.println("Aiden Leong");
// 将缓冲区内容发送到显示屏
display.display();
delay(2000); // 显示2秒
}
void loop() {
// 清屏
display.clearDisplay();
// 显示动态内容 - 模拟计数器
static int counter = 0;
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Counter: ");
display.println(counter++);
display.setCursor(0, 20);
display.println("Aiden Leong");
// 画一条线
display.drawLine(0, 40, 127, 40, SSD1306_WHITE);
// 画一个矩形
display.drawRect(10, 45, 50, 15, SSD1306_WHITE);
// 显示
display.display();
delay(1000); // 每秒更新一次
}

#include <Arduino.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
void setup() {
Serial.begin(115200);
u8g2.begin();
Serial.println("OLED (U8G2) 初始化成功!");
u8g2.enableUTF8Print();
// --- setup() 中的静态显示 ---
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_profont17_tr);
u8g2.drawStr(0, 20, "Hello");
u8g2.drawStr(0, 40, "World!");
u8g2.setFont(u8g2_font_profont12_tr);
u8g2.drawStr(0, 60, "Aiden Leong");
} while ( u8g2.nextPage() );
delay(2000); // 显示2秒
}
void loop() {
// --- loop() 中的动态显示 ---
u8g2.firstPage();
do {
static int counter = 0;
// 使用小号字体显示计数器
u8g2.setFont(u8g2_font_wqy12_t_chinese2);
char counter_str[20];
snprintf(counter_str, sizeof(counter_str), "计数器: %d", counter++);
u8g2.setCursor(0, 12);
u8g2.print(counter_str);
u8g2.setCursor(0, 32);
u8g2.print("你好");
// 画线和矩形 (保持不变)
u8g2.drawLine(0, 40, 127, 40);
u8g2.drawFrame(10, 45, 50, 15);
} while ( u8g2.nextPage() );
delay(1000);
}

/etc/update-motd.d目录中,以shell脚本形式提供Welcome to Ubuntu 24.04.3 LTS (GNU/Linux 6.8.0-79-generic aarch64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Sep 18 01:34:49 AM UTC 2025
System load: 0.0
Usage of /: 11.7% of 57.25GB
Memory usage: 4%
Swap usage: 0%
Processes: 121
Users logged in: 0
IPv4 address for enp0s1: 192.168.64.2
IPv6 address for enp0s1: fd74:46d5:8a72:b02c:689d:40ff:fe98:b46c
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
Expanded Security Maintenance for Applications is not enabled.
19 updates can be applied immediately.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Wed Sep 3 03:22:35 2025 from 192.168.64.1rsync -avz --delete --include 'dir1/***' --include 'package.json' --exclude='*' . root@xx.xx.xx.xx:/root/remote_target/#include <linux/init.h> // 包含模块初始化和清理函数的宏
#include <linux/module.h> // 必须包含的核心头文件,用于所有模块
#include <linux/kernel.h> // 包含内核函数,如 printk()
// 模块许可证。强烈推荐设置,否则内核会发出警告。
MODULE_LICENSE("GPL");
// 模块作者信息
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World kernel module.");
MODULE_VERSION("0.1");
// 模块初始化函数
// 当使用 'insmod' 或 'modprobe' 加载模块时,这个函数会被调用
static int __init hello_init(void) {
// printk 是内核中的 printf 函数
// KERN_INFO 是日志级别,表示这是一般信息性消息
// 它会输出到内核的日志缓冲区,可以用 'dmesg' 命令查看
printk(KERN_INFO "Hello, World! The module has been loaded.\n");
return 0; // 返回 0 表示成功
}
// 模块清理函数
// 当使用 'rmmod' 卸载模块时,这个函数会被调用
static void __exit hello_exit(void) {
printk(KERN_INFO "Goodbye, World! The module has been unloaded.\n");
}
// 注册模块的初始化和清理函数
// module_init 告诉内核,hello_init 是加载时要执行的函数
module_init(hello_init);
// module_exit 告诉内核,hello_exit 是卸载时要执行的函数
module_exit(hello_exit);
# obj-m 是一个变量,告诉 kbuild (内核构建系统) 我们要构建一个可加载的模块
# hello.o 是我们的目标文件名,- 表示它是一个模块,最终会生成 hello.ko
obj-m += hello.o
# all 是默认的目标
# make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# -C: 切换到指定目录
# /lib/modules/$(shell uname -r)/build: 这是内核源代码或头文件所在的位置
# M=$(PWD): 告诉内核构建系统,我们的模块源代码在当前目录
# modules: 指定要执行的操作是编译模块
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
# clean 是清理的目标,用于删除编译过程中产生的文件
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
sudo insmod hello.komodprobe -r hellolsmod命令可以列出当前系统已加载的内核模块。$ lsmod
Module Size Used by
tls 159744 0
qrtr 49152 2
cfg80211 1249280 0
snd_hda_codec_generic 114688 1
snd_hda_intel 57344 0
snd_intel_dspcfg 20480 1 snd_hda_intel
snd_hda_codec 208896 2 snd_hda_codec_generic,snd_hda_intel
binfmt_misc 28672 1
snd_hda_core 163840 3 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec
snd_hwdep 24576 1 snd_hda_codec
snd_pcm 196608 3 snd_hda_intel,snd_hda_codec,snd_hda_core
snd_timer 53248 1 snd_pcm
snd 147456 6 snd_hda_codec_generic,snd_hwdep,snd_hda_intel,snd_hda_codec,snd_timer,snd_pcm
9pnet_virtio 20480 0
9pnet 106496 1 9pnet_virtio
soundcore 16384 1 snd
nls_iso8859_1 12288 1
input_leds 12288 0
joydev 36864 0
uas 32768 0
usb_storage 90112 1 uas
sch_fq_codel 24576 2
dm_multipath 49152 0
efi_pstore 12288 0
nfnetlink 20480 2
dmi_sysfs 24576 0
qemu_fw_cfg 24576 0
ip_tables 36864 0
x_tables 65536 1 ip_tables
autofs4 57344 2
btrfs 1929216 0
blake2b_generic 24576 0
hid_generic 12288 0
usbhid 81920 0
hid 184320 2 usbhid,hid_generic
raid10 77824 0
raid456 212992 0
async_raid6_recov 24576 1 raid456
async_memcpy 16384 2 raid456,async_raid6_recov
async_pq 16384 2 raid456,async_raid6_recov
async_xor 16384 3 async_pq,raid456,async_raid6_recov
async_tx 16384 5 async_pq,async_memcpy,async_xor,raid456,async_raid6_recov
xor 12288 2 async_xor,btrfs
xor_neon 16384 1 xor
raid6_pq 110592 4 async_pq,btrfs,raid456,async_raid6_recov
libcrc32c 12288 2 btrfs,raid456
raid1 61440 0
raid0 24576 0
crct10dif_ce 12288 1
polyval_ce 12288 0
polyval_generic 12288 1 polyval_ce
ghash_ce 24576 0
sm4 12288 0
sha3_ce 16384 0
sha2_ce 20480 0
sha256_arm64 24576 1 sha2_ce
sha1_ce 12288 0
virtio_gpu 94208 0
virtio_dma_buf 12288 1 virtio_gpu
virtio_rng 12288 0
xhci_pci 28672 0
xhci_pci_renesas 24576 1 xhci_pci
aes_neon_bs 24576 0
aes_neon_blk 28672 1 aes_neon_bs
aes_ce_blk 36864 0
aes_ce_cipher 12288 1 aes_ce_blklsmod的原理是读取/proc/modules文件的内容模块名称 内存占用 引用计数 依赖模块 模块状态 内存地址$ cat /proc/modules
tls 159744 0 - Live 0x0000000000000000
qrtr 49152 2 - Live 0x0000000000000000
cfg80211 1249280 0 - Live 0x0000000000000000
snd_hda_codec_generic 114688 1 - Live 0x0000000000000000
snd_hda_intel 57344 0 - Live 0x0000000000000000
snd_intel_dspcfg 20480 1 snd_hda_intel, Live 0x0000000000000000
snd_hda_codec 208896 2 snd_hda_codec_generic,snd_hda_intel, Live 0x0000000000000000
binfmt_misc 28672 1 - Live 0x0000000000000000
snd_hda_core 163840 3 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec, Live 0x0000000000000000
snd_hwdep 24576 1 snd_hda_codec, Live 0x0000000000000000
snd_pcm 196608 3 snd_hda_intel,snd_hda_codec,snd_hda_core, Live 0x0000000000000000
snd_timer 53248 1 snd_pcm, Live 0x0000000000000000
snd 147456 6 snd_hda_codec_generic,snd_hda_intel,snd_hda_codec,snd_hwdep,snd_pcm,snd_timer, Live 0x0000000000000000
9pnet_virtio 20480 0 - Live 0x0000000000000000
9pnet 106496 1 9pnet_virtio, Live 0x0000000000000000
soundcore 16384 1 snd, Live 0x0000000000000000
nls_iso8859_1 12288 1 - Live 0x0000000000000000
input_leds 12288 0 - Live 0x0000000000000000
joydev 36864 0 - Live 0x0000000000000000
uas 32768 0 - Live 0x0000000000000000
usb_storage 90112 1 uas, Live 0x0000000000000000
sch_fq_codel 24576 2 - Live 0x0000000000000000
dm_multipath 49152 0 - Live 0x0000000000000000
efi_pstore 12288 0 - Live 0x0000000000000000
nfnetlink 20480 2 - Live 0x0000000000000000
dmi_sysfs 24576 0 - Live 0x0000000000000000
qemu_fw_cfg 24576 0 - Live 0x0000000000000000
ip_tables 36864 0 - Live 0x0000000000000000
x_tables 65536 1 ip_tables, Live 0x0000000000000000
autofs4 57344 2 - Live 0x0000000000000000
btrfs 1929216 0 - Live 0x0000000000000000
blake2b_generic 24576 0 - Live 0x0000000000000000
hid_generic 12288 0 - Live 0x0000000000000000
usbhid 81920 0 - Live 0x0000000000000000
hid 184320 2 hid_generic,usbhid, Live 0x0000000000000000
raid10 77824 0 - Live 0x0000000000000000
raid456 212992 0 - Live 0x0000000000000000
async_raid6_recov 24576 1 raid456, Live 0x0000000000000000
async_memcpy 16384 2 raid456,async_raid6_recov, Live 0x0000000000000000
async_pq 16384 2 raid456,async_raid6_recov, Live 0x0000000000000000
async_xor 16384 3 raid456,async_raid6_recov,async_pq, Live 0x0000000000000000
async_tx 16384 5 raid456,async_raid6_recov,async_memcpy,async_pq,async_xor, Live 0x0000000000000000
xor 12288 2 btrfs,async_xor, Live 0x0000000000000000
xor_neon 16384 1 xor, Live 0x0000000000000000
raid6_pq 110592 4 btrfs,raid456,async_raid6_recov,async_pq, Live 0x0000000000000000
libcrc32c 12288 2 btrfs,raid456, Live 0x0000000000000000
raid1 61440 0 - Live 0x0000000000000000
raid0 24576 0 - Live 0x0000000000000000
crct10dif_ce 12288 1 - Live 0x0000000000000000
polyval_ce 12288 0 - Live 0x0000000000000000
polyval_generic 12288 1 polyval_ce, Live 0x0000000000000000
ghash_ce 24576 0 - Live 0x0000000000000000
sm4 12288 0 - Live 0x0000000000000000
sha3_ce 16384 0 - Live 0x0000000000000000
sha2_ce 20480 0 - Live 0x0000000000000000
sha256_arm64 24576 1 sha2_ce, Live 0x0000000000000000
sha1_ce 12288 0 - Live 0x0000000000000000
virtio_gpu 94208 0 - Live 0x0000000000000000
virtio_dma_buf 12288 1 virtio_gpu, Live 0x0000000000000000
virtio_rng 12288 0 - Live 0x0000000000000000
xhci_pci 28672 0 - Live 0x0000000000000000
xhci_pci_renesas 24576 1 xhci_pci, Live 0x0000000000000000
aes_neon_bs 24576 0 - Live 0x0000000000000000
aes_neon_blk 28672 1 aes_neon_bs, Live 0x0000000000000000
aes_ce_blk 36864 0 - Live 0x0000000000000000
aes_ce_cipher 12288 1 aes_ce_blk, Live 0x0000000000000000dmesg命令用于打印内核日志,它可以从内核日志缓冲区中读取日志信息。在内核模块中,可以使用printk函数将日志信息输出到内核日志缓冲区中,然后使用dmesg命令查看这些日志信息。/proc/echo_driver写入的内容将被打印到内核日志中(使用dmesg查看)#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h> // 用于 copy_from_user 和 copy_to_user
#include <linux/string.h>
#define MODULE_NAME "echo_proc"
#define PROC_ENTRY_NAME "echo_driver"
#define MAX_BUFFER_LEN 1024
static char proc_buffer[MAX_BUFFER_LEN];
static unsigned long proc_buffer_size = 0;
// 当用户读取 /proc/echo_driver 时,此函数被调用
static ssize_t proc_read(struct file *file, char __user *user_buf, size_t count, loff_t *offp)
{
ssize_t len = 0;
// 如果偏移量不为0,说明已经读完了,返回0表示文件结束
if (*offp > 0) {
return 0;
}
// 确保不会读取超出缓冲区大小的数据
if (count > proc_buffer_size) {
len = proc_buffer_size;
} else {
len = count;
}
// 将内核空间的数据复制到用户空间
if (copy_to_user(user_buf, proc_buffer, len) != 0) {
// 复制失败
return -EFAULT;
}
// 更新偏移量
*offp += len;
printk(KERN_INFO "echo_proc: Read %zu bytes from buffer\n", len);
return len;
}
// 当用户写入 /proc/echo_driver 时,此函数被调用
static ssize_t proc_write(struct file *file, const char __user *user_buf, size_t count, loff_t *offp)
{
ssize_t len = 0;
// 确保写入的数据不会超过缓冲区大小
if (count > MAX_BUFFER_LEN - 1) {
printk(KERN_WARNING "echo_proc: Write size %zu exceeds buffer limit\n", count);
len = MAX_BUFFER_LEN - 1;
} else {
len = count;
}
// 将用户空间的数据复制到内核空间
if (copy_from_user(proc_buffer, user_buf, len) != 0) {
// 复制失败
return -EFAULT;
}
// 在字符串末尾添加空字符,以便安全地作为字符串处理
proc_buffer[len] = '\0';
proc_buffer_size = len;
// 移除可能的换行符,方便读取时显示
if (len > 0 && proc_buffer[len - 1] == '\n') {
proc_buffer[len - 1] = '\0';
proc_buffer_size--;
}
printk(KERN_INFO "echo_proc: Wrote %zu bytes to buffer: %s\n", proc_buffer_size, proc_buffer);
return len; // 返回实际写入的字节数
}
// 定义文件操作结构体
static const struct proc_ops my_proc_ops = {
.proc_read = proc_read,
.proc_write = proc_write,
};
// 模块初始化函数
static int __init echo_proc_init(void)
{
struct proc_dir_entry *proc_entry;
// 在 /proc 下创建一个条目
proc_entry = proc_create(PROC_ENTRY_NAME, 0666, NULL, &my_proc_ops);
if (proc_entry == NULL) {
printk(KERN_ERR "echo_proc: Failed to create /proc/%s\n", PROC_ENTRY_NAME);
return -ENOMEM;
}
printk(KERN_INFO "echo_proc: Module loaded. /proc/%s created.\n", PROC_ENTRY_NAME);
return 0;
}
// 模块退出函数
static void __exit echo_proc_exit(void)
{
// 移除 /proc 条目
remove_proc_entry(PROC_ENTRY_NAME, NULL);
printk(KERN_INFO "echo_proc: Module unloaded. /proc/%s removed.\n", PROC_ENTRY_NAME);
}
module_init(echo_proc_init);
module_exit(echo_proc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple echo driver using /proc");
MODULE_VERSION("0.1");__init宏用于标记初始化函数,这些函数在模块加载时执行,并在模块卸载时释放。使用__init宏标记的函数在编译时会被放在一个特殊的段中,这个段在模块加载时会被映射到内存中,并在模块卸载时被释放。module_init宏用于标记模块初始化函数,该函数在模块加载时被调用。使用module_init宏标记的函数必须在模块加载时执行,并且必须在模块卸载时释放。printk函数用于在内核中输出日志信息。它类似于用户空间的printf函数,但是它的输出会被重定向到内核日志缓冲区,而不是标准输出。使用printk函数可以方便地调试内核模块。module_param宏用于定义模块参数,这些参数可以在模块加载时通过命令行参数进行设置。使用module_param宏定义的参数可以在模块加载时通过命令行参数进行设置,例如:insmod hello.ko param1=10 param2="hello"。proc_create函数用于创建一个proc文件,该文件可以用于在用户空间和内核空间之间进行通信。使用proc_create函数创建的proc文件可以在/proc目录下看到,并且可以通过cat命令查看其内容。name:proc文件的名称。mode:proc文件的权限。parent: parent目录。proc_fops:proc文件的操作函数,用于定义如何读写proc文件。struct proc_dir_entry *proc_entry;
proc_entry = proc_create(PROC_ENTRY_NAME, 0666, NULL, &my_proc_ops);
if (proc_entry == NULL) {
printk(KERN_ERR "echo_proc: Failed to create /proc/%s\n", PROC_ENTRY_NAME);
return -ENOMEM;
}struct proc_dir_entry *mydir;
mydir = proc_mkdir("mydriver", NULL); // 创建 /proc/mydriver
if (mydir) {
proc_create("status", 0444, mydir, &status_proc_ops); // 创建 /proc/mydriver/status
}proc_write函数用于接收来自proc文件的写入。当用户在用户空间中写入proc文件时,proc_write函数会被调用,并将用户写入的数据作为参数传递给该函数。printk和pr_info等pr_开头的函数都可以用来打印内核日志。二者区别仅仅在于,pr_开头的函数是printk的封装,它从字面以上就可以知道logging level,也会提供预先配置好的打印格式。Loading image...



apt updateapt upgradeapt install build-essential cmake git vim完全一致版本的内核头文件。apt install linux-headers-$(uname -r)make时已经生成,位于arch/x86/include/generated(或对应CPU架构)目录中。