Linux bottom layer

1. ARM basic knowledge

Basic: C language with a certain hardware foundation

Features---"Back and Back Contact 

arm target:

      Understand simple assembly code  

      Can read circuit diagrams, chip manuals

      Learn how to use software to control hardware ideas

      Solutions to the problem

Talk about the understanding of embedded?

     Centered on computing applications, a dedicated computer system with tailorable software and hardware. There are certain requirements for power consumption, volume, performance, cost, etc.

Features: strong specificity, dedicated computer; large difference in operating environment; less resources than general-purpose PCs (applicability, just enough); low power consumption, small size, high integration, low cost; long life cycle

Learning arm focuses on two aspects: the principle of program operation and the principle of hardware control.

1. Basic computer theory  

     In computer systems, high and low levels are used to represent logic 1 and 0

The storage, transmission, and operation (processing of data) of data in the computer are all carried out in binary form

Through data transmission, what is actually transmitted through the bus is an electrical signal, high and low levels (0, 1). Memory only has high and low levels. The operation is performed in the circuit, and the operation is completed in the integrated circuit.

2. The composition of the computer   

    Composed of input devices, output devices, memory, calculators, and controllers

 1. Input device: converts other signals into signals (electrical signals) that the computer can recognize.

 2. Output device: Convert electrical signals (0, 1) into signals that people or other devices can understand.

 3. Memory: A component that stores programs and data, and is also the basis for computers to realize "stored program control".

       Program: an ordered collection of instructions //assembly instructions   

       ROM: flash (EMMC), disk space, no data loss when power off

       RAM: memory, lost data when power off

     + -->Summation command  

    32 is the operating system, addressing is 32 bits. Addressing space bits 2^32,4G.

  4. Calculator: The part of the CPU for information processing and calculation, often performs arithmetic and logic operations. Its core is the arithmetic logic unit ALUCPU, which uses various digital circuits to form a variety of calculation circuits, such as: addition, subtraction, etc.

Such as the addition operator:

  5. Controller: the command center of the entire computer

The key thing I want to learn is: the principle of program operation

think:

   1.运算器不同,处理指令不同,及对应指令集不同。不同的处理器上如何运行同一个c语言程序?

3. 指令的解析

一条指令(机器码)的执行通常分为三个阶段:

1)取指:控制器将PC寄存器中的值发送给内存,内存将对应地址中的指令(机器码)传送回CPU的指令寄存器IR中
2)译码:指令译码器对IR中的指令进行识别,即将指令(机器码)翻译成具体的运算操作(+/-/*...)
3)执行:运算器执行对应的指令并将结果写入寄存器

 执行完一条指令后CPU内对应的硬件会将PC的值自动增加使PC指向内存中的下一条指令

  •    指令的执行是按照流水线
  • 取指--》取指器  根据PC值取指令
  • 译码--》译码器 
  • 执行--》执行器  

以上三个器件,都是单周期的器件,三个器件的工作是独立,

指令1  指令2  指令3  指令4  指令5

1 取指 

2 译码   取指 

3 执行   译码   取指 

4    执行   译码    取指 

5                     执行    译码    取指

6   执行    译码

7                                     执行

  • PC永远指向当前正在取指指令的地址,一旦取到指令,pc后移4byte,保存下一条指令地址。

4. 编译原理

 CPU能够识别的唯一的语言是机器码,一个CPU能够识别哪些机器码是由处理器的硬件(运算器种类)决定的不同的机器码代表不同的运算,同样不同的CPU的机器码是不通用的即不可以移植,汇编是用一个标识符来代表一个机器码,所以不同的CPU汇编也不一样,即汇编语言不可以移植。

     C语言编译的时候我们可以使用不同的编译器将C编译成不同的汇编和机器码,所以C可以不依赖CPU架构

总结面试题:

           1.指令解析的过程

           2.为什么不同处理器,要用不同的编译器编译程序代码?编译原理

5. 认识ARM    

  • ARM含义?

1. ARM代表一个公司

2. ARM表示一种技术

3. ARM可以表示一些处理器的统称

  • 架构:

          arm-v4,arm-v5,arm-v6,arm-v7(32Bits),arm-v8(64Bits)

架构指支持的汇编指令集  

  • 内核:

         cortex-a9,a53,a73,a77     

         ARM公司授权芯片的公司,芯片产家在内核的基础上,增加了一些外设,发布一款芯片,这些芯片可以统称为SOC

  • SOC:

     System Of Chip: System On Chip), S5P6818, Snapdragon 855 (Qualcomm), Kirin 990 (Hisilicon)

s5p6818:

  • ARM History

1. In 1978, CPU Corporation 

Cambridge processing Unit (Cambridge Warehouse)

2. 1979 Acorn (assembles computers)

3. 1985, 32-bit, 8MHz, 

Reduced instruction set RISC used

The name of the chip ARM ----" Acorn RISC machine (Acorn RISC machine)

4. In 1990,

iPhone £1.5m VLSI: £250,000

12 engineers + technical patents: £1.5 million

ARM Company - "Advanced RISC Machine (Advanced Reduced Instruction Set Computer)

ARM does not produce chips, but licenses technology and provides solutions.

For example: Xiaomi mobile phone (bought Qualcomm chip + UI + camera optimization, etc.)

5. In 2016, Japan’s Softbank acquired

  • Instruction Set
  • Reduced instruction set (RISC) --> microprocessor (reduced instruction set computer)

Select some relatively simple and frequently used instructions in the complex instruction set

The width of the instruction is fixed, and most of them are single-cycle instructions.  

       Example: If there is an addition operator, there is no multiplication operator 3*3 ---》3+3+3  

  • Complex instruction set (CISC) --> computer CPU (complex instruction set computer)

Pay attention to the functionality of the instruction, the cycle of the instruction, and the width of the instruction is not fixed

  eg Simplification: After compiling, use disassembly to view code instructions.  

             Compile the program with a cross-compilation tool to generate an executable program for arm

                   arm-linux-gnueabinf-gcc  1.c

                   file a.out --> View executable file properties

            Use the disassembly command to convert the elf file to a disassembly file.dis

            arm-linux-gnueabinf-objdump -D  a.out > a.dis

View the instructions of the ubuntu complex instruction set:

         gcc 1.c ---> Compile and generate a.out executable file

          file a.out view file properties

         objdump -D a.out > a.dis ----> disassembly

  • ARM company product distribution
  • Cortex-A:

      Qualcomm, MediaTek, HiSilicon, Samsung, Freescale, for cutting-edge virtual memory-based operating systems and user applications.

  • Cortex-R:

        Real-time processors provide high-performance solutions for embedded systems that require reliability, fault tolerance, and real-time responsiveness. Automotive electronics, camera cameras.

  • Cortex-M: MCU 

       For cost- and power-sensitive MCU and terminal applications, generally do not run the operating system, but can run real-time operating systems: FreeRTOS, uCosII, LiteOS (Huawei), STMicroelectronics (ST) STM32 series

  • ARM architecture: ARM-v8(A)--->Cortex-A53(8-core)-->S5P6818 Main frequency: 1.4GHZ

  •  ARM data type conventions

     ARM-v7 architecture: 32bit processor   

  • char: 8 bits
  • halfword: 16 bits
  • word: 32 bits
  • doubleword:64位(cortex-a)    

    ARM-v8 architecture: 64bit processor, backward compatible with 32bit (we learn 32bit)

  • char: 8 bits
  • halfword: 16 bits
  • word: 32 bits
  • doubleword:64位(cortex-a)
  • quadword: 128-bit (ARM-v8)

  • What do 32-bit and 64-bit processors mean?
  • 32-bit: One instruction can perform operations on 32-bit data
  • 64-bit: One instruction can perform operations on 64-bit data

  • Most ARM cores provide:

ARM-v7 architecture: 

  • ARM instruction set (32-bit) 

One instruction occupies 32 bits of memory space

  • Thumb instruction set (16-bit)

One instruction occupies 16 bits of memory space

ARM-V8: backward compatible with ARM-v7 architecture

  • ARM instruction set: A64, A32
  • Thumb instruction set: T32, T16
  • Regardless of whether it is A64 or A32, each instruction occupies 32 bits of space
  • Regardless of whether it is T32 or T16, each instruction occupies 16 bits of space

ARM-v7:

ARM instruction set: A32

Thumb指令集:T16

ARM指令集功能更全,性能更高

thumb指令集比ARM指令集指令密度要大

  • ARM处理器的工作模式

ARM内核的命名规格历史

ARM7 ARM9 ARM10 ARM11 ,ARM11之后,命名规格改变

Cortex-A9 A53 A75

ARM7-11 有7种基本工作模式:

  • User :  非特权模式,大部分任务执行在这种模式   
  • FIQ (Fast Interrupt Request) :   当一个高优先级(fast) 中断产生时将会进入这种模式
  • IRQ (Interrupt Request):   当一个低优先级(normal) 中断产生时将会进入这种模式

    FIQ和IRQ打断当前正在做的事去做其他的事情,做了再回来继续做自己的事情。鼠标键盘等都是这样实现的。(Linux内核会有中断,驱动写中断驱动代码)

中断的概念:

  • Supervisor(SVC) :  当复位或软中断指令执行时将会进入这种模式

 (任务的切换会切入这个模式,权限最高的模式,刚启动的时候在这个模式下,权限高,可以做一些核心的操作。进行系统调用的时候会切换这个模式。)

  • Abort :   当指令存取异常时将会进入这种模式  
  • Undef :   当执行未定义指令时会进入这种模式  
  • System :  使用和User模式相同寄存器集的特权模式

保证不同任务每次调用同一个函数都是从头开始。

Cortex-A特有模式:

  • Monitor : 是为了安全而扩展出的用于执行安全监控代码的模式;

也是一种特权模式

特定的模式拥有特定的权限,执行特定的代码,完成特定的功能

  • CPU(内核)组成:
  • 运算器

加法运算 --》加法器 --》加法指令

  • 控制器
  • 存储器---》REG  Register:此寄存器由ARM公司集成到CPU的内部来存放机器码
  • A32:每个寄存器可以存储一个32位数据
  • A64:每个寄存器可以存储一个64位数据

   总结:

  • ARM7,9,11 有37个32-Bits长的寄存器
  •     1 个用作PC( program counter)
  •     1 for CPSR (current program status register)
  •     5 for SPSR (saved program status registers)
  •     30 general purpose registers
  •    Cortex-A has 3 more registers and 40 32-Bits long registers
  •    Monitor mode r13_mon , r14_mon , spsr_mon
  •    R0-r15, CPSR, SPSR These registers are provided by ARM, and each register is 32 bits.

           These registers have no address, only a unique number, through which the corresponding address space can be accessed. R0- R15, cpsr, spsr are the corresponding numbers, how many binary digits does each number correspond to? 32

  • R13: Stack pointer register

          the stack pointer, sp

Store the address of the top of the stack

  • R14: link register

the link register, lr

  • When the function is called, save the return address

(Save the address of the next instruction corresponding to the instruction of the call letter, and give the lr value to pc when returning, and continue to execute the next code)

 

  • R15: Program count register

the program counter, pc

  • Store the address of the current fetch instruction

The cpu takes instructions (values) one by one from the memory

  • CPSR: Current Program Status Register
  • Store the current program running status

current program status register, cpsr

These NZCV bits are called condition bits, and the next eight bits are called control bits (indicated by C) 

  • SPSR: Register to save program state

saved program status register

  • for saving cpsr

  • Clock: responsible for sending out the clock signal for the CPU to start timing.

Two, arm assembly instruction learning

1. Basic concepts

  • What codes can generate assembly instructions in c language

      1 "The statement with ';' can be compiled to generate instructions

   2 "Preprocessing with '#' sign, how to compile the auxiliary compiler, and what content to compile

  • Compile the overall taxonomy

        1》指令:  编译完生成一条机器码存储在内存单元当中,CPU执行时能完成对应的操作(类似于C中的语句)

     2》伪操作 (相当于c中的’#‘的内容)告诉编译器怎么编译):不会生成机器码也不会占用内存,其作用是告诉编译器怎样编译(类似于C中的预处理指令)

     3》伪指令  (如:cpu中没有乘法器,对应没有乘法指令,3*3 ---》用加法器实现3+3+3,替换实现):不是指令,编译器在编译时将其替换成等效的指令

  汇编中注释代码用'@'注释一行 ,注释一段代码 /**/

  • 指令分类
1.数据处理指令:		对数据进行逻辑、算数运算
2.跳转指令:			实现程序的跳转,实质是修改PC
3.Load/Store指令:   对内存的读写操作//如 a++ 读a的值,将运算结果从cpu写道内存
4.状态寄存器传送指:	对CPSR进行读写操作//其他都不能动CPSR
5.异常中断产生指令:	触发软中断,常用于内核的系统调用 //SWI:软中断
6.协处理器指令:		操作协处理器的指令
    //如3*3 ---》用加法器实现3+3+3,比较慢。我们可以外接一个协处理器(乘法器)(每个协处理器的功能比较单一),协处理器指令就是操作这个协处理器的,用的比较多的cp15协处理器。

  • 汇编指令代码框架
.text   @声明一段代码
.global _start      @将_start 声明为一个全局的符号,其他.s文件也可以引用
                   @如调用函数  func  :  .global func
_start:              @汇编的入口
     @汇编代码段
     
.end                @汇编的结束

2. 汇编指令

  •   指令的语法格式:
  •  <opcode><code>{s} Rd,Rn,oprand2
  •   opcode:指令的名字
  •   code:条件码(if else),可以省略不写,默认指令是无条件执行
  •   s:状态标志

       加s,指令的执行结果影响cpsr的NZCV位,

       不加s,不影响

  • Rd:目标寄存器
  • Rn:第一个操作寄存器
  • oprand2:第二个操作数,可以是普通寄存器,可以是立即数
  •  注:指令的名字,条件码,s连到一起写,指令名和目标寄存器之间使用空格,寄存器和数据之间使用逗号隔开,指令中的字符不区分大小写

2.1 数据处理指令

  •  1》数据搬移指令  mov
  • 格式:<opcode><code>{s} Rd,oprand2
  • 如果是立即数,前边必须加#

  • PC寄存器详细讲解:

  • 指令的执行三步:取地,译码,执行(PC永远指向当前正在取指指令的地址)

2》立即数:立即数是保存在指令中的数,取指令的同时将值取过去,和普通变量的区别是,变量保存在内存中的数据,需要单独取值运算。

   立即数的本质:立即数是包含在指令当中的数据(即属于指令的一部分)

   立即数的优点:读取指令的同时也将立即数读取到了内存中,速度快

   立即数的缺点:数量有限

如:MOV ,#0x12345678   @报错,不合法

注:使用mov 给寄存器里面存放值的时候,#号后面需是有效数(1:立即数,2:取反之后是立即数),如果不是立即数需要用ldr指令进行存放。

如果不是立即数,用伪指令ldr  赋值

  • 3》算数运算指令

算数运算指令 add adc sub sbc mul

数据运算指令格式s

<操作码><目标寄存器><第一操作寄存器><第二操作数>

         ADD R3,R1,R2 ;R2可以是立即数 只有乘法这不能为立即数

操作码 指定当前指令是哪种运算

目标寄存器 存放运算结果

第一操作寄存器 存放参与运算的一个数据(只能是寄存器)

第二操作数 存放参与运算的另一个数据(可以是寄存器/立即数)

  • add 普通的加法指令

  • adc 带进位的加法指令

           假设2个64位的数相加

  • 第一个64位的数,R0存放低32位,R1存放高32位,
  • 第二个64位的数,R2存放低32位,R3存放高32位
  • 结果R4存放低32位,R5存放高32位

  • 注意:mul r2, r0, #0x4  @ 错误

 乘法指令的第二个操作数只能是一个寄存器

 mul r2, r1,r0

2.2 跳转指令

1》修改PC,不建议使用,因为需要查询指令的地址

2》  b   bl :指令跳转

  • 格式:b/bl Label 
  • Label: 指令
  • 相当C语言的函数调用      
  • B指令(不带返回的跳转)       

      不保存返回地址的跳转(返回地址不保存到lr中)

  • BL指令(带返回的跳转指令),将LR的值修改成跳转指令下一条指令的地址,再将PC的值修改成跳转标识符下指令的地址

补充了解:

RM指令条件码表:可跟的判断条件成立跳转(NZCV在用于判断两者之间关系使用比较多)

如:c代码如下:

练习:
    实现以下逻辑
		unsigned int r1 = 9;
		unsigned int r2 = 15;
		while(1)
		{
        
        
		        if(r1 == r2)
				goto stop;
			if(r1 > r2)
		            	r1 = r1 - r2;
		        if(r1 < r2)
		         	r2 = r2 - r1;
		}
	stop:
		        while(1);

汇编指令练习答案如下:
    mov r1,#9
    mov r2,#15
loop:
     cmp r1,r2  @cmp 比较指令
      beq  stop
      subhi r1,r1,r2
      subcc r2,r2,r1
      b loop
stop:
    b stop

 2.3 Load/Store指令

    对内存的读写操作//如 a++ 读a的值,将运算结果从cpu写到内存

可用地址查找:(我们不用查找,脚本文件中配置了内存空间的分配)   

查看内存中内容:

1>单寄存器操作指令 ldr/str

 格式:ldr/str  Rm, [Rn]

 Rm: 存储是数据

 Rn:存储的数据,地址

将CPU中r1寄存器中的数据存储到内存中r0地址的空间中

  • 将r0指向的地址空间中的内容,读到r2寄存器中
  • ldr r2, [r0]

  • 将r1中的值存储到r0+4指向的地址空间中,R0中的值不变
  • str r1, [r0, #4];

  • 将r2中的值存储到r0指向的地址空间中,r0 = r0 + 4
  • str r2, [r0], #4

  • 将R3中的值存储到R0+4指向的地址空间中,并且r0 = r0 + 4
  • str r3, [r0, #4]!

 

  • 2>多寄存器操作指令  stm ldm
  • 将r1到r4中的值存储到r0指向地址空间中,连续16个字节的地址空间
  • stm r0, {r1-r4}

  • 将r0指向的地址空间中,连续的16个字节的数据,读到r5-r8寄存器中
  • ldm r0, {r5-r8}

  • 如果寄存器列表中的寄存器编号既有连续又有不连续,连续的使用“-”隔开,不连续的使用“,”
  • stm r0, {r1-r3,r4}

  • 2. 不管寄存器列表中的寄存器编号顺序如何变化,都是小地址对应小编号的寄存器高地址对应大编号的寄存器
  • stm r0, {r4,r3,r2,r1}
  • ldm r0, {r8,r7,r6,r5}

  • 3>栈的操作指令 stmfd  ldmfd
  • 栈的种类
    • 空栈(Empty)

栈指针指向的地址是空的,在栈中存储数据时,可以直接存储,存储完成之后需要将栈指针再次指向空的位置。

    • 满栈(Full)

栈指针指向的地址有数据,在栈中存储数据时,需要先将栈指针,指向一个空的位置,然后在存储数据。

    • 增栈(Ascending)

栈指针向高地址方向移动

    • 减栈(Descending)

栈指针向低地址方向移动

操作栈的方式有四种

  • 满增栈 满减栈 空增栈 空减栈
  • FA:Full Ascending  满增(FA)
  • FD:Full Descending 满减(FD)
  • EA:Empty Ascending 空增(EA)
  • ED:空减
  • ARM默认采用的是满减栈

  • stmfd/ldmfd sp!, {寄存器列表}
  • stmfd sp!, {r1-r5}(写) (压栈)

更新栈指针指向的地址空间

  • ldmfd sp!, {r6-r10}(读) (出栈)

特殊:

stmfd sp!, {r1-r5,lr}(写) (压栈)  

ldmfd sp!, {r6-r10,pc}(读) (出栈)    //r1-r5出栈给r6-r10, 将lr的值出栈给pc

.text
.globl _start
_start:
/*
   @1.数据处理指令
   @1》数据搬移指令  mov
   mov r0,#0x1
   mov r1,#2
   @mov r2,#0x00103000 不是立即数
   mov r3,#0x00108000   @是立即数据
   @mov r3,#0x12345678  
   @error: invalid constant (12345678) after fixup 
   @#0x1是立即数,携带在指令的数据,取指令的时候
   @可以同时将数据取过来使用,读取数据快
   @判断那些是立即数:将一个数据转化为二进制,所有的1组合起来可以
   @形成一个0-255之间的数据,且将这个数据循环右移偶数位可以得到
   @这个数据本身得数叫立即数。
   
   @2》指令
   ldr r4,=0x12345678
   mov r5,#0xfffffff  
   @mvn r5,#0xf0000000 @按位取反指令
  
   
   @3》算数运算
   mov r0,#1
   ldr r1,=0xffffffff
   adds r2,r1,#0x2
   adds r3,r1,r0
   
   @举例:两个64位数据得加减运算 
   @0x00000030 fffffffe
   @0x00000041 00000005
   
   ldr r0,=0x00000030
   ldr r1,=0xfffffffe
   ldr r2,=0x00000041
   ldr r3,=0x00000005
   
   mov r8,#6
   mov r9,#5
   mul r10,r8,r9
   
   adds r5,r1,r3
   adcs  r4,r0,r2
   adc r11,r8,r9
   
   subs r7,r3,r1
   sbc r6,r2,r0
 
   @2.跳转指令 本质修改pc的值
   @mov pc,#0x00000005   
   @b(不携带返回的跳转) bl(携带返回的跳转)-会自动更新lr的值
   ldr r0,=0x00000030
   ldr r1,=0xfffffffe
   bl loop
   ldr r2,=0x00000041
   ldr r3,=0x00000005
   b stop

loop:
   mov r8,#6
   mov r9,#5
   mov pc,lr
    
 
   @3.Load/Store指令
   @1》单寄存器操作指令 ldr/str
   ldr r0,=0x40000100
   ldr r1,=0x12345678
  @ str r1,[r0]  @将r1的值写道r0保存的内存地址中
  @ ldr r2,[r0]  @将r0保存的内存地址中的值读到r2中
   @str r1,[r0,#8] @将r1的值写道r0+8内存地址中
   @str r1,[r0],#8 @将r1的值写道r0保存的内存地址中,r0=r0+8
   str r1,[r0,#4]!@将r1的值写道r0+4内存地址中,且r0=r0+4
  
   @2》多寄存器操作指令 ldm/stm
   ldr r0,=0x40000100
   ldr r1,=0x11234567
   ldr r2,=0x22234567
   ldr r3,=0x33234567
   bl add
   ldr r4,=0x44234567
   ldr r7,=0x77234567
   ldr r8,=0x88234567
   
  @ stm r0!,{r1-r4,r7,r8}
   stm r0,{r1-r4,r7,r8}
   ldr r1,=0xffffffff
   ldm r0,{r3,r1,r4,r6,r2,r5}
    */
   @3》栈的操作指令 stmfd  ldmfd  sp
   ldr sp,=0x40000200
   ldr r1,=0x11234567
   ldr r2,=0x22234567
   ldr r3,=0x33234567
   ldr r4,=0x44234567
   ldr r7,=0x77234567
   ldr r8,=0x88234567
   
   stmfd sp!,{r1-r4,r7-r8}
   
   ldr r1,=0x1
   ldr r2,=0x2
   ldr r3,=0x3
   ldr r4,=0x4
   ldr r7,=0x7
   ldr r8,=0x8
   
   ldmfd sp!,{r1-r4,r7-r8}
   
  
 stop:
  b stop   @while(1)
  
 .end
	 

2.4 状态寄存器指令

     对CPSR进行读写操作//其他都不能动CPSR   (SWI 指令是linux内核有,所以arm为了匹配才有的指令)(CPSR保存cpu的状态、模式、中断中断开关、运算状态,非常重要,不能任意更改,只有一类指令能操作这个寄存器)

1》读cpsr  指令mrs 

2》写cpsr 指令 msr   :一般情况不能修改cpsr,只能用msr命令修改,user模式下不能切换到其他模式。

Note: To modify the control field of CPSR ( bit [ 7 :0 ]) , you must specify which area to modify when modifying CPSR

        In USER mode, the value of CPSR cannot be modified to prevent applications from modifying the CPU state and protect the operating system

CPSR_C modifies the lower eight ctrl (control) field of CPSR , and generally only modifies the C field

2.5 Abnormal interrupt generation instruction (in case of exception handling)

Trigger soft interrupt, often used in kernel system calls //SWI: soft interrupt

Verification of the processing code that actually triggers the soft interrupt:

3. Exception source and handling process

  • Pattern review:
ARM7-11 有7种基本工作模式:(不同的模式下干不同的事,效率高)
User :  非特权模式,大部分任务执行在这种模式
FIQ :   当一个高优先级(fast) 中断产生时将会进入这种模式
IRQ :   当一个低优先级(normal) 中断产生时将会进入这种模式
Supervisor :  当复位或软中断指令执行时将会进入这种模式
Abort :   当存取异常时将会进入这种模式
Undef :   当执行未定义指令时会进入这种模式
System :  使用和User模式相同寄存器集的特权模式
Cortex-A特有模式:
Monitor : 是为了安全而扩展出的用于执行安全监控代码的模式;也是一种特权模式

3.1 Abnormal

Exception is the most important knowledge point to understand the operation of CPU. Almost every processor supports specific exception handling. Interrupt is one of the exceptions. Sometimes we measure the real-time performance of an operating system by looking at the shortest response interrupt time of the os and the number of response interrupts per unit time.

Note: When the processor encounters an exception, it will suspend the current program and turn to handle the exception (execute the exception handler), and return to the code interrupted by the exception to continue execution after the processing is completed

3.2 Exception sources

  There are 7 events that lead to exceptions: FIQ, IR Q (most contacted), Reset (reset), soft interrupt, DataAbort, PrefetchAbort, Undef.

1reset复位异常 (svc)
    当CPU刚上电时或按下reset重启键之后进入该异常,该异常在管理模式下处理。
    
2irq/fiq一般/快速中断请求  (irq、fiq)
        CPU和外部设备是分别独立的硬件执行单元,CPU对全部设备进行管理和资源调度处理,CPU要想知道外部设备的运行状态,要么CPU定时的去查看外部设备特定寄存器,要么让外部设备在出现需要CPU干涉处理时“打断”CPU,让它来处理外部设备的请求,毫无疑问第二种方式更合理,可以让CPU“专心”去工作,这里的“打断”操作就叫做中断请求,根据请求的紧急情况,中断请求分一般中断和快速中断,快速中断具有最高中断优先级和最小的中断延迟,通常用于处理高速数据传输及通道的中数据恢复处理,如DMA等,绝大部分外设使用一般中断请求。
        
3预取指令中止异常     PrefetchAbort(abort)
      该异常发生在CPU流水线取阶段,如果目标指令地址是非法地址进入该异常,该异常在中止异常模式下处理。
      
4未定义指令异常(Undef)
该异常发生在流水线技术里的译码阶段,如果当前指令不能被识别为有效指令,产生未定义指令异常,该异常在未定义异常模式下处理。

5软件中断指令(swi)异常 (svc)
         该异常是应用程序自己调用时产生的,用于用户程序申请访问硬件资源时,例如:printf()打印函数,要将用户数据打印到显示器上,用户程序要想实现打印必须申请使用显示器,而用户程序又没有外设硬件的使用权,只能通过使用软件中断指令切换到内核态,通过操作系统内核代码来访问外设硬件,内核态是工作在特权模式下,操作系统在特权模式下完成将用户数据打印到显示器上。这样做的目的无非是为了保护操作系统的安全和硬件资源的合理使用,该异常在管理模式下处理。
         
6数据中止访问异常 DataAbort (Abort)
该异常发生在要访问数据地址不存在或者为非法地址时,该异常在中止异常模式下处理。

3.3 Exception handling (important)

  Steps (completed automatically):

1. Copy CPSR to SPSR_

Save the cpsr of the running mode to the spsr in the corresponding abnormal mode.

2. Modify CPSR: (Modify cpsr to switch to the corresponding abnormal mode)

a. Enter the ARM state       (mandatory)

b. Enter the corresponding abnormal mode     (switch mode)

c. Prohibit the corresponding interruption     (another exception will not interrupt the current exception handling)

3. Save the return address to LR_    

 Before jumping to exception handling (modified pc), save the address of the next instruction of pc in lr, and then modify pc to jump to the corresponding exception handling position.

4. Set the PC to the corresponding exception vector address (jump to the corresponding position in the exception vector table)

 Modify the PC to switch to the exception handling position

步骤详解:
1》保存执行状态
          当前程序的执行状态是保存在CPSR里面的,异常发生时,要保存当前的CPSR里的执行状态到异常模式里的SPSR里,将来异常返回时,恢复回CPSR,恢复执行状态。
2》模式切换
       硬件自动根据当前的异常类型,将异常码写入CPSR里的M[4:0]模式位,这样CPU就进入了对应异常模式下。不管是在ARM状态下还是在THUMB状态下发生异常,都会强制切换到ARM状态下进行异常的处理,这是由硬件自动完成的,将CPSR[5] 设置为 0。同时,CPU会关闭中断IRQ(设置CPSR 寄存器I位),防止中断进入,如果当前是快速中断FIQ异常,关闭快速中断(设置CPSR寄存器F位)。
      
3》保存返回地址
当前程序被异常打断,切换到异常处理程序里,异常处理完之后,返回当前被打断模式继续执行,因此必须要保存当前执行指令的下一条指令的地址到LR_excep(异常模式下LR,并不存在LR_excep寄存器,为方便读者理解加上_excep,以下道理相同),由于异常模式不同以及ARM内核采用流水线技术,异常处理程序里要根据异常模式计算返回地址。

4》跳入异常向量表
该操作是CPU硬件自动完成的,当异常发生时,CPU强制将PC的值修改为一个固定内存地址,这个固定地址叫做异常向量。

3.4 Exception vector table

    The entire exception handling process is automatically completed, and there will be corresponding hardware inside ARM to automatically complete it, that is, it is fixed when designing ARM, so each exception handling will have a fixed address. The address of the exception vector table cannot be changed, and some new processors can be changed through the coprocessor later.

 If linu wants to modify the start address of the exception vector table, it needs to use the coprocessor to complete it. Generally, the address of the exception vector table will be set at 0xFFFF0000 (if IRQ is generated, the exception vector address will be 0xFFFF0018)

Summarize:

The exception vector table is a space in memory

Four bytes of storage are allocated for each exception source in the exception vector table

When an exception occurs, the value of PC will automatically become the address of the exception source in the exception vector table

We write a jump instruction in the corresponding position of the exception vector table to make it jump to the exception handler entry

The base address of the exception vector table for the Cortex-A series processor can be set by the coprocessor CP15

详解:(帮助理解)
1》跳入异常向量表操作是异常发生时,硬件自动完成的,剩下的异常处理任务完全交给了程序员。由上表可知,异常向量是一个固定的内存地址,我们可以通过向该地址处写一条跳转指令,让它跳向我们自己定义的异常处理程序的入口,就可以完成异常处理了。
2》正是由于异常向量表的存在,才让硬件异常处理和程序员自定义处理程序有机联系起来。异常向量表里0x00000000地址处是reset复位异常,之所以它为0地址,是因为CPU在上电时自动从0地址处加载指令,由此可见将复位异常安装在此地址处也是前后接合起来设计的,其后面分别是其余7种异常向量,每种异常向量都占有四个字节,正好是一条指令的大小,最后一个异常是快速中断异常,将其安装在此也有它的意义,在0x0000001C地址处可以直接存放快速中断的处理程序,不用设置跳转指令,这样可以节省一个时钟周期,加快快速中断处理时间。
3》存储器映射地址0x00000000是为向量表保留的。在有些处理器中,向量表可以选择定位在高地址0xFFFF0000处【可以通过协处理器指令配置】,当今操作系统为了控制内存访问权限,通常会开启虚拟内存,开启了虚拟内存之后,内存的开始空间通常为内核进程空间,和页表空间,异常向量表不能再安装在0地址处了。

3.5 Install and set exception vector table and save field instructions

2》 安装异常向量表
    我们可以通过简单的使用下面的指令来安装异常向量表:
    b reset   ;	跳入reset处理程序
    b undef_handler ;跳入未定义处理程序
    b swi_handler    ;跳入软中断处理程序
    b pref_handler  ;跳入预取指令处理程序
    b data_handler   ;跳入数据访问中止处理程序
    b res ;		跳入未使用程序
    b irq_handler   ;跳入中断处理程序
    b fiq_handler  ;跳入快速中断处理程序
    通常安装完异常向量表,跳到我们自己定义的处理程序入口,这时我们还没有保存被打断程序的现场,因此在异常处理程序的入口里先要保存打断程序现场。
    
2》 保存执行现场
        异常处理程序最开始,要保存被打断程序的执行现场,程序的执行现场无非就是保存当前操作寄存器里的数据,可以通过下面的栈操作指令实现保存现场:
            stmfd sp!, {r0-r1,lr}
            需要注意的是,在跳转到异常处理程序入口时,已经切换到对应异常模式下了,因此这里的SP是异常模式下的SP了,所以被打断程序现场(寄存器数据)是保存在异常模式下的栈里,上述指令将R0~R1全部都保存到了异常模式栈,最后将修改完的被打断程序返回地址入栈保存,之所以保存该返回地址就是将来可以通过类似:MOV PC, LR的指令,返回用户程序继续执行。
            异常发生后,要针对异常类型进行处理,因此,每种异常都有自己的异常处理程序,中断异常处理过程通过系统中断处理来进行分析。

3.6 Return of exception handling (completed by the user himself)

 Write the exception handling code yourself.

 1) Restore CPSR from SPSR_ to restore the processor to the state before the exception

2) Restore the PC from LR_, so that the program returns to the position interrupted by the exception to continue execution

Note: CPSR always saves the current program running status, and SPSR only backs up CPSR when it is abnormal

异常处理完成之后,返回被打断程序继续执行,具体操作如下:
1-》恢复被打断程序运行时寄存器数据(从栈中恢复)
2-》恢复程序运行时状态CPSR(恢复spsr_<mode>到cpsr)
3-》通过进入异常时保存的返回地址,返回到被打断程序继续执行(恢复lr<mode>到pc)

3.7 Correspondence between abnormal source and abnormal mode

Exception source: FIQ IRQ Reset/soft interrupt DataAbort/PrefetchAbort Undef

Abnormal mode: FIQ IRQ SVC Abort Undef

3.8 Abnormal Response Priority

Reset、Data Abort、FIQ、IRQ、Prefetch Abort、SWI、Undefined instruction

   high - low

4. Coprocessor instruction + pseudo-instruction + pseudo-operation (understand)

  • coprocessor instructions

Instructions to operate the coprocessor (not used)       (to assist the cpu to process data)

1.数据运算
2.内存访问
3.与主处理器通信
MRC 将协处理器中寄存器的内容读取到ARM处理器的寄存器中
MCR 将ARM理器中寄存器的内容读取到协处理器的寄存器中
  • directive

  Essence: It is not an instruction itself, but the cpu is replaced with an equivalent operation. (may be programmed as)

举例1:延时一个指令周期(耗时一条指令的时间)  (cpu没有这个指令)
	NOP    ;执行NOP和MOV R0,R0一个效果,执行NOP,cpu替换成MOV R0,R0。
	MOV R0,R0  
LDR的两种形式         
;->指令
        LDR R1,[R2]     
;->伪指令
	LDR R1,=0x12345678     ;R1 = 0x12345678
	;可以将任何一个32bit的数据放入寄存器

  • fake operation

  The instructions are specified by the arm company, and the pseudo-operations are specified by the compiler. Different compilers have different pseudo-operation instructions.

(We learn linux, use linux compiler) 

5. Compile Supplementary Content

 位运算指令 and orr eor bic
     格式:
     <opcode><code>{s} Rd, Rn, oprand2
     mov r0, #0xFF 
     [7:4]bits 清零
     and r1, r0, #0xFFFFFF0F
     and r1, r0, #(~(0xF << 4))
    
    [11:8]bits 置1
    orr r2, r0, #(0xF << 8)
    
    [9:6]bits 取反
    eor r3, r0, #(0xF << 6)
    
    bic 位清除指令,把哪位清0,就给哪位写1
    [4:0]bits 清零
    bic r4, r0, #0x1F
    
比较指令  cmp 比较两个数大小
    格式:
              cmp  Rn, oprand2 
    没有目标寄存器,指令的结果影响cpsr的nzcv位
    并且不需要加s
    本质:做减法运算
    例子:比较r0和r1的大小,
    如果r0 > r1,r0 = r0 - r1
    如果r0 < r1,r1 = r1 - r0
    mov r0, #5
    mov r1, #9
    cmp r0, r1
    subhi r0, r0, r1
    subcc r1, r1, r0

6. CPU control hardware principle

   Of all the instructions we have learned, among the six major instructions, only memory access instructions can access content other than the CPU. How does the cpu control the hardware? *****load/store command--"operate 4G memory

Any chip has an address mapping table. Tell you how the address space is mapped so that we can find the corresponding hardware address.

Our SOC model is S5P6818, and the corresponding chip user manual is: (recommended: quick PDF reader) 

S5P6818X User Manual V0.00

One of the chapters is: You can see the address mapping relationship in a memory map or a table in the Memory Controller.

Hardware control principle: *****

    The CPU cannot directly control the hardware, and the hardware is controlled by its corresponding controller (register)

    Each controller (register) is mapped to a section of space within the CPU address range

    The CPU achieves indirect control of the hardware by reading and writing the controller (register) 

Hardware board introduction:

 

There is a map.lds in the provided project template, which guides how the program is arranged. To run a bare-metal program, you first need to read this file.


OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
       
       
	. = 0x43c00000; //起始地址,异常向量表的起始地址
	. = ALIGN(4);   
	.text      :  //代码
	{
       
       
		./Start/start.o(.text) 
                       //运行的第一段代码是start.s编译生成的.o
		*(.text)
	}
	. = ALIGN(4);
    .rodata : //只读  
	{ *(.rodata) }
    . = ALIGN(4);
    .data :   //.data
	{ *(.data) }
    . = ALIGN(4);
	__bss_start = .; 
    .bss ://.bss
     { *(.bss) }
	__bss_end__ = .;
}

7. start start code

start.S文件:
.text        @ .:伪操作
	.arm

	.global	_start
_start:   @第一段代码
	@ 第一条指令,内存地址0x43c00000
	@ 异常向量表
	b reset       @直接通过跳转指令跳转到reset
	b .
	b .
	b .
	b .
	b .
	b irq	@ IRQ异常对应的位置
	b .

 /* The actual reset code */
reset:
	/* 将异常向量表的基地址重定向到0x43c00000,即这段代码执行后遇到IRQ后PC的值会变成0x43c00000 + 0x18 */
	ldr	r0,=0x43c00000   @伪指令,将0x43c00000 放到r0中
	mcr	p15,0,r0,c12,c0,0		@ Vector Base Address Register
                     @p15是一个协处理器,管内存的,c12,c0都是p15里边的寄存器。c12管异常向量表的位置的。
	mrc p15, 0, r0, c1, c0, 0             @mrc是协处理器指令
	bic r0, #(1<<13)
	mcr p15, 0, r0, c1, c0, 0

	/* 设置CPU为SVC模式 32位数据处理 ARM状态 */
	mrs r0, cpsr
	bic r0, r0, #0x1f
	orr r0, r0, #0xd3
	msr cpsr, r0

	/* Enable NEON/VFP unit */ 设置协处理器,浮点运算的协处理器
	mrc p15, #0, r1, c1, c0, #2
	orr r1, r1, #(0xf << 20)
	mcr p15, #0, r1, c1, c0, #2
	mov r1, #0
	mcr p15, #0, r1, c7, c5, #4
	mov r0, #0x40000000
	fmxr fpexc, r0

	/* Cache init */协处理器设置高速缓存,比内存读取更快
	mrc	p15, 0, r0, c0, c0, 0
	and	r1, r0, #0x00f00000
	and	r2, r0, #0x0000000f
	orr r2, r2, r1, lsr #20-4
	cmp r2, #0x30
	mrceq p15, 0, r0, c1, c0, 1
	orreq r0, r0, #0x6
	mcreq p15, 0, r0, c1, c0, 1

	/* Invalidate L1 I/D */
	mov r0, #0
	mcr	p15, 0, r0, c8, c7, 0
	mcr	p15, 0, r0, c7, c5, 0

	/* 关闭MMU,所以该代码运行在实际的物理地址上 */mmc负责虚拟地址和虚拟地址转换的,关闭,操作的是实际的物理地址。
/*mmu:内存管理单元,基于操作系统才有*/
	mrc p15, 0, r0, c1, c0, 0
	bic r0, r0, #0x00002000
	bic r0, r0, #0x00000007
	orr r0, r0, #0x00001000
	orr r0, r0, #0x00000002
	orr r0, r0, #0x00000800
	mcr p15, 0, r0, c1, c0, 0

	/* 初始化各个模式下的栈 */每个模式下都有自己栈指针,即每个模式都有自己的栈地址。每个模式下的栈初始化都需要切换到这模式下。

	/*********svc mode stack************/
	mrs r0, cpsr
	bic r0, r0, #0xdf

	orr r1, r0, #0xd3
	msr cpsr, r1
	ldr sp, _stack_svc_end

	/**********undef mode stack**********/
	orr r1, r0, #0xdb
	msr cpsr, r1
	ldr sp, _stack_und_end

	/*********abort mode stack*********/
	orr r1, r0, #0xd7
	msr cpsr, r1
	ldr sp, _stack_abt_end

	/*********irq mode stack************/
	orr r1, r0, #0xd2
	msr cpsr, r1
	ldr sp, _stack_irq_end

	/*********fiq mode stack************/
	orr r1, r0, #0xd1
	msr cpsr, r1
	ldr sp, _stack_fiq_end

   /*********user mode stack************/     
	orr r1, r0, #0x10
	msr cpsr, r1
	ldr sp, _stack_usr_end
      思考:为什么最后初始化user模式下的栈?因为user模式下不能再切换到其他模式了。

	/*Close Watch Dog Timer*/
	ldr r0, =0xC0012004
	ldr r1, [r0]
	orr r1, r1, #0x04000000
	str r1, [r0]

	ldr r0, =0xC0019000
	ldr r1, [r0]
	and r1, r1, #0xFFFFFFDF
	str r1, [r0]
	
	/* USER模式下的栈放在最后初始化 */
	/*
	 	1.因为USER模式不能切换成其他模式
		2.main执行时CPU处于USER模式
	 */
	/* Call _main */
	b main    @跳转到main函数运行代码。

/* IRQ的异常处理程序 */
irq:
	@ 遇到IRQ异常后CPU自动保存的返回地址是遇到异常时指令下下条指令的地址,所以我们需要人为修正
	sub  lr,lr,#4
	@ 因为IRQ模式下使用的寄存器与USER模式相同(R0-R12),所以在处理异常前要先将之前的寄存器压栈保护
	stmfd sp!,{
       
       r0-r12,lr}
	@ 异常处理,跳转到C处理(也可以用汇编在这写。)
	bl do_irq
	@ 异常返回
	@ 1.将R0-R12寄存器的值出栈恢复
	@ 2.将SPSR的值给CPSR,回到了USER模式
	@ 3.将LR的值给PC,实现程序的返回
	ldmfd sp!,{
       
       r0-r12,pc}^

_stack_svc_end:      .long   stack_svc + 512
_stack_und_end:      .long   stack_und + 512
_stack_abt_end:      .long   stack_abt + 512
_stack_irq_end:      .long   stack_irq + 512
_stack_fiq_end:      .long   stack_fiq + 512
_stack_usr_end:      .long   stack_usr + 512

.data
stack_svc:      .space   512
stack_und:      .space   512
stack_abt:      .space   512
stack_irq:      .space   512
stack_fiq:      .space   512
stack_usr:      .space   512

8. LED experiment

step:

        1. Through the schematic diagram of the bottom board, find the corresponding circuit and analyze how the light turns on. Give a high level light.

        2. Find the corresponding pins through the principle of the core board.

        3. Determine the pin function through the manual -- "GPIO, output a high level light on

        There are 160 GPIOs in total, divided into 5 categories: ABCDE, 32 in each category, numbered 0-31  

          Control Register-->Settings

           1) Select the function of GPIOA28 -- "The corresponding register is GPIOAALTFN1

               地址:0xC001A024          第24位和25位置为0就先择GPIO功能

           2)设置GPIOA28为输出功能--》GPIOAOUTENB

                地址:0xC001A004        第28位置为1是输出模式

           3)设置GPIOA28为输出高电平--》GPIOAOUT

               地址:0xC001A000         第28位置为1红灯亮

本地开发和交叉开发

本地开发:PC端编写代码,编译代码,运行代码

交叉开发:PC端编写代码,编译代码,Target(目标板)运行代码

PC                       Target 

X86架构               arm架构

  • 使用交叉编译工具链将源码编译成支持ARM架构的可执行程序
  • 再将可执行程序拷贝到目标板上运行。

PC:gcc

交叉编译工具链:arm-none-linux-gnueabi-gcc 

注:arm-none-linux-gnueabi-:交叉编译工具链的名字,名字就是一个代号,

在工作中用的不一定是这个,不同的公司做的交叉编译工具链的名字不同

安装交叉编译工具链

  (1)获取交叉编译工具链

一般交叉编译工具链和uboot和linux内核源码,都具有配套的关系。

  • 自己去gnu官网获取交叉编译工具链的源码,自己进行编译生成对应的交叉编译工具链。不推荐:编译过程很繁琐
  • 直接从芯片厂家获取交叉编译工具链
  • 直接跟开发板的生成厂家获取交叉编译工具链
  • 直接找主管获取交叉编译工具链(单位)

**********************************************

(2) Install the cross-compilation toolchain:

installation steps:

1. Create an arm-gcc directory under the home directory of u Ubuntu

$ cd  ~

$ mkdir  arm-gcc

$ cd arm-gcc

Copy the gcc-4.9.4.tar.xz in " 1. Tool software \2. Assembly environment construction \3. Compilation tools" in the student materials to the arm-gcc directory and decompress it

$ tar -xvf   gcc-4.9.4.tar.xz

2. Add the cross-compilation toolchain to the global environment variable to make it globally available

Open the configuration file /etc/bash.bashrc        

$ sudo you   /etc/bash.bashrc

Add the following to its last line

export  PATH=$PATH:/home/hq/arm-gcc/gcc-4.9.4/bin

Check the environment variables in the system: printenv/env

Restart the configuration file to make the configuration take effect

source  /etc/bash.bashrc

3. Test whether the cross-compilation toolchain is successfully installed

arm-none-linux-gnueabi-gcc -v

Print the following content, indicating success

gcc version 4.9.4 (Sourcery G++ Lite 2010.09-50) 

LED experiment

  • Analyzing Circuit Diagrams
  • The idea of ​​​​analyzing the circuit diagram: from the peripheral (floor)---"SOC (core board) analysis 

Analyze LEDs

  • Find the position of the led light on the circuit board
  • There will be white words next to the LED light, this white word is silk screen , the word next to the LED light is the number of the LED light
  • Open the schematic diagram of the bottom board, and search for the LED number (RGB) on the schematic diagram

Analyze the circuit diagram of led

Common anode three-color diode: three diodes , the anodes are connected together

  • RGB_R/RGB_G/RGB_B represent the network label

The same name of the network label means that it has the same electrical connection properties, reflected on the circuit board, and they are connected together by wires

  • According to the network label to the core board schematic diagram, find which pin of the soc drives the LED light
  • 2. Read the chip manual (black box test, white box test )
  • Working registers: R0-R15, cpsr, spsr, provided by ARM, no address
  • Control register: It is a space of memory with an address provided by the chip manufacturer. Registers are used in the GPIO chapter, so read the GPIO chapter when reading the chip manual, there must be the use and function realization of related registers.
  • We only need to write or read a value to the control register to allow our processor to complete certain functions. This is the idea of ​​our software programming to control hardware.

1》GPIOxOUT: control pin output high and low level

RED_LED--->GPIOA28

GPIOAOUT --->  0xC001A000

GPIOA28 output high level:

GPIOAOUT[28] <-- write-- 1

GPIOA28 output low level:

GPIOAOUT[28] <-- write -- 0

2 "GPIOxOUTENB: Control the input and output mode of the pin

GPIOAOUTENB  ---> 0xC001A004

Set the GPIOA28 pin to output mode:

GPIOAOUTENB[28] <-- write -- 1

3》GPIOxALTFN: Control pin function selection

GPIOAALTFN1  ---> 0xC001A024

Set GPIOA28 pin as GPIO function:

GPIOAALTFN1[25:24]  <--写-- 0b00

00 = ALT Function0   

01 = ALT Function1 

10 = ALT Function2   

11 = ALT Function3 

GPIO pin function selection: every two bits control a GPIO pin,

The corresponding functions can be found in the chip manual

2.3 chapter to view.

3. Write the code

1. Set GPIOA28 as GPIO function

2. Set GPIOA28 as output function

while(1)

{

Set GPIOA28 output high level

time delay

Set GPIOA28 output low level

time delay

}


#define GPIOAALTFN1 ((unsigned int *)0xc001a024)
#define GPIOAOUTENB ((unsigned int *)0xc001a004)
#define GPIOAOUT ((unsigned int *)0xc001a000)

#define GPIOEALTFN0 ((unsigned int *)0xc001e020)
#define GPIOEOUTENB ((unsigned int *)0xc001e004)
#define GPIOEOUT ((unsigned int *)0xc001e000)

#define GPIOBALTFN0 ((unsigned int *)0xc001b020)
#define GPIOBOUTENB ((unsigned int *)0xc001b004)
#define GPIOBOUT ((unsigned int *)0xc001b000)
	
void delay_ms(unsigned int ms)
	{
	unsigned int i,j;
	for(i = 0; i < ms; i++)
	for(j = 0; j < 1800; j++);
	}

int main()
{
	*GPIOAALTFN1 &= (~(3<<24));
	*GPIOAOUTENB |= (1<<28);

	*GPIOEALTFN0 &= (~(3<<26));
	*GPIOEOUTENB |= (1<<13);

	*GPIOBALTFN0 |= (1<<25);
	*GPIOBALTFN0 &= (~(1<<24));
	*GPIOBOUTENB |= (1<<12); 
while(1)
	{
	*GPIOAOUT |= (1<<28);
	delay_ms(2000);
	*GPIOAOUT &=(~(1<<28));
	delay_ms(2000); 

	*GPIOEOUT |= (1<<13);
	delay_ms(2000);
	*GPIOEOUT &=(~(1<<13));
	delay_ms(2000);
	
	*GPIOBOUT |= (1<<12);
	delay_ms(2000);
	*GPIOBOUT &=(~(1<<12));
	delay_ms(2000); 
	}
	return 0;
}


4. Download, debug and modify bugs

1》Copy the .bin file to windows

2"Hardware connection between the development board and the computer

Plug the USB end of the serial cable into the USB port of the computer

Insert the serial end of the serial line into the UART0 port of the development board

Plug in the development board

3》Configure Windows HyperTerminal

If the serial port line is used for the first time, the serial port driver needs to be installed

The serial port driver file is in the data

Configure HyperTerminal:

You can view the instructions for configuring HyperTerminal

There are in the data

In the device manager, check the port number used by the serial cable

Configure port properties:

Baud rate: 115200

Data bits: 8

stop bits: 1

Check digit: none

Flow control: no

4》Power on the development board, the HyperTerminal will print information

Press any key before the countdown reaches 0 to enter the FS6818# interface

Execute the command loadb 0x43c00000 --"Download the binary file to memory 0x43c00000

Send--"Send File--"Select the .bin file to download, select the Kermit protocol --" Confirm the download

Execute the command: go 0x43c00000 --" go to 0x43c00000 to run the code

If you need to download the code again, repeat step 4

Modify the countdown time:

setenv  bootdelay  60

saveenv

3. System transplantation

1. Basic concepts

1.1 What   is system transplantation?

It is to port the operating system to the corresponding hardware platform

Porting the linux system to the development board

(Computer-Windows system; mobile phone-Android system, the application program runs,

Only when the system is running can we do driver development)

1.2 Why is the learning system transplanted?

Software and hardware can be cut

The company's new hardware platform---》

Transplant linux system to hardware platform

1.3 What is the purpose of learning system transplantation?

1》Work needs

2》Build a system environment for the following driver development

3"The development of embedded (soft + hard) application layer is inseparable from the operating system

1.4 How to learn about system transplantation?

Just learn the process of transplantation

1.5 Migration process

1. Environment construction   

2. uboot porting (B IOS )

The main functions of uboot are as follows

1) Initialize some hardware to prepare for the follow-up  

2) Boot and load the kernel

3) Pass parameters to the kernel

4) Execute user commands

BIOS--"boot system boot (SD card boot, hard disk boot, U disk boot)

Some hardware is initialized

3. Linux kernel porting 

The kernel in windows is similar

windows=kernel+library+graphical interface+file system

4. Migration of root file system

(Windows C drive, d, etc., Linux is an inverted tree)

2. Local development and cross-development

Local development: write code on PC, compile code, run code

Cross-development: write codes on the PC side, compile the codes, and run the codes on the Target (target board)

(Question: Hasn’t the system been ported to the target board? Why use cross-development, can’t you write and compile code directly on the target board?)

PC  Target 

X86 architecture arm architecture

Use the cross-compilation tool chain to compile the source code into an executable program that supports the ARM architecture, and then copy the executable program to the target board to run.

PC:gcc

Cross-compilation toolchain: arm-none-linux-gnueabi-gcc 

arm-none-linux-gnueabi-: the name of the cross-compilation toolchain, the name is a code name

(This is not necessarily the one used at work, the cross-compilation toolchains made by different companies have different names)

3. Install the cross-compilation toolchain

1. Get the cross-compilation toolchain

Generally, the cross-compilation tool chain and uboot and linux kernel source codes have a supporting relationship.

1"Go to the gnu official website to obtain the source code of the cross-compilation toolchain, and compile it by yourself to generate the corresponding cross-compilation toolchain. Not recommended: the compilation process is cumbersome

2" Obtain the cross-compilation toolchain directly from the chip manufacturer

3" Directly obtain the cross-compilation toolchain from the manufacturer of the development board

4" Directly find the supervisor to obtain the cross-compilation toolchain (unit)

**********************************************

2. Install the cross-compilation toolchain:

Compile the code into an executable program for the ARM architecture

installation steps:

1. In the ubuntu home directory (~), create a toolchain 

mkdir toolchain 

2. Copy gcc-4.6.4.tar.xz to the toolchain directory

cp dir/ gcc-4.6.4.tar.xz   ~/toolchain

3. Unzip the cross-compilation toolchain

tar -vxf gcc-4.6.4.tar.xz

4. Configure environment variables

Open sudo vi /etc/bash.bashrc 

Add the following on the last line:

export PATH=$PATH:/home/linux/toolchain/gcc-4.6.4/bin/

Modify to your own path

5. Make environment variables take effect immediately

source /etc/bash.bashrc

6. Test whether the cross-compilation toolchain is successfully installed

arm-none-linux-gnueabi-gcc -v

Print the following content, indicating success

gcc version 4.6.4 (Sourcery G++ Lite 2010.09-50) 

4. How to make hardware connection between PC and Target

1. Serial port line : print various debugging information to the serial port tool

2. Network cable : used to download images of uboot , kernel and root file system (executable program)

Mount the root file system over the network

Need to install the corresponding server in ubuntu

Download files through network cable---"tftp service

Mount the root file system (board (path) ) through the network cable --- "nfs service

The client uboot and kernel source code of tftp service and nfs service have been installed by default

(server and client)

5. Install tftp service

Tftp is a simple text file transfer protocol based on the udp protocol

1. Check whether the tftp service is installed on ubuntu

dpkg -s tftpd-hpa

Printing the following indicates that the tftp service is installed:

Architecture: i386

Source: tftp-hpa

Version: 5.2-7ubuntu3.1

2. Install tftp service

 (Prerequisite: ubuntu must be able to connect to the external network)

sudo apt-get update  update-source

sudo apt-get install -f  update dependencies

sudo apt-get install tftpd-hpa tftp-hpa 

3. Configure tftp service

1. Create a tftpboot folder in your home directory

mkdir tftpboot 

Purpose: The tftpboot directory stores the files you want to download to

Executables on the board

2. Modify the permissions of tftpboot

sudo chmod 777 tftpboot

3. Configure the environment variables of the tftp service

Open sudo vi /etc/default/tftpd-hpa 

Modify the following:

  1 # /etc/default/tftpd-hpa

  2 

  3 TFTP_USERNAME="tftp" 

tftp user name, no need to modify

  4 TFTP_DIRECTORY="/home/hq/tftpboot"

The storage path of files downloaded by tftp service needs to be modified

Change to your own corresponding tftpboot path

  5 TFTP_ADDRESS="0.0.0.0:69"

The default port number of 69 used by tftp service

  6 TFTP_OPTIONS="-c -s -l"  

The parameters of the tftp service, this needs to be modified

4. Restart the tftp service

1. sudo service tftpd-hpa  re start to start the TFTP service

2. sudo service tftpd-hpa restart Restart the TFTP service

5. Locally test whether the tftp service is successfully installed

(The process of pinging other machines: the local network card driver, to the kernel, the kernel processes this driver, sends the ping packet to the network card of another system, processes this packet, puts the content of the packet into the protocol station (inside the kernel), recognizes the ping packet, and then passes the network card driver to the network card, and returns a packet (all will go to the real physical hardware))

$ tftp 127.0.0.1

tftp> get 1.txt # Download from the tftpboot directory

# 1.txt file to the current directory (this example is,

 Touch 1.TXT under tftpboot, and then run it under the home directory)

  tftp 127.0.0.1;然后get 1.txt,就把tftpboot下面的1.TXT下载到家目录下面了)

tftp> put 2.txt   # 把当前目录中的2.txt文件

 # 上传到tftpboot文件夹中

tftp> q   <回车>  退出

6. 可能出现的问题

下载或上传是,一直卡,

原因:

  • tftp服务安装成功,需要重启tftp服务
  • tftp服务安装不成功
  • 关闭windows和ubuntu的防火墙(防火墙会阻碍数据传输)

6. nfs服务的安装

Network File System

1. 检查nfs服务是否安装

dpkg -s nfs-kernel-server

2. 安装nfs服务(前提:可以上网)

sudo apt-get install nfs-kernel-server

3. 配置nfs服务

1》在家目录下创建nfs文件夹

mkdir nfs 

2》设置文件夹的权限最大

sudo chmod 777 nfs 

3》拷贝根文件系统到nfs目录下

根文件系统一会发给你们(rootfs-A53-ok.tar.xz)

cp /mnt/hgfs/share/rootfs-A53-ok.tar.xz  ~/nfs

4》对根文件系统的压缩包进行解压缩

cd ~/nfs

tar -vxf rootfs-A53-ok.tar.xz

5》配置nfs服务的环境变量

sudo vi /etc/exports

在文件的最后一行添加以下内容:

 /home/hq/nfs/rootfs/  *(rw,sync,no_subtree_check,no_root_squash) 

解析:

/home/ hq /nfs/rootfs/: the path of your own root file system

*: All users, note: no space can appear between * and the following left bracket "(".

rw: read and write permissions

sync: synchronize files

no_subtree_check: Do not check file permissions for subdirectories

no_root_squash: If the client is the root user, then he has root permissions to the entire file

Note: Do not add # in front of this paragraph, # is the comment symbol in this file

 4. Restart the nfs service

1. sudo service nfs-kernel-server start Start nfs service

2. sudo service nfs-kernel-server restart restart nfs service

5. Locally test whether the nfs service is successfully installed

1》Back to the home directory

cd ~

2》sudo mount -t nfs local IP address: /home/ hq /nfs/rootfs/ /mnt

(Local IP address: ubuntu's IP)

sudo mount -t nfs 172.20.10.100:/home/hq/nfs/rootfs  /mnt

sudo mount -t nfs 10.60.138.66:/home/hq/nfs/rootfs/ /mnt

nfs: use the nfs service, set the local IP address: /home/ hq /nfs/rootfs/

The file is mounted to the /mnt directory

3》Check whether the /mnt directory is mounted successfully

cd  /mnt 

ls (At this time, the things under mnt are the same as those under rootfs)

4》Uninstall the mounted file

sudo umount /mnt

Note: Uninstallation commands cannot be executed in the /mnt directory

7. Deploy the operating system to the development board

7.1 Deploy uboot

1 "New development board, everything is blank, make a SD card boot disk

1> Copy the sd card boot disk production tool to the toolchain directory of ubuntu

             cp /mnt/hgfs/share/ sdtool . tar.xz ~/toolchain -raf 

tar -xvf sdtool.tar.xz

2> Go to the sdtool directory

cd ~/toolchain/sdtool 

3> Insert the sd card into the computer and let your ubuntu recognize the sd card

Note: You must use a card reader, not the SD card slot that comes with the computer

Insert the SD card into the card reader, insert the card reader into the computer,

Format the SD card under Windows .

Virtual Machine--"Removable Devices--"realtek USB3.0-CRW---"Connection

What are the files in the sdtool folder?

s5p6818-sdmmc.sh : script for burning uboot to sd card

ubootpak.bin: executable file of uboot

/dev/sdb: device file of sd card

4>  Burn ubootpak.bin to SD card

execute command in ubuntu

sudo ./s5p6818-sdmmc.sh  /dev/sdb ubootpak.bin

If the information is printed, the deployment is successful:

688+1 records in

689+0 records out

352768 bytes (353 kB) copied, 0.00914641 s, 38.6 MB/s

^_^ The image is fused successfully

Summary: report SD card read-only error,

Turn the switch in the SD card to the lock position,

The lock is close to the contact position of the sd card

5> Test whether the sd card is made successfully

Insert the sd card into the development board,

Set the DIP switch on the development board to start for the sd card,

The DIP switch is used to set the startup mode of uboot on the development board

OM1   OM2 OM3   Device

ON   ON    X      nand flash

OFF   ON    X      USB 

ON OFF ON EMMC The flash of this development board is EMMC

OFF   OFF   OFF    SD/TF 

6> Power on the development board again and start successfully.

7.2 System deployment in the development stage

uboot mirror------》Flash(EMMC)    SD

linux kernel image --"

Download directly to the development board memory via network (TFTP) and start the kernel

Root file system image --"

Directly mount the root file system through the network (NFS), (development)

Benefits: High efficiency, the number of reads and writes of Flash is limited .

7.3 Product Release System Deployment

uboot image---------->Flash

linux kernel image------>Flash

Root file system image ----->Flash 

First download the image to the memory using tftp,

Then move from memory to flash,

Then move from flash to memory,

start from memory again

7.4 System deployment in the development stage

1.  Put the kernel image uImage in the tftpboot directory

cp /mnt/hgfs/share/uImage  ~/tftpboot

2. 使用tftpboot命令将uImage下载到0x480000000

tftp 0x48000000 uImage 

3. 设置uboot的自动参数bootargs,

bootargs:启动linux内核是,uboot会将

bootargs后边的参数传递给内核,内核(wind系统)根据这些参数,去设置IP等东西内容为:

bootargs=root=/dev/nfs nfsroot=192.168.1.99:/home/hq/nfs/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.222

root=/dev/nfs :根文件系统的类型

nfsroot=192.168.0.99:/home/hq/nfs/rootfs 根文件系统的服务器的IP和路径

rw :文件系统的可读可写的权限

console=ttySAC0,115200:

使用串口0实现内核和PC的数据的交互,波特率是115200

init=/linuxrc:启动内核之后,运行的1号进程

ip=192.168.1.250 :开发板的IP地址

本次bootargs环境变量设置为:

setenv bootargs  root=/dev/nfs nfsroot=192.168.1.99:/home/hq/nfs/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.222

saveenv

4. Use the bootm command to start the kernel 

bootm kernel address root file system address device tree address

bootm 0x48000000

5. Set uboot to self-start mode, set the bootcmd environment variable

Format:

bootcmd=uboot command 1\;uboot command 2\;uboot command 3\;....

The above three commands will be executed in sequence until the end.

setenv bootcmd uboot command 1\;uboot command 2\;uboot command 3\;.....

eg:

setenv bootcmd tftp 0x48000000 uImage\;bootm 0x48000000

saveenv 

6. Power on the development board again.

(press any key)  

7.5 Product Release System Deployment

1. Put uboot in EMMC

How to put uboot into flash(EMMC)

( uboot is placed in SD, but when the mobile phone leaves the factory, the system cannot be installed in the SD card, right? The system is put in flash when the mobile phone leaves the factory)

Premise: SD card must burn a uboot in advance

1> Start uboot through the SD card and enter the FS6818# interface

2> Copy ubootpak.bin to the tftpboot directory

cp /mnt/hgfs/share/ubootpak.bin  /home/hq/tftpboot 

3> Burn ubootpak.bin into memory using tftp command

tftp 0x41000000 ubootpak.bin (run on the board, pay attention to the link network cable)

         (Download is downloaded to memory, it does not exist when power off)

( The address 0x41000000 , 0x4 2 000000 is also ok)

4> update_mmc (move the data in the memory to emmc, emmc disk space)

update_mmc 2 2ndboot 0x41000000 0x200 0x78000

update_mmc     

- type :  2ndboot | boot | raw | part 

:flash device number EMMC: 2

: type 2ndboot 

: The starting address of uboot in memory , in bytes

: The starting address of flash : in blocks 

: How many pieces of space to download to flash (byte length)

Bytes transferred = 352296 (56028 hex)

The size of a block is 512 bytes

The size of length  > 352296 / 512 =  688.07

fastboot=flash=mmc,

2: ubootpak:2nd:0x200,0x78000;

flash=mmc,2:2ndboot:2nd:0x200,0x4000;

Excuting an order:

update_mmc 2 2ndboot 0x41000000 0x200 0x78000

(run on the board)

Print the following to indicate success

head boot dev  = 2

update mmc.2 type 2ndboot = 0x200(0x1) ~ 0x78000(0x3c0): Done

(Tell the CPU the address of your UBOOT, startup parameters, type, etc., so that when the device is powered on, it will boot uboot to start, and when the kernel is transplanted, uboot will boot the kernel to start)

5> Test whether to update ubootpak.bin to EMMC

Set the DIP switch to switch to EMMC startup

The development board is powered on again.

8.  uboot command (1):

mmc command

mmc info - display info of the current MMC device

Show details of the current MMC settings

mmc read addr blk# cnt

addr: corresponds to the address of the memory

blk#: block number of mmc device

cnt: the number of mmc device blocks

Meaning: use mmc with blk# as the starting block, the data of cnt block size,

Read to the addr address of the memory

(mmc is the hard disk, in blocks, M em is the memory)

mmc write addr blk# cnt

addr: corresponds to the address of the memory

blk#: block number of mmc device

cnt: the number of mmc device blocks

Meaning: set the starting address of the memory to the content at addr

Write to mmc with blk# as the starting block, cnt block size data,

mmc erase blk# cnt

blk#: block number of mmc device

cnt: the number of mmc device blocks

Meaning: Erase the data whose starting block number of mmc is blk#, cnt block size.

The erasing time is affected by the number of cnt blocks,

The larger the cnt, the longer the trial.

( It’s okay not to erase, because when writing, the previous ones are overwritten )

1. Put the kernel image uImage and ramdisk.img in the tftpboot directory

cp /mnt/hgfs/share/uImage  ~/tftpboot

cp /mnt/hgfs/share/ramdisk.img  ~/tftpboot

2. Use the tftpboot command to download uImage to 0x410000000

tftp 0x41000000 uImage 

 (there are 0x2 681 blocks)

3. Move the kernel image in memory to mmc

mmc write 0x41000000 0x800 0x4000

4. Use the tftpboot command to download ramdisk.img to 0x41000000

tftp 0x41000000 ramdisk.img

 

 (has 0x 1369 blocks)

ramdisk.img: image of the root file system

5. Move the root file system image in memory to mmc

mmc write 0x41000000 0x20800 0x20800

6. Set the bootcmd command to start the system from flash

setenv bootcmd mmc read 0x48000000 0x800 0x4000\;mmc read 0x49000000 0x20800 0x20800\;bootm 0x48000000 0x49000000

saveenv 

7. 设置自启动的参数

setenv bootargs root=/dev/ram rw initrd=0x49000040,0x1000000 rootfstype=ext4 init=/linuxrc console=ttySAC0,115200

saveenv

(setenv bootargs root=/dev/ram(从ram里面挂载) rw(文件系统可读可写) initrd=0x49000040(根文件系统的起始地址),0x1000000(大小) rootfstype=ext4(文件系统) init=/linuxrc(启动之后是一号进程) console=ttySAC0(串口调试),115200(波特率))

注意:

0x49000040和0x1000000之间不允许出现空格,

使用英文逗号隔开

root=/dev/ram:从ram(内存)中挂载根文件系统

initrd=0x49000040 0x1000000 :

根文件系统的入口地址,省略前边64字节头

根文件系统的大小0x1000000

rootfstype=ext4:根文件系统的类型

8. 重启开发板,测试是否部署成功(可以关掉虚拟机)

boot:执行boot命令,自动的执行bootcmd环境

变量后边的命令。就不需要重启

9. uboot中的命令(2)

1.help命令

help 查看当前uboot支持的所有的命令命令就是一个字符串,uboot收到字符串之后,会解析字符串,完成对应的功能

FS6818# help

0       - do nothing, unsuccessfully

1       - do nothing, successfully

?       - alias for 'help'

base    - print or set address offset

bdinfo  - print Board Info structure

boot    - boot default, i.e., run 'bootcmd'

bootd   - boot default, i.e., run 'bootcmd'

bootm   - boot application image from memory

bootp   - boot image via network using BOOTP/TFTP protocol

cmd     - cmd [command] options...

cmp     - memory compare

cp      - memory copy

crc32   - checksum calculation

dhcp    - boot image via network using DHCP/TFTP protocol

drawbmp - darw bmpfile on address 'addr' to framebuffer 

env     - environment handling commands

exit    - exit script

ext4load- load binary file from a Ext4 filesystem

ext4ls  - list files in a directory (default /)

ext4write- create a file in the root directory

fastboot- fastboot- use USB Fastboot protocol

fatinfo - print information about filesystem

fatload - load binary file from a dos filesystem

fatls   - list files in a directory (default /)

fatwrite- write file into a dos filesystem

fdisk   - mmc list or create ms-dos partition tables (MAX TABLE 7)

go      - start application at address 'addr'

goimage - start Image at address 'addr'

help    - print command description/usage

i2c     - I2C sub-system

i2cmod  - set I2C mode

iminfo  - print header information for application image

loadb   - load binary file over serial line (kermit mode)

loadbmp - load bmpfile with command or 'bootlog' environment 

loads   - load S-Record file over serial line

loadx   - load binary file over serial line (xmodem mode)

loady   - load binary file over serial line (ymodem mode)

loop    - infinite loop on address range

md      - memory display

mdio    - MDIO utility commands

mii     - MII utility commands

mm      - memory modify (auto-incrementing address)

mmc     - MMC sub system

mmcinfo - display MMC info

mtest   - simple RAM read/write test

mw      - memory write (fill)

nm      - memory modify (constant address)

ping    - send ICMP ECHO_REQUEST to network host

pmic    - PMIC

printenv- print environment variables

reset   - Perform RESET of the CPU

run     - run commands in an environment variable

saveenv - save environment variables to persistent storage

saves   - save S-Record file over serial line

sdfuse  - sdfuse  - read images from FAT partition of SD card and write them to booting device.

setenv  - set environment variables

showvar - print local hushshell variables

source  - run script from memory

test    - minimal test like /bin/sh

tftpboot- boot image via network using TFTP protocol

udown   - Download USB

update_mmc- update mmc data

version - print monitor, compiler and linker version

2. help command 

View the help manual for the command

3. loadb  (help loadb: check the function and usage of loadb)

loadb The address downloaded to memory 

Download the binary file to an address in memory, using the kermit protocol (file transport protocol)

flash              memery

Not lost when power off Lost when power off

slow speed fast

cheap price expensive

Flash access is accessed through memory in bytes

block to access,

512 bytes per block size

4. printenv print uboot environment variables

FS6818# printenv

baudrate=115200

bootargs=root=/dev/nfs nfsroot=192.168.0.222:/home/hqyj/nfs/rootfs,v4,tcp rw console=/dev/ttySAC0,115200 init=/linuxrc ip=192.168.0.250

bootcmd=loadb 43c00000;go 43c00000

bootdelay=3

bootfile=uImage

ethact=dwmac.c0060000

ethaddr=00:e2:1c:ba:e8:60

ethprime=RTL8211

fastboot=flash=mmc,2:ubootpak:2nd:0x200,0x78000;flash=mmc,2:2ndboot:2nd:0x200,0x4000;flash=mmc,2:bootloader:boot:0x8000,0x70000;flash=mmc,2:boot:ext4:0x00100000,0x04000000;flash=mmc,2:system:ext4:0x04100000,0x2F200000;flash=mmc,2:cache:ext4:0x33300000,0x1AC00000;flash=mmc,2:misc:emmc:0x4E000000,0x00800000;flash=mmc,2:recovery:emmc:0x4E900000,0x01600000;flash=mmc,2:userdata:ext4:0x50000000,0x0;

gatewayip=192.168.0.1

ipaddr=192.168.0.250

loadb=0x43c00000;go 0x43c00000

netmask=255.255.255.0

serverip=192.168.0.222

stderr=serial

stdin=serial

stdout=serial

Environment size: 872/32764 bytes

Note: When uboot matches the command, it is a partial match

pri/print/printenv commonly used

5. bootm (it is used to start the kernel) (the go command can only be followed by one address)

The running address of the bootm kernel The running address of the root file system The running address of the device tree  

Boot the linux kernel to start

6. ping command

ping ip address

Whether the development board and the computer can be pinged

7. setenv set environment variables

1》Add environment variables to uboot

setenv environment variable name value corresponding to the environment variable

eg: setenv xiaoming nozuonodie

Note: the equal sign will be added automatically

2 "Delete environment variables

setenv The name of the environment variable to be deleted

eg:setenv  xiaoming 

3 "Modify environment variables

setenv old environment variable name new environment variable value

eg:setenv xiaoming hahaha

After setting the environment variable, the environment variable exists in memory,

Data will be lost after power on again

8. saveenv save environment variables to flash (MMC)

10. The role of environment variables in uboot

baudrate=115200 baud rate

bootdelay=3 countdown time after uboot starts

gatewayip=192.168.0.1 gateway

ipaddr=192.168.0.100 The ip address of the development board (FS6818)

netmask=255.255.255.0 subnet mask

serverip=192.168.0.99 The   ip address of the server (PC: Ubuntu)

11. Test the use of ping command and tftp command

0》How to connect the development board and PC

1》Set the IP address of ubuntu

1> Modify the ip address of ubuntu

(1) Set the ubuntu system to use a wired network card

(2) Set the ubuntu system to a fixed IP address

(3) Check whether the IP address is set successfully

ifconfig 

2》Set the IP address of the development board

setenv serverip 192.168.0.99

setenv ipaddr 192.168.0.100

setenv gatewayip 192.168.0.1

setenv netmask 255.255.255.0

saveenv 

3"Test the development board can pass through Ubuntu

Execute the following command on HyperTerminal

FS6818# ping 192.168.0.106

If the ping fails, print the following information:

dwmac.c0060000 Waiting for PHY auto negotiation to complete......... TIMEOUT !

Waiting for PHY realtime link...... TIMEOUT !

 done

dwmac.c0060000: No link.

ping failed; host 192.168.0.106 is not alive

If the ping is successful, print the following information:

Speed: 100, full duplex

Using dwmac.c0060000 device

host 192.168.0.106 is alive

Summarize:

Reason for ping failure

1》The firewall of windows may not be closed

2》Check the network cable between the development board and the computer

3》Restart tftp service

4》Check whether the environment configuration of the tftp service is correct

5 "Whether 100M full duplex is set

Summary: Reasons for tftp failure

1"Contains the reasons for the above ping failure

2》Check whether the environment variable setting of uboot is correct

serverip ipaddr gatewayip netmask

3》Check the network settings of ubuntu

Remember, the ip address of the development board and ubuntu are in the same network segment

12. u-boot porting

【1】bootloader concept

boot: guide

loader: load

Bootloader: used to boot and load the kernel, and start the kernel, and then pass parameters to the kernel

Bootloader belongs to the collective name of the kernel boot program.

For example: u-boot Bios vivi redboot etc.

The most widely used bootloader in embedded development is u-boot.

uboot is an open source software

[2] Features of u-boot

1. u-boot is an open source software

2. u-boot supports platforms with multiple architectures

arm  powerPC  mips x86 

3. The source code of u-boot is short and concise

4. u-boot is a bare metal code

5. u-boot bootloads the kernel, starts the kernel, and passes parameters to the kernel

6. u-boot can complete the initialization of some hardware: uart, memory, emmc, network card

7. u-boot is a short-lived ghost, after starting the kernel,

After passing parameters to the kernel ( telling the kernel where to mount the root file system ), the life cycle of u-boot ends.

[3] Acquisition of u-boot source code

1. Obtain from uboot official website

ftp://ftp.denx.de/pub/u-boot/

For the s5p6818 chip, it cannot be obtained from the official,

Samsung did not open source the code assigned to s5p6818 into uboot

2. Chip manufacturer obtains 

3. Development board manufacturers --- "Currently the 6818 development boards on the market, the development board manufacturers only provide u-boot.bin 

4. Superior supervisor --- "Recommendation

This porting course uses: u-boot-2014.07-netok.tar.bz2

[4] Selection of uboot version

1. Do not choose a version that is too new

Unstable, less data

2. Do not choose too old version

May not support your own hardware platform

3. Do not select the test version u-boot,

Select a stable version of u-boot

The suffix rcx indicates the test version

4. Select the u-boot that supports your own hardware platform version 

This porting course uses: u-boot-2014.07-netok.tar.bz2

This version of uboot supports s5p6818

【5】Preparation before transplantation

Get basic hardware information

cpu (core): cortex-a53

arch:armV8

vendor:samsung

SOC:S5P6818

board (public board): S5P6818 

Public board: A set of reference circuit boards designed by the chip manufacturer based on the chip

NEXELL: A chip manufacturer in South Korea,

Samsung authorized the S5P6818 chip to NEXELL

【6】Began to prepare for transplanting uboot 

1. Create a bootloader in the ubuntu home directory

mkdir bootloader 

2. Copy u-boot-2014.07-netok.tar.bz2 to bootloader

cp /shared folder path/u-boot-2014.07-netok.tar.bz2 ~/bootloader

cp /shared folder path/u-boot-2014.07.tar.bz2 ~/bootloader

3. Unzip the uboot source code

tar -vxf u-boot-2014.07-netok.tar.bz2

mv u-boot-2014.07 u-boot-2014.07-6818

tar -vxf u-boot-2014.07.tar.bz2 ( downloaded from the official website )

【7】Introduction to the u-boot directory

Platform-dependent code: linked to hardware

a rch (architecture)

The download on the official website supports multiple platforms:

board (board, supported board)

The above two are related to hardware.

Platform-independent code: can be shared with hardware-independent code

fs 

drivers 

include

tools

....

In the uboot source code provided by Huaqing, the platform-related source code,

Delete all unused ones .

[8] Porting uboot

1. Read the README

2. Configure the cross-compilation toolchain

Open the Makefile in the top directory of  the u-boot source code

vi Makefile  (*****)

"readability" (modularity; see the name;)

The following content:

 198 ifeq ($(HOSTARCH),$(ARCH))

 199 CROSS_COMPILE ?=                                      

 200 endif

change into

 198 ifeq (arm,arm) (the two inside are equal, the reason for writing ARM is that we use arm)

 199 CROSS_COMPILE ?= arm-none-linux-gnueabi-  (do not write a space after it)

 200 endif

3. Delete the intermediate files of the u-boot source code

make distclean/clean 

4. Configure u-boot source code to support fs6818 development board

make _config

make fs6818_config

If you print the information, it means success:

hqyj@hqyj:u-boot-2014.07-fs6818$ make fs6818_config

Configuring for fs6818 board...

If the following information is printed, it means that u-boot does not support this development board:

hqyj@hqyj:u-boot-2014.07-fs6818$ make maxiaoli_config

make: *** No rule to make target `maxiaoli_config'.  Stop.

make: *** [maxiaoli_config] Error 1

5. Compile u-boot source code and generate ubootpak.bin 

make / make all (compilation time is relatively long)

 

The above problems may occur during Make, and the above problems can be solved by the following methods :

6. Download the generated ubootpak.bin file to the development board, and test whether it can be used.

How to put uboot into flash(EMMC)

Premise: SD card must burn a uboot in advance

1> Start uboot via SD card,

Enter into FS6818# interface

2> Copy ubootpak.bin to the tftpboot directory

cd bootloader/u-boot-2014.07-fs6818

cp ubootpak.bin  ~/tftpboot 

3> Burn ubootpak.bin into memory using tftp command

tftp 0x41000000 ubootpak.bin 

4> update_mmc 

update_mmc     

- type :  2ndboot | boot | raw | part 

:flash device number EMMC: 2

: type 2ndboot 

: The starting address of uboot in memory, in bytes

: The start address of flash: in blocks

: How much space to download to flash

Bytes transferred = 352296 (56028 hex)

The size of a block is 512 bytes

The size of length > 352296 / 512

fastboot=flash=mmc,

2:ubootpak:2nd:0x200,0x78000; flash=mmc,2:2ndboot:2nd:0x200,0x4000;

Excuting an order:

update_mmc 2 2ndboot 0x41000000 0x200 0x78000

Print the following to indicate success

head boot dev  = 2

update mmc.2 type 2ndboot = 0x200(0x1) ~ 0x78000(0x3c0): Done

5> Test whether to update ubootpak.bin to EMMC

Set the DIP switch to switch to EMMC startup

The development board is powered on again.

13. u_boot analysis

One: makefile file analysis

According to README, u_boot needs to be configured first and then make

The configuration command is make fs6818_config

The following commands can be found from the top-level makefile:

1. Open the Makefile in the top directory of the u-boot source code 

vi Makefile

2. Search for the fs6818_config target

Search for _config by multipart matching,

Get the following information:

467 %_config:: outputmakefile

468 @$(MKCONFIG) -A $(@:_config=)

Parse:

%: pattern wildcard

The first @: the following command is not echoed to the ubuntu terminal

@:_config=: Kill _config in fs6818_config,

keep fs6818

method 1:

Remove the first @ sign and re-execute make fs6818_config

  /home/linux/bootloader/u-boot-2014.07-fs6818/mkconfig -A fs6818

When the above is echoed to the terminal, it can be seen that fs6818 is in front of config;

Then analyze the mkconfig file,

The following is the part about ARM in the makefile

vi Makefile 

Put the following content:

 198 ifeq ($(HOSTARCH),$(ARCH))

 199 CROSS_COMPILE ?=                                      

 200 endif

change into

 198 ifeq (arm,arm) (the two inside are equal, the reason for writing ARM is that we use arm)

 199 CROSS_COMPILE ?= arm-none-linux-gnueabi-  (do not write a space after it)

 200 endif

It depends on how these files are organized into u_boot .

Look at the link script u-boot.lds (refer to the actual file comment here, u-boot.lds: link script (how to guide the arrangement of the program) )

It can be seen from the u-boot.lds file,

The first file to run is start.o , and then start to analyze the arch/arm/cpu/slsiap/s5p6818/start.S file, and the makefile analysis is completed.

Conclusions: 1. The first file is: arch/arm/cpu/slsiap/s5p6818/start.S

Two: start.s analysis

Function description: The first stage: set to SVC mode, turn off watchdog, mask interrupt, initialize SDRAM, set stack and clock, code from flash to SDRAM, clear BSS segment, call start_armboot (C function)

1> Build exception vector table

Set to SVC mode

Set to SVC mode, turn off watchdog

Put the mouse on lowlevel_init , and perform ctrl +] to jump

Clear BSS segment

Put the mouse on board_init_ r, press ctrl +] to jump

Jump to the arch/arm/lib/board.c file

void board_init_f(head bootflag){}

The initialization of the structure gd (global data) of the board, the gb structure is used to store the structure of global information.

The second stage: call board.c\start_armboot to start

First figure out the goal of u_boot: read the kernel from flash and start

Analysis code board.c:

If you want to read the kernel, you must support flash. The code is in line 561 and line 588 , which call

flash_init() and nand_init() initialize nor and nand.

Next is the environment variable function env_relocate () in line 617. Entering the print command in u_boot will display a lot of environment variables, which are used to set the baud rate, etc. The environment variables come from:

1. The default one. 2. It is saved on the flash. When starting up, check whether it is on the flash. If not, use the default one.

Continue to analyze, don't worry about it in the middle, it is some network card and debugger settings, until line 927 run _ main_loop (), the infinite loop in the function calls the main_loop () function, go to the file main.c to see this function,

in the main_loop() function. If there is input on the U-Boot console before the bootdelay countdown reaches 0, it will enter the cycle of command parsing and execution; if there is no input on the console, U-Boot will start the kernel. 

autoboot_command() determines whether the keyboard is pressed, that is, whether the countdown is interrupted, and if the keyboard is pressed, execute the corresponding

branch. run_command_list, this function will execute a series of commands specified by the parameter s, which is the command of the environment variable bootcmd,

The default startup command is stored in bootcmd, so the linux kernel starts! This is after the countdown in uboot ends

The principle of automatically starting the linux kernel. If a key on the keyboard is pressed before the countdown ends, run_command_list

The function will not be executed, which is equivalent to autoboot_command being an empty function.

Back to the main_loop function, if the button is pressed before the countdown ends, the cli_loop function will be executed, which is the command processing function, which is responsible for receiving and processing the input commands.

run_command_list  (s,  -1,0 ) executes the bootcmd command to start the kernel ; so reading and starting the kernel depends on the s command, that is , bootcmd ,

It can be seen from the environment variables that bootcmd (/uboot/include/configs/fs6818.h)

#define CONFIG_BOOTCOMMAND "ext4load mmc 2:1 0x48000000 uImage;ext4load mmc 2:1 0x49000000 root.img.gz;bootm 0x48000000"

There are two types of kernel formats: zImage and uImage.

Not all U-Boots support zImage, whether it supports it depends on whether the macro CONFIG_ZIMAGE_BOOT is defined in its configuration file (fs6818.h does not define CONFIG_ZIMAGE_BOOT). So some uboots support zImage startup, while others do not. But all uboots definitely support uImage startup

The kernel header information in ulmage format is defined in /uboot/include/Image.h.

ih_load is the loading address, that is, the address of the kernel in DDR (running address); ih_ep is the kernel entry address.

copy code

typedef struct image_header {

uint32_t    ih_magic;    /* Image Header Magic Number    */

uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */

uint32_t    ih_time;    /* Image Creation Timestamp    */

uint32_t    ih_size;    /* Image Data Size    */

uint32_t    ih_load;    /* Data     Load  Address    */

uint32_t    ih_ep;    /* Entry Point Address    */

uint32_t    ih_dcrc;    /* Image Data CRC Checksum    */

uint8_t    ih_os;    /* Operating System    */

uint8_t    ih_arch;    /* CPU architecture    */

uint8_t    ih_type;    /* Image Type    */

uint8_t    ih_comp;    /* Compression Type    */

uint8_t ih_name[IH_NMLEN];    /* Image Name    */

} image_header_t;

Summarize:

1) Move the kernel to DDR;

2) Check the kernel format and CRC;

3) Prepare to pass parameters;

4) Jump to execute the kernel

Three: run_command analysis

As you can imagine, the command is a structure, {name, fun()}, start to analyze run_command, and implement this function at line 1280 of main.c.

Skip directly to line 1325. Line 1355 is the parsing command. For example, if the input command is md.w 0, it will be parsed as argv[0]= " md.w " ,

argv[1] = " 0 " . argv[0] puts the command, and argv[1] puts the parameter. Analyze 1361 lines of code as follows:

if ((cmdtp = find_cmd(argv[0])) == NULL) {

      printf ("Unknown command '%s' - try 'help'\n", argv[0]);

      rc = -1;  /* give up after bad command */

      continue;}

How to find the command? cmdtp is a structure, see the specific code (located at line 39 of include\command.h).

The find_cmd(argv[0]) function is in line 346 of common\command.c. First look at line 360 ​​__u_boot_cmd_start and __u_boot_cmd_end. These two things cannot be found after searching the code. They are in the link script, and in the C language, the link script can also pass in values. The *(.u_boot_cmd) section is analyzed below. Line 93 in include \command.h :

#define Struct_Section  __attribute__ ((unused,section (".u_boot_cmd")))

I don’t know which function to call after entering bootm  . Search for bootm in the project and find it in common \cmd_bootm.c

U_BOOT_CMD(

   bootm,  CFG_MAXARGS,  1,  do_bootm,

   "bootm   - boot application image from memory\n",

   "[addr [arg ...]]\n    - boot application image stored in memory\n"

   "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"

   "\t'arg' can be the address of an initrd image\n"

#ifdef CONFIG_OF_FLAT_TREE

  "\tWhen booting a Linux kernel which requires a flat device-tree\n"

  "\ta third argument is required which is the address of the of the\n"

  "\tdevice-tree blob. To boot that kernel without an initrd image,\n"

  "\tuse a '-' for the second argument. If you do not pass a third\n"

  "\ta bd_info struct will be passed instead\n"

#endif);

Go search for the macro U_BOOT_CMD and find line 97 in include\command.h  :

#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \

cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

Expand the above macro to get:

cmd_tbl_t  __u_boot_cmd_bootm 

__attribute__ ((unused,section (".u_boot_cmd")))= {#name, maxargs, rep, cmd, usage, help}

It can be seen from here that a structure such as __u_boot_cmd_bootm is defined, and the type is cmd_tbl_t.

(The code is typedef struct cmd_tbl_s cmd_tbl_t;  line 93 in include\command.h :)

This structure has an attribute: __attribute__, which forces the segment attribute section to be set to .u_boot_cmd (in u-boot.lds)

The content inside is: {#name, maxargs, rep, cmd,  usagehelp }

替换得:{bootm, CFG_MAXARGS, 1, do_bootm, "bootm   - boot application image from memory\n",     "bootm   - boot application image from memory\n","[addr [arg ...]]\n    - boot application image stored in memory\n"    "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"  "\t'arg' can be the address of an initrd image\n"

Experimental content: add a hello command.

Create a new file named cmd_hello.c in the common directory . The code inside is modified according to cmd_bootm.c . The code is as follows:

#include 

#include 

#include 

int do_hello(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

int i;

printf( " hello world!\n, the number of parameters is %d " , argc);

for(i=0;i<=argc;i++)

printf(参数是:%s  ,argv[i]);

return 0;

}

另外里面还需要定义一个宏:

U_BOOT_CMD(

   hello,  CFG_MAXARGS,  1,  do_hello,

   "hello short help.....",

   "hello long help.............................................."\n

);

最后把此文件放到common目录下,修改此目录下的makefile第54行:

加上文件cmd_hello.c,然后重新make一下即可。

四:内核启动分析

一:

根据bootm 命令执行do_bootm_states函数,658行非常重要,通过bootm_os_get_boot_func查找系统启动函数,参数images->os.os为系统类型,函数返回值是查找到的系统启动函数do_bootm_linux。处理615行BOOTM_STATE_OS_PREP状态,调用do_bootm_linux->boot_prep_linux处理环境变量bootargs,bootargs保存着传递给linux kernel的参数。

638行调用boot_selected_os,启动linux内核,第4个参数为linux镜像头,boot_os数组如下:

static boot_os_fn *boot_os[] = {

[IH_OS_U_BOOT] = do_bootm_standalone,

#ifdef CONFIG_BOOTM_LINUX

[IH_OS_LINUX] = do_bootm_linux,

#endif

#ifdef CONFIG_BOOTM_NETBSD

[IH_OS_NETBSD] = do_bootm_netbsd,

#endif

#ifdef CONFIG_LYNXKDI

[IH_OS_LYNXOS] = do_bootm_lynxkdi,

#endif

#ifdef CONFIG_BOOTM_RTEMS

[IH_OS_RTEMS] = do_bootm_rtems,

#endif

#if defined(CONFIG_BOOTM_OSE)

[IH_OS_OSE] = do_bootm_ose,

#endif

#if defined(CONFIG_BOOTM_PLAN9)

[IH_OS_PLAN9] = do_bootm_plan9,

#endif

#if defined(CONFIG_BOOTM_VXWORKS) && \

(defined(CONFIG_PPC) || defined(CONFIG_ARM))

[IH_OS_VXWORKS] = do_bootm_vxworks,

#endif

#if defined(CONFIG_CMD_ELF)

[IH_OS_QNX] = do_bootm_qnself,

#endif

#ifdef CONFIG_INTEGRITY

[IH_OS_INTEGRITY] = do_bootm_integrity,

#endif

};

The red function do_bootm_linux is the corresponding system startup function.

Line 295, the function kernel_entry, look at the name "kernel_entry", indicating that this function enters the Linux kernel, and also

That's the ultimate big boos! ! This function has three parameters: zero, arch, params, the first parameter zero is also 0;

The second parameter is the machine ID; the third parameter ATAGS or the first address of the device tree (DTB), ATAGS is a traditional method, use

Instead of passing some command line information, if you use the device tree, you must pass the device tree (DTB).

Line 299, get the kernel_entry function, the function kernel_entry is not defined by uboot, but in Linux

Kernel definition, the first line of code of the Linux kernel image file is the function kernel_entry, and images->ep saves the Linux

The starting address of the kernel image, which stores the first line of code of the Linux kernel!

boot_jump_linux, line 315~318 is to set the value of register r2?

Why set the value of r2? The Linux kernel is an assembly code at the beginning, so the function kernel_entry is an assembly function

number. To pass parameters to the assembly function, use r0, r1 and r2 (when the number of parameters does not exceed 3), so the r2 register is

The third parameter of the function kernel_entry.

If you use the device tree, r2 should be the starting address of the device tree, and the device tree address is stored in images

in the ftd_addr member variable.

If the device tree is not used, r2 should be the starting address of the parameters passed by uboot to Linux, that is,

is the value of the environment variable bootargs, call the kernel_entry function to enter the Linux kernel, this line will never return, and the mission of uboot will be

finished.

14. Transplantation of linux kernel

[1] Features of the linux kernel

1 "Linux kernel source code open source

2 "Linux supports multiple architecture platforms arm x86 (both mobile phones and Ubuntu use the Linux kernel)

3"The linux kernel code is modularized ()

4"The linux kernel code adopts the idea of ​​layering

5"Linux kernel source code has good portability and tailoring features

6"Linux kernel source code is realized by assembly and c language

[2] Selection of linux kernel source code

1" can be obtained from the official linux kernel

https://mirrors.edge.kernel.org/pub/linux/kernel/

2》Obtained from the chip manufacturer

3》Obtained from the development board manufacturer

4 "Obtained from the technical director --" recommended

[3] The naming rules of the linux kernel

linux-major version number. minor version number. revision number.tar.xz 

Main version number: The main version number will only be updated when the kernel source code is relatively new

Minor version number:

The minor version number is an even number: indicates a stable version

The minor version number is an odd number: indicates a test version

Revision number: as long as there is a code update in the kernel source code,

Upgrade revision number

【4】How to choose the version of the linux kernel

1》Not too new

2》Not too old

3"Select a stable version

Samsung does not have the source code of S5P6818, and open source it into the Linux kernel, so you cannot download the Linux kernel source code from the official website, and use the kernel source code provided by Samsung.

The kernel source code used in this course is:

kernel-3.4.39-ok.tar.bz2

[5] Configuration and compilation of linux kernel source code

1. Create a kernel folder in the ubuntu home directory

Copy the kernel source compressed package to the kernel folder

mkdir kernel 

cd kernel

cp /mnt/hgfs/share/kernel-3.4.39-ok.tar.bz2 ./

Decompress the kernel source code

tar -vxf kernel-3.4.39-ok.tar.bz2

2. Enter the kernel source code directory and analyze the directory structure of the kernel source code

cd kernel-3.4.39

ls 

Platform-related: related to hardware, code cannot be shared

arch 

Platform independent: code can be shared

fs

include 

driver

net 

tools

usr

ipc

....

3.  Configure the cross-compilation toolchain

Open the Makefile in the top-level directory of the kernel source code,

Search for CROSS_COMPILE 

195 ARCH        ?= 

196 CROSS_COMPILE   ?= 

change into:

195 ARCH        ?= arm

196 CROSS_COMPILE   ?= arm-none-linux-gnueabi-   

4. Read the README

1" After getting the kernel source code, you should clear the

Intermediate files in the kernel source

make clean 

make distclean 

make mrproper --"clear cleaner

2"  Configure the kernel source code to support the current hardware platform

183 "make ${PLATFORM}_defconfig"

184 Create a ./.config file by using the default

symbol values from

186 arch/$ARCH/configs/${PLATFORM}_defconfig.

187 Use "make help" to get a list of all available

188 platforms of your architecture.

(创建一个.config文件,使用默认的符号值,从arch/arm/configs/目录下的这个文件${PLATFORM}_defconfig.生成一个.config文件。)

方法1:

make help 

得到以下信息:

fs6818_defconfig         - Build for fs6818

方法2:m

进入arch/arm/configs/目录 

发现以下文件fs6818_defconfig 

所以PLATFORM=fs6818. :q!

让当前的内核支持自己的硬件平台,

应该执行make fs6818_defconfig

执行结果,打印以下信息:

hqyj@hqyj:kernel-3.4.39$ make fs6818_defconfig

HOSTCC  scripts/basic/fixdep

 HOSTCC  scripts/kconfig/conf.o

SHIPPED scripts/kconfig/zconf.tab.c

SHIPPED scripts/kconfig/zconf.lex.c

SHIPPED scripts/kconfig/zconf.hash.c

 HOSTCC  scripts/kconfig/zconf.tab.o

 HOSTLD  scripts/kconfig/conf

#

# configuration written to .config

#

After executing the command, write the configuration information of the board to the .config file in the top directory of the kernel source code

// After the hardware board fs6818 can be supported, the hardware device driver on the board needs to be installed

3 " Configure the kernel based on menu options

Execute the command: make menuconfig 

In the actual development, the configuration of the menu options along with the kernel is to use the make menuconfig command

Question 1:

The first time you use make menuconfig, you need to install a tool with a graphical interface

The graphical map needs to be installed before configuration (make meuconfig):

sudo apt-get install libncurses5-dev 

Question 2:

The following error occurs:

cripts/kconfig/mconf Kconfig

Your display is too small to run Menuconfig!

It must be at least 19 lines by 80 columns.

make[1]: *** [menuconfig] Error 1

make: *** [menuconfig] Error 2

Reason: The font of the terminal is too large, shrink it a little

4 "Compile the kernel to generate uImage 

make uImage 

  //Compiling may be relatively long, you don't need to compile, use the teacher to send it, some plug-ins may not be complete, you can use it.

time make uImage -jx

time: Echo compilation time

-jx: compile using multithreading

x can be 2,4,6,8

The following errors may occur during compilation:

"mkimage" command not found - U-Boot images will not be built

 make[1]: *** [arch/arm/boot/uImage] Error 1

make: *** [uImage] Error 2

The reason for the error: the mkimage command cannot be found,

According to the prompt analysis, mkimage should exist in the uboot source code directory

The uboot source code must be compiled before there will be an mkimage executable program

way of solving the problem:

Put the mkimage in the tools directory of the uboot source code,

sudo cp mkimage   /usr/bin

Copy it to the /use/bin directory of ubuntu:

sudo  cp  ./tools/mkimage    /usr/bin      

 uboot directory ubuntu directory

Compile the kernel source again to pass.

5 "Copy the uImage in the arch/arm/boot/ directory to the tftpboot directory,

Test whether uImage can start normally, and mount the root file system ( restart the development board, as shown in the figure below, automatic method: first download uImage to the memory, then bootcmd starts the kernel, and then passes parameters to the kernel )

Download uImage to memory through tftp,

Mount the root file system through nfs.

Pay attention to check whether the settings of bootcmd and bootargs are correct.

Interview question: Relationship between Makefile .config Kconfig files

Makefile: Guide the kernel to compile

.config: stores the configuration information of the kernel + hardware

Kconfig: store menu options (source code of menuconfig)

Execute the make fs6818_defconfig command to generate a .config file according to the configuration information in the fs6818_config file and the Kconfig file 

When make menuconfig is executed, the graphical interface of the menu is generated according to Kconfig,

If the configuration is performed according to the menu graphical interface, the .config file will be updated 

Makefile文件根据.config文件中的信息,决定将那些文件

编译到uIamge中,那些不编译到uImage中。

例子:

驱动移植

1.需要有一个驱动对应的.c的代码

2.  Kconfig .config Makefile

第一步:修改Kconfig:(Kconfig是产生菜单的文件

 53 config CHEN_JINGJING  //在.config中生成选项

 54 bool "chengjingjing said welcome to hqyj!"  //选项菜单中的名字OBJ目标文件名:obj-$(CONFIG_CHEN_JINGJING)

(tristate:有三种选项,三态(Y(把驱动编译到内核中),M(模块),N(不编译驱动)),bool:两态)

添加完选项以后,回到最顶层目录执行make menuconfig 

在这个目录下面执行:make menuconfigCO

//通过选项选择要安装的驱动,图形界面能让我们快熟的找到想安装的驱动。

找到上图的device drivers,然后按回车,出现下面界面,找到character devices, 

按回车

看到上面的自己的添加的内容,根据自己的需求按键盘上面的Y,M,N三选一;选中以后到exit退出即可(选择yes)

vi .config 

CONFIG_MA_XIAOLI = y

vi Makefile ( path to open Makefile )

obj-$(CONFIG_FARSIGHT_DEMO) +=demo.o

Compile:

make uImage-->uImage ( contains the new driver kernel)

 The corresponding path (/dev) on the board has the driver file of chenjingjing.

make modules -->demo.ko ( M (compile and generate modules ))

--------------------

Makefile

modules:

Rules for Compiling Modules

Y (to be compiled into the kernel) M (to compile and generate modules) N (to not compile the driver)

sudo insmod demo.ko install driver

sudo rmmod demo Uninstall the driver

In the actual development, the driver development is carried out in a modular way.

When you need to use the driver, load it into the kernel through the insmod command,

When the driver is not needed, use the rmmod command to uninstall the driver.

Benefits: 1. Development is more convenient and convenient

2. Save development time

3. Easy to find mistakes

If it is compiled into the kernel, once the kernel crashes, it is not sure whether it is a kernel problem or a driver problem.

If you compile in a modular way, the kernel can be started successfully, indicating that there is no problem with the kernel.

If the driver is loaded, the kernel crashes, indicating that there is a problem with the driver.

15. Case 1: Compile the LED driver into the kernel

       The driver is to provide an interface for the application layer, and the driver provides an interface for turning on and off the light.

       Specifically, whether you implement a running light or a breathing light depends on the code of the application layer.

     1. Copy the fs6818_led.c and fs6818_led.h files to

              In the drivers/char directory

              cp /mnt/hgfs/share/led-driver/fs6818_led.c

                            ~/kernel/kernel-3.4.39/drivers/char

              cp /mnt/hgfs/share/led-driver/fs6818_led.h

                            ~/kernel/kernel-3.4.39/drivers/char

2. Open the Makefile in the drivers/char directory, and add the compilation information of the corresponding driver

 obj-$(CONFIG_FS6818_RGB) += fs6818_led.o

     3. Open Kconfig under the drivers/char directory, add the corresponding menu options, and add the following information

12     config FS6818_RGB

13     bool "FS6818_LED_DREVERS"

14     default y

15     help

16   This is FS6818 LED Driver!~

4. Execute make menuconfig to configure menu options

       Pressing <Y> includes, (includes)

                     <N> excludes, (not included)

                     <M> modularizes features. (modular)

                     Press <Esc><Esc> to exit, (退出)

                     <?> for Help, (see help manual)

                     </> for Search. 

                     Legend: [*] built-in (* compile)

                     [ ] excluded (empty does not compile)

                     <M> module 

                     < > module capable 

                             

              How to find out which submenu the FS6818_LED_DREVERS menu option is in?

              Press the "/" key, a search dialog box appears, enter the content after config:

               "FS6818_RGB", press Enter to get the following information.

       │ Symbol: FS6818_RGB [=y] configuration information     

       │ Type : boolean menu option type             

       │ Prompt: FS6818_LED_DREVERS menu option        

       │   Defined at drivers/char/Kconfig:12  

       │   Location:                            

       │ -> Device Drivers submenu              

       │       -> Character devices 

 

(The information obtained by Makefile is y, and compile the following XXX.o into uImage)

     5. Compile the kernel to generate uImage

              make uImage

             

                CC      drivers/char/fs6818_led.o

     6. Copy uImage to the tftpboot directory

       cp arch/arm/boot/uImage  ~/tftpboot

       Restart the development board, download the kernel through the network, and mount the root file system

     7. Test whether the drive can work normally

       Compile the code of the application layer to generate executable files

       cp /mnt/hgfs/share/led-driver  ~/ -raf

       arm-none-linux-gnueabi-gcc fs6818led_test.c

       最终生成a.out可执行文件

       拷贝a.out可执行文件到根文件系统中,

       cp led-driver/a.out  ~/nfs/rootfs

             

       进入超级终端,执行./a.out

案例2:将led灯的驱动使用模块化的方式进行编译《模块》

       问题1. make uImage 编译时间问题

              如果执行make clean 所有的.o文件都需要重新生成,所以编译时间会很长

              如果之前编译过,如果对应的.c文件没有修改过,编译时不在重新生成.o文件,只重新编译生成修改过的.c文件,重新生成.o文件。

             

              Makefile可以根据文件的时间戳,进行分析

1. 拷贝fs6818_led.c和fs6818_led.h文件到

              drivers/char目录下

              cp /mnt/hgfs/share/led-driver/fs6818_led.c

                            ~/kernel/kernel-3.4.39/drivers/char

              cp /mnt/hgfs/share/led-driver/fs6818_led.h

                            ~/kernel/kernel-3.4.39/drivers/char

2. 打开drivers/char目录下的Makefile,添加对应的

              驱动的编译信息

              obj-$(CONFIG_FS6818_RGB) += fs6818_led.o

3. 打开drivers/char目录下的Kconfig,添加对应的

              menu option, add the following information

               12 config FS6818_RGB

               13     tristate "FS6818_LED_DREVERS"  

               14     default y

               15     help

               16         This is FS6818 LED Driver!~

               

               bool:

               [*] : The corresponding driver is compiled into the kernel uImage image

               [ ] : The corresponding driver is not compiled into the kernel uImage image

       stistate: tristate

<*> : The corresponding driver is compiled into the kernel uImage image

< > : The corresponding driver is not compiled into the kernel uImage image

 <M>: The corresponding driver is not compiled into the kernel uImage image,

Instead, compile the driver in a modular way and generate ****.ko

To use the driver, load the driver into the kernel through the command,

If you don't need to use the driver, you can uninstall the driver from the kernel by command

        

4. Execute make menuconfig to configure menu options

              How to find out which submenu the FS6818_LED_DREVERS menu option is in?

              Press the "/" key, a search dialog box appears, enter the content after config:

"FS6818_RGB", press Enter to get the following information.

│ Symbol: FS6818_RGB [=y] configuration information     

│ Type : tristate menu option type: tristate            

│ Prompt: FS6818_LED_DREVERS menu option        

│   Defined at drivers/char/Kconfig:12  

│   Location:                            

│ -> Device Drivers submenu              

│       -> Character devices 

Modify the following menu options to M

                <M> FS6818_LED_DREVERS

5. Compile the kernel, regenerate uImage, and delete the driver code of the LED light in the previous uImage

              make uImage

              Re-copy the uImage file to tftpboot in the home directory

              cp arch/arm/boot/uImage ~/tftpboot

             

6. Modularly compile the kernel source code to generate a .ko file

              make modules

              The compilation information is as follows, indicating success:

              LD [M]  drivers/char/fs6818_led.ko

             

              Copy the fs6818_led.ko file to the home directory of the root file system

cp drivers/char/fs6818_led.ko ~/nfs/rootfs/home

      

7. Test whether the drive can work normally

              Compile the code of the application layer to generate executable files

       cp /mnt/hgfs/share/led-driver  ~/ -raf

       arm-none-linux-gnueabi-gcc fs6818led_test.c

              Finally generate a.out executable file

              Copy the a.out executable file to the home directory of the root file system,

              cp led-driver/a.out ~/nfs/rootfs/home

             

              Restart the development board, download the kernel through tftp, mount the root file system through nfs,

              After successful startup,

              On the HyperTerminal, enter cd home to enter the home directory of the root file system.

             

       Load the driver into the kernel using the modular command,

       insmod fs6818_led.ko installs the modular driver into the kernel

       lsmod View modularly loaded drivers

       rmmod fs6818_led uninstall driver, no need to add .ko

       mknod creates a device node mo in the /dev directory

Format: mknod /dev/led c 500 0

Name of device node Character device Major device number Minor device number

             

              Enter HyperTerminal, execute ./a.out

              # rmmod fs6818_led

              rmmod: can't change directory to '/lib/modules': No such file or directory

              Solution:

              Create a modules folder in the lib directory

              cd lib

              mkdir modules

             

              Execute rmmod fs6818_led again

              rmmod: can't change directory to '3.4.39-farsight': No such file or directory

              Solution:

              Create a 3.4.39-farsight folder in the lib/modules directory

              cd lib/modules

              mkdir 3.4.39-farsight

16. Root file system (method 1)

【1】Concept  

    Root file system: some files that the system must depend on to run

(such as scripts, libraries, configuration files...), the essence is directories and files. Root file system image: a single file rootfs -----> ramdisk.img generated after the root file system is packaged and compressed in a certain format

         Filesystem: A software mechanism for managing and accessing disks,

         Different file systems have different mechanisms for managing and accessing disks

[2] busybox, a tool for transplanting the root file system

         1. Short and dapper

         2. The version is updated quickly, and there is little difference between versions

【3】 How to get busybox

         https://busybox.net/downloads/

[4] Introduction to directories in the root file system

    Note: function analysis of each file

bin: command file (made by busybox tool)

dev: device file (the device recognized by the operating system has a corresponding file, that is, when the device is running)

etc: configuration file (configure some information related to the kernel)

lib: Library files, such as the standard library of C (copied from the cross-compilation toolchain)

linuxrc: the first program to run after the root file system is mounted (created by the busybox tool)

mnt: Shared directory (not necessary), such as when mounting an SD card, mount the SD card in this directory

proc: Process-related files (files only exist when a process is running)

root: user authority (the board itself runs as root user)

sbin: Superuser commands, not available to general users (the board itself is made by superusers through busybox tools)

sys: system files (when the system is running, there will be files only after the system is loaded)

tmp: Temporary files (for example, temporary files will be generated when a new device is inserted)

usr: user file (created by busybox tool)

var: Store downloaded files and software (optional)

        

mkdir lib mnt proc root sys tmp var

[5] Using the busyBox tool to create a root file system requires the use of the gcc-4.9.4 version of the cross-compilation toolchain, and the cross-compilation toolchain needs to be reconfigured

    step:

    1. In the ubuntu home directory (~), create a toolchain

         mkdir toolchain

    2. Copy gcc-4.9.4.tar.xz to the toolchain directory

         cp shared directory/gcc-4.9.4.tar.xz ~/toolchain

    3. Unzip the cross-compilation toolchain

         tar -vxf gcc-4.9.4.tar.xz

    4. Configure environment variables

         Open sudo vi /etc/bash.bashrc

         Add the following on the last line:

         export PATH=$PATH:/home/hqyj/toolchain/gcc-4.9.4/bin/

                               Modify to your own path

         Note: gcc-4.9.1 needs to be commented out

    5. Make environment variables take effect immediately

         source /etc/bash.bashrc

    6. Test whether the cross-compilation toolchain is successfully installed   

    After switching the cross-compilation toolchain, the ubuntu terminal needs to be restarted

         arm-none-linux-gnueabi-gcc -v

         Print the following content, indicating success

gcc version 4.9.4 (Sourcery G++ Lite 2010.09-50)

【6】Use busybox tool to make rootfs root file system

    1. Copy busybox-1.31.1.tar.bz2 to the home directory of ubuntu

         cp /mnt/hgfs/share/busybox-1.31.1.tar.bz2 ~/

    2. Unzip the root file system and switch to the busybox-1.31.1 directory

         tar -vxf busybox-1.31.1.tar.bz2

         cd busybox-1.31.1

    3. Analyze README

         Not getting too much important information

    4. Obtain help information for compilation through make help

    

Clear intermediate files

make clean   - delete temporary files created by build

make distclean    - delete all non-source files (including .config)

         compile

make all - Executable and documentation 

         Configuration via GUI

    make menuconfig   - interactive curses-based configurator 

         Install and uninstall

make install - install busybox into CONFIG_PREFIX

    make uninstall   

    5. Modify the Makefile and configure it as a cross-compilation toolchain

         Open the Makefile

         Put the following content:

164 CROSS_COMPILE ?= 

190 ARCH ?= $(SUBARCH) 

change into

164 CROSS_COMPILE ?=  arm-none-linux-gnueabi-

190 ARCH ?= arm

6. Execute the make menuconfig command to configure busybox

1 " Use a static library instead of a shared library. If the shared library is transplanted to the board, it cannot be used

    Settings  --->

    [*] Build static binary (no shared libs) (NEW)

2 " Configure as a vi-style line editing command

Settings  --->

    [*]   vi-style line editing commands (NEW)

3 " Configure the installation path of the root file system

             Settings  --->

(./_install) Destination path for 'make install'

    Modify ./_install to /home/hq/rootfs

After entering, press the tab key first, and then modify it

 

4 " Configure commands that support driver modularization

    Linux Module Utilities  --->

Previously, the previous default was *, modify * to empty

          [ ] Simplified modutils 

              [*] depmod (27 kb) (NEW)         

              [*] insmod (22 kb) (NEW)         

              [*] lsmod (1.9 kb) (NEW)         

              [*] Pretty output (NEW)         

              [*] modinfo (24 kb) (NEW)        

              [*] modprobe (28 kb) (NEW)       

              [*] Blacklist support (NEW)    

              [*] rmmod (3.3 kb) (NEW) 

7. Compile the source code

         make  /  make all

8. Install the root file system to /home/hqyj/rootfs

         make install

 

9. Test the root file system and deploy the root file system

         1》First change the rootfs of the nfs directory to another name

             cd ~/nfs

             mv rootfs rootfs-ok

         2》Copy the rootfs file you made to ~/nfs

             cp ~/rootfs  ~/nfs -raf

10. Restart the development board and test whether the rootfs can be used normally

         Premise, use tftp to download uImage

         Mount the root file system using nfs    

1》The mount is successful, but the error message is printed:

can't run '/etc/init.d/rcS': No such file or directory

can't open /dev/tty2: No such file or directory

can't open /dev/tty3: No such file or directory

can't open /dev/tty4: No such file or directory

    Solution:

    Cd  ~/nfs/rootfs

    Create a dev folder, create an etc/init.d folder,

    Create rcS file in etc/init.d directory

    mkdir -p  etc/init.d

    mkdir dev

    cd etc/init.d

    touch rcS

    2》Continue to restart the development board for testing

    The following problem occurs:

can't run '/etc/init.d/rcS': Permission denied

    The solution to this problem: modify rcS with maximum authority

    chmod 777 rcS

    In the rcS file the following must be added:

    #!/bin/sh

    /bin/mount -a

    echo /sbin/mdev > /proc/sys/kernel/hotplug

    /sbin/mdev -s

    explain:

mount -a: The system will automatically parse the fstab configuration file, the system according to

   This configuration file performs a series of hook actions     

 echo /sbin/mdev > /proc/sys/kernel/hotplug:

 Writing the string "/sbin/mdev" to the file /proc/sys/kernel/hotplug actually tells the kernel driver that the program to create device files in the future is /sbin/mdev   

  mdev -s: The system starts, and the device file corresponding to the kernel driver is automatically created

can't open /dev/tty2: No such file or directory

can't open /dev/tty3: No such file or directory

can't open /dev/tty4: No such file or directory

This problem will be solved later

Reboot the board again:  

3》Create the file fstab inittab in the etc directory of the root file system

             cd etc

             touch fstab

             touch inittab

            

             Open the inittab file and add the following:

             ::sysinit:/etc/init.d/rcS

             ::askfirst:-/bin/sh

             ::restart:/sbin/init

             ::ctrlaltdel:/sbin/reboot

             Explanation: The process of system startup  

Open the fstab file and add the following

#device mount-point type options dump fsck orde

proc   /proc  proc  defaults  0  0

tpfs  /tmp   tmpfs defaults  0  0

sysfs  /sys   sysfs defaults  0  0

tmpfs  /dev   tmpfs defaults  0  0

Parse:

    First column: device type

    Second column: mount point

    Third column: type

    Columns 4,5,6: Access Rights           

    Restart the development board again, basically successful,

    4"Create other files:

    mkdir lib mnt proc root sys tmp var

    Restart the development board again, and the root file system is created successfully.

        

    5》Add user name

         Create a profile file in the etc directory of the root file system ,

         Open the profile file and add the following:

         export HOSTNAME=hq

         export USER=root

         export HOME=ot

         #export PS1="\[\u@\h \W\]\$ "

         #cd root

         export PS1="[$USER@$HOSTNAME \W\]\# "

    PATH=/bin:/sbin:/usr/bin:/usr/sbin

LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH

    export PATH LD_LIBRARY_PATH

         Restart the development board

         6 "Transplant the shared library to the root file system,  

    Create test.c in ubuntu and write the following code

             #include <stdio.h>

             int main(int argc, char *argv[])

             {

                 

                  printf("hello world!\n");

                  return 0;

             }  

Use the cross-compilation toolchain to compile test.c to generate the test executable program

arm-none-linux-gnueabi-gcc test.c -o test Copy the test executable program to the newly created file system

cp test ~/nfs/rootfs

Restart the development board, and run the application program test on the HyperTerminal terminal

         ./test

    The following error message appears:

    -/bin/sh: ./test: not found

Cause of the problem: The dynamic library necessary for the application to run is missing.

Solution: Copy the library in the cross-compilation toolchain to the lib directory of the root file system

    cp /home/hq/toolchain/gcc-4.9.4/arm-none-linux-gnueabi/sysroot/lib/*  ~/nfs/rootfs/lib 

cp /home/hq/toolchain/gcc-4.6.4/arm-arm1176jzfssf-linux-gnueabi/sysroot/lib/*  ~/nfs/rootfs/lib

To the end of this course:

The production of the specific ramdisk.img root file system can be done according to the steps required in the experiment manual   

    Cross-compilation toolchain, 132M, too large, do not install cross-compilation toolchain in the board

        

17. Making the root file system

【Purpose】

       Master the creation and transplantation of the root file system

       Note: In the experiment, the command line prompt is "$" means running on the host, "#" means running on the target board

【lab environment】

  1. ubuntu 12.04 release
  2. FS6818 platform

【Experimental steps】

1. Copy the "busybox-1.22.1.tar.bz2" file in the "Tools and Source Code\Third Day\BusyBox-1.22.1" directory in the student materials to a directory in ubuntu and decompress it

$ tar  -xvf  busybox-1.22.1.tar.bz2

2. Enter the top-level directory of the busybox source code to configure the source code

$ cd  busybox-1.22.1/

Execute the following command to enter the configuration interface

$ make  menuconfig

In the graphical configuration interface, enter the "Busybox Settings --->" menu, then enter the "Build Options --->" menu, select the "Build BusyBox as a static binary (no shared libs)" option in the "Build Options" interface, delete the "Build with Large File Support (for accessing files > 2 GB)" option, and then fill in the used compilation in the "Cross Compiler prefix" prefix "arm-none-linux-gnueabi-".

3. After exiting the configuration interface, execute the following command to compile the busybox source code

$ make

4. Execute the following command to install busybox

$ make  install

After the installation is complete, the _install directory is generated under the top-level directory of the source code, and the relevant files required by the root file system are generated under the _install directory, as shown in the figure:

 5.  Improve other directories in the root file system

Go to the _install directory

$ cd  _install/

Create related directories

$ mkdir  dev  etc  home  lib  mnt  proc  root  sys  tmp  var

Copy the libraries in the cross-compilation toolchain to the lib directory

$ cp /home/linux/toolchain/toolchain-4.5.1-farsight/arm-none-linux-gnueabi/libc/lib/ . -a

Remove the static library in it

$ sudo  rm  lib/*.a

Delete the symbol table in the library file to reduce the size of the library file:

$ arm-none-linux-gnueabi-strip  lib/*

Copy the contents of etc in the original root file system to the root file system made by yourself

$ cp  -rf  /home/linux/rootfs/etc/*  etc/

Delete the previous root file system

$ rm  -rf  /home/linux/rootfs/*

Copy the root file system made by yourself to the nfs mount directory

$ cp  -rf *  /home/linux/rootfs/

6. Follow the steps 3 and 4 in Experiment 4 to test whether the root file system generated by yourself can be mounted and used. If it cannot be mounted and used normally, check whether the above steps are correct.

The root file system is a hash file. If we want to burn it into EMMC, we must pack and compress these files into an image file of a certain format.

7. Make the root file system into a root file system image

Go to the home directory of ubuntu

$ cd  ~

Execute the following command to create an image file with a size of 8M

$ dd  if=/dev/zero  of=ramdisk  bs=1k  count=8192

Format the image as ext2

$ mkfs.ext2  -F  ramdisk

Mount the image file to the /mnt directory under ubuntu

$ sudo  mount  -t  ext2  ramdisk  /mnt

Copy all the files in the root file system we made to the image

$ sudo  cp  -a  /home/linux/nfs/rootfs/*  /mnt/

unmount

$ sudo  umount  /mnt

compressed image file

$ gzip  --best  -c  ramdisk  >  ramdisk.gz

Use the mkimage tool to add a verification header to the image file and then generate a usable image ramdisk.img

$ mkimage -n "ramdisk" -A arm -O linux -T ramdisk -C gzip -d ramdisk.gz ramdisk.img

Copy the self-made root file system image to the tftp download directory and modify its permissions

$ cp  ramdisk.img  /home/linux/tftpboot/

$ chmod  777  /home/linux/tftpboot/ramdisk.img

8. Reconfigure the linux kernel to support the ramdisk file system

Enter the top-level directory of the linux source code used in Experiment 7

$ cd  kernel-3.4.39/

Execute the following command to enter the kernel configuration interface

$ make  menuconfig

Enter the "Device Drivers --->" menu in the graphical interface, and then enter the "[*] Block devices --->" menu, select "RAM block device support" as "Y", and change "Default RAM disk size (kbytes)" to "8192", as shown in the figure:

 

Save and exit after the configuration is complete.

Go back to the top directory of the kernel source code and recompile the kernel source code:

$ make  uImage

Copy the generated uImage file to the download directory of the tftp server:

$ cp  arch/arm/boot/uImage  /home/linux/tftpboot/

$ chmod  777  /home/linux/tftpboot/uImage

9. According to steps 6 and 7 of Test 4, burn the newly generated kernel image uImage and root file system image ramdisk.img into EMMC and start the test from EMMC

4. Drive

1. Drive the syllabus

  • kernel module
  • character device driver
  • to interrupt

2. What is the difference between ARM bare metal code and driver?

  • What they have in common: Both can operate the hardware
  • difference:
  • The bare metal uses C language to write values ​​into the corresponding registers, and the driver writes values ​​into the registers according to a certain routine
  • Arm bare metal is compiled and executed separately, and the driver relies on kernel compilation and execution ( according to the architecture and configuration specified by the kernel )
  • The arm bare metal can only execute one code at the same time, and the driver can execute multiple codes at the same time (and when operating the serial port, programmers don’t have to write part of the code written by the kernel, which is more convenient)
  • The arm bare metal only needs one main, and the corresponding logic code can be written in the main function to drive, which depends on the framework of the kernel and the process of operating the hardware.

(Registers for operating LED lights in the driver) (The driver module relies on the kernel framework to execute code)

3. Linux system composition

  • The user space of 0-3G is the space of 0-3G for each process alone
  • System call (soft interrupt swi) ---- (the application layer interacts with the bottom layer through the system call, swi, switches the application layer to the kernel layer.

Note: 1G of physical memory is mapped to 0~4G of virtual memory, each process can access the kernel, 0~3G is owned by each process independently, and 3G~4G is shared by all. The code runs on physical memory, and writing values ​​to virtual memory is actually written on physical memory

  • kernel  :                           【3-4G】

5 major functions of the kernel :

  • Process management : process creation, destruction, scheduling and other functions

Note: Can be interrupted, can not be interrupted, that is, whether it is interrupted by a signal. How to change from the running state to the interruptible waiting state, and the uninterruptible waiting state. The operating system will start to allocate a time slice to each process. When the sleep function is written in the process, the process will go from running to sleeping state, but at this time the CPU cannot wait. There are two methods, 1: According to the time slice, the CPU automatically jumps, 2: You can write the code that can cause CPU scheduling in the program.

  • File management : organize and manage files through the file system ext2/ext3/ext4 yaff jiffs, etc.
  • Network management : encapsulation and disassembly of data processes through the network protocol stack (OSI, TCP) (data sending and receiving are completed through the network card driver, and the network card driver does not generate files (there is no corresponding file under the Linux system dev), so functions such as open cannot be used, but sockets are used).
  • Memory management : application and release of user space and kernel space memory through the memory manager
  • Device management : device driver management (corresponding to driver engineers)
  • Character device driver:  (led mouse keyboard lcd touchscreen (touch screen))

1. Access in units of bytes, sequential access (access in sequence)

2. The device file will be created, open read write close to access

  • Block device driver   : (camera u disk emmc)

1. Access according to the block (512 bytes) (sector), can be accessed sequentially, can be accessed out of order

2. The device file will be created, open read write close to access

  • Network card device driver : (cat)

1. Send and receive according to network data packets.

4. Macrokernel, Microkernel

  • Macro kernel: Integrate processes, networks, files, devices, memory and other functions into one kernel

Features: The code runs efficiently.

Disadvantage: If one part goes wrong, the whole kernel will crash.

eg:ubuntu Android

  • Microkernel: Only processes and memory mechanisms are integrated into the kernel, and files, devices, and drivers are outside the operating system. Let the whole system run through the API interface.

Disadvantage: Low efficiency Advantage: Strong stability (Huawei mobile phone)

eg: Hongmeng 

5. Drive module (three elements: entrance; exit; license)

  • Entrance: Application for resources
  • Export: release of resources
  • License: GPL (writing a module requires open source, because the Linux system is open source, so you need to write a license agreement)

#include <linux/init.h>

#include  

                                                                         

static int __init  hello_init(void) 

( __init may not be specified, and may not be written, but it is normally written)

//__init puts hello_init in the .init.text section

{

return 0;

}

static void __exit hello_exit(void)

//__exit puts hello_exit in the .exit.text section

{

}

module_init(hello_init);

//Tell the entry address of the kernel driver (the function name is the first address of the function)

module_exit(hello_exit);

//Tell the exit address of the kernel driver

MODULE_LICENSE("GPL");

//license

  • Makefile:

KERNELDIR:= /lib/modules/$(shell uname -r)/build/   //path to Ubuntu kernel

#KERNELDIR:= /home/linux/kernel/kernel-3.4.39/

(board kernel path)

PWD:=$(shell pwd) //path to drive file

(Open a terminal to see the path of the terminal)

all: //target

make -C $(KERNELDIR) M=$(PWD) modules

(-C: enter the top directory)

Note: Enter the kernel directory and execute the command make modules

If M=$(PWD) is not specified, the .c file in the kernel directory will be compiled into .ko

M=$(PWD) the path where you want to compile the module

clean:

make -C $(KERNELDIR) M=$(PWD) clean

obj-m:=hello.o //Specify the name of the compiled module

chasing code

Create index file

ctags -R

on the terminal

vi -t xxx

jump in code

ctrl + ]

ctrl + t

The kernel path corresponding to the Ubuntu kernel

hello.c代码部分:

#include <linux/init.h>
#include <linux/module.h>

//入口函数:申请资源
//存储类型 数据类型 制定存放区域  函数名(形参)
static int __init hello_init(void)
{
    return 0;
} 

//出口函数:释放资源
static void __exit hello_exit(void)
{

}

//入口
module_init(hello_init);
//出口
module_exit(hello_exit);
//许可证
MODULE_LICENSE("GPL");



Makefile代码部分:

#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = hello.o

6. Command :

sudo insmod hello.ko install driver module

sudo rmmod hello uninstall driver module

lsmod view modules

dmesg view messages

sudo dmesg -C directly clears the message without echoing

sudo dmesg -c empty after echo

7. The printing function in the kernel

Search for a function, after you find it, find any one in it, and look at the prototype of the function, it’s OK

printk(print level "content")

printk(KERN_ERR "Fail%d",a);

printk(KERN_ERR "%s:%s:%d\n",__FILE__,__func__,__LINE__);

(Which file, which function, which line is the driver in)

printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);

vi -t KERN_ERR (check the kernel printing level)

include/linux/printk.h

#define KERN_EMERG "<0>" /* system is unusable */ (system is not used)

#define KERN_ALERT "<1>" /* action must be taken immediately */ (be taken immediately)

#define KERN_CRIT "<2>" /* critical conditions */ (critical conditions, critical resources)

#define KERN_ERR "<3>" /* error conditions */ (Error)

#define KERN_WARNING    "<4>"   /* warning conditions           */(警告)

#define KERN_NOTICE "<5>"   /* normal but significant condition */(提示)

#define KERN_INFO "<6>" /* informational */ (level when printing information)

#define KERN_DEBUG "<7>" /* debug-level messages */  (debug level)

0 ------ 7

highest lowest

linux@ubuntu:~$ cat /proc/sys/kernel/printk 4 4 1 7

Terminal's level Default level for messages Maximum level for terminal Minimum level for terminal

#define console_loglevel (console_printk[0])

#define default_message_loglevel (console_printk[1])

#define minimum_console_loglevel (console_printk[2])                                               

#define default_console_loglevel (console_printk[3])

The message will only be displayed if the level of the message is greater than the terminal level

But if our Ubuntu is modified by the developer, all messages will not be automatically echoed .

Modify the system default level

su root

echo 4 3 1 7 > /proc/sys/kernel/printk

Default for virtual machines:

The default situation of the board: 

If you want to modify the printing level corresponding to the development board

vi  rootfs/etc/init.d/rcS

echo 4 3 1 7 > /proc/sys/kernel/printk

After adding it in rootfs /etc/init.d/rcS and then starting the board, the level of the board is as follows:

Messages are printed when installing and uninstalling drivers.

printk.c代码部分:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
//入口函数-申请资源
static int __init printk_init(void)
{
    printk("hello wrold.\n");
    printk(KERN_INFO "%s %s %d\n",__FILE__,__func__,__LINE__);//6
    printk(KERN_ALERT "---%s %s %d\n",__FILE__,__func__,__LINE__);//1
    return 0;
}

//出口函数-释放资源
static void __exit printk_exit(void)
{
    printk("welcome to hqyj.\n");
    printk(KERN_INFO "%s %s %d\n",__FILE__,__func__,__LINE__);//6
    printk(KERN_ALERT "***%s %s %d\n",__FILE__,__func__,__LINE__);//1
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");

Makefile代码部分:

KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
#KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = 1-printk.o

8. Drive multi-file compilation

hello.c  add.c

 Makefile

 obj-m:=demo.o

 demo-y+=hello.o add.o

( -y effect: put hello.o add.o into demo.o )

Finally generate the demo.ko file

9. Module passing parameters

  • How the command is delivered

sudo insmod demo.ko hello world

---------------------------------------------------------

 * Standard types are:                                                                             

 * byte, short, ushort, int, uint, long, ulong (char not found!!!!!!!!!)

 *  charp: a character pointer

 *  bool: a bool, values 0/1, y/n, Y/N.

 *  invbool: the above, only sense-reversed (N = true).

 

  • module_param(name, type, perm) 

Function: Receive the parameters passed by the command line

parameter:

@name : the name of the variable

@type : the type of the variable

@perm : permission 0664 0775 (other users only have read and execute permissions for me, no write permissions )

     modinfo hello.ko (check variables)

  • MODULE_PARM_DESC(_parm, desc)

Function: Describe the function of the variable

parameter:

@_parm: variable

@desc : description field

Can only pass decimal, can not write hexadecimal

practise:

1. How to use the byte type  (ascii is used to pass parameters)

2. How to pass a string to a pointer

sudo insmod hello.ko a=20 b=30 c=65 p="hello_world"

Note: When passing characters, write the A SCII code value; when passing strings, there must be no spaces

  • module_param_array(name, type, nump, perm) 

Function: Receive the array passed by the command line

parameter:

@name : array name

@type : the type of the array

@nump : the number of parameters, the address of the variable

@perm : permission

sudo insmod hello.ko a=121 b=10 c=65 p="hello" ww=1,2,3,4,5


param.c代码部分:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/moduleparam.h>
char ch = 'A';
module_param(ch, byte, 0770);
MODULE_PARM_DESC(ch, "this is char value.");

short a = 10;
module_param(a, short, 0775);
MODULE_PARM_DESC(a, "this is short value.");

char *p = NULL;
module_param(p, charp, 0774);
MODULE_PARM_DESC(p, "this is char pointer.");

int st[8];
int num; 
module_param_array(st,int,&num,0770);
MODULE_PARM_DESC(st,"this is 8 int number.");

//入口函数-申请资源
static int __init printk_init(void)
{
    int i;
    printk("hello wrold. num=%d\n",num);
    printk("ch=%c a=%d p=%s\n", ch, a, p);
    printk(KERN_INFO "%s %s %d\n", __FILE__, __func__, __LINE__);     //6
    printk(KERN_ALERT "---%s %s %d\n", __FILE__, __func__, __LINE__); //1
    for(i=0;i<num;i++)
    {
        printk("%d\n",st[i]);
    }
  
    return 0;
}

//出口函数-释放资源
static void __exit printk_exit(void)
{
    printk("welcome to hqyj.\n");
    printk("ch=%c a=%d p=%s-----\n", ch, a, p);
    printk(KERN_INFO "%s %s %d\n", __FILE__, __func__, __LINE__);     //6
    printk(KERN_ALERT "***%s %s %d\n", __FILE__, __func__, __LINE__); //1
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");


Makefile代码部分:

#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = param.o

  

10. 复习

1.模块

三要素

  • 入口

static int __init hello_init(void)

{

return 0;

}

module_init(hello_init);

  • 出口

static void __exit hello_exit(void)

{

}

module_exit(hello_exit)

  • 许可证

MODULE_LICENSE("GPL");

  • 多文件编译

obj-m:=demo.o

demo-y+=hello.o add.o

  • 内核中的打印:

printk(打印级别 “打印的内容”);

printk(“打印的内容”);

/proc/sys/kernel/printk

4    4    1     7 

出现这个错误,提示,说明scripts下没有生成相应的文件,cd到kernel所在目录,执行: make scripts

然后 make 就可以编译了

11. 字符设备驱动

linux系统中一切皆文件

  • 应用层:  APP1  APP2  ...

fd = open("led驱动的文件",O_RDWR);

read(fd);

write();

close();

  • 内核层:  

对灯写一个驱动

 led_driver.c

 driver_open();

 driver_read();

 driver_write();

 driver_close();

struct file_operations {

int (*open) (struct inode *, struct file *);   

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);       

int (*release) (struct inode *, struct file *);(close)

}

cdev:

Device number 1 Device number 2 Device number n

Device Driver 1 Device Driver 2 ....    Device Driver n

Device number: 32 bits, unsigned number

High 12 bits: major equipment number: which type of equipment is distinguished

Lower 20 bits: minor device number: distinguish which device of the same type

  • Hardware layer: LED uart ADC PWM

Each driver has a corresponding file_operations

  • The process of o pen:

open opens the file, which is related to the device number of the underlying driver.

Access the open function in struct file_operations in the device driver through the device number .

  • The process of read :

The open function will have a return value, the file descriptor fd, and the read function will pass fd

Find the read function in the struct file_operations in the driver .

Led driver: character device Steps:

  1. Register character device driver - get a character device driver frame, and get the device number
  2. Determine the hardware device for operation - LED light (initialization light)
  3. Initialize the lamp (first establish the mapping between the actual physical address and the virtual address of the lamp)- 

Based on operating system development, operating virtual memory,

  1. The interaction of copying user space data to kernel space data (when the user uses it, the driver will actually run, involving data interaction)
  2. Create a device file (device node) at the application layer

12. Registration of character device driver

  • int register_chrdev(unsigned int major, const char *name,

  const struct file_operations *fops)

Function: Register a character device driver

parameter:

@major: major device number  

  : If the value you fill in is greater than 0, it thinks this is the main device number

  : If the value you fill in is 0, the operating system will assign you a major device number

@name : name cat /proc/devices 

(

When registering a character device driver.

If successful, when you use the cat /proc/devices command to view, you can see the major device number and the name automatically assigned by the system

@fops : operation method structure

Return value: major>0, return 0 if successful, return error code ( negative number) if failed vi -t EIO

major=0, success major equipment number, failure return error code (negative number)   

  

  • void unregister_chrdev(unsigned int major, const char *name)

Function: Unregister a character device driver

parameter:

@major: major device number

@name: name

Return value: None

chrdev.c code part:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>

#define NAME "chrdev_led"

unsigned int major = 0;

int chrdev_open(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t chrdev_read(struct file *file_t, char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t chrdev_write(struct file *file_t, const char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
int chrdev_close(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations fops = {
    .open = chrdev_open,
    .read = chrdev_read,
    .write =chrdev_write,
    .release =chrdev_close,
};
//入口函数-申请资源
static int __init printk_init(void)
{
    //注册字符设备驱动
    major=register_chrdev(major, NAME, &fops);
    if(major < 0)
    {
        printk("register_chrdev err.");
        return -EINVAL;    
    }
    return 0;
}

//出口函数-释放资源
static void __exit printk_exit(void)
{
    //注销设备驱动
    unregister_chrdev(major,NAME);
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");

user_app.c code part:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
	int fd=open("./led",O_RDWR);
    
	char buf[32];
	read(fd,buf,sizeof(buf));
	write(fd,buf,sizeof(buf));

     close(fd);
	return 0;
}

Makefile.c code part:


#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = chrdev.o

13. Manually create device files

sudo mknod  led  (the path is arbitrary) c/b major device number minor device number

Remember to add -rf when sudo  rf  led  is deleted

14. Light up the LED on the board through the character device driver

app:      test.c  char buf[3]

                       1 0 0

   0 1 0

   0 0 1

------------------|------------------------

kernel:   led_driver.c

-------------------|------------------------

hardware:  RGB_led

  • How the application passes data to the driver ( the direction of reading and writing is from the user 's point of view )

#include 

  • int copy_from_user(void *to, const void __user *from, int n)

Function: Copy data from user space to kernel space ( when the user needs to write data )

parameter:

@to : the first address of memory in the kernel

@from: the first address of user space

@n : the length of the copied data (bytes)

Return value: returns 0 on success, returns the number of uncopied bytes on failure

  • int copy_to_user(void __user *to, const void *from, int n)

Function: copy data from kernel space to user space ( user starts to read data )

parameter:

@to : the first address of user space memory

@from: the first address of the kernel space

@n : the length of the copied data (bytes)

Return value: returns 0 on success, returns the number of uncopied bytes on failure

ch​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​Code of rdev.c:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define NAME "chrdev_led"

unsigned int major = 0;

char kbuf[32]="welcome to hqyj";

int chrdev_open(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t chrdev_read(struct file *file_t, char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    //将内核空间的数据拷贝到用户空间
    if (sizeof(kbuf) < n)
        n = sizeof(kbuf);
    if (copy_to_user(ubuf, kbuf, n) != 0)
    {
        printk("copy_to_user err.");
        return -EINVAL;
    }
    return 0;
}
ssize_t chrdev_write(struct file *file_t, const char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    if (sizeof(kbuf) < n)
        n = sizeof(kbuf);
        //将用户空间的数据拷贝到内核空间
    if (copy_from_user(kbuf, ubuf, n) != 0)
    {
        printk("copy_from_user err.");
        return -EINVAL;
    }
    printk("kbuf=%s\n", kbuf);
    return 0;
}
int chrdev_close(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations fops = {
    .open = chrdev_open,
    .read = chrdev_read,
    .write = chrdev_write,
    .release = chrdev_close,
};
//入口函数-申请资源
static int __init printk_init(void)
{
    //注册字符设备驱动
    major = register_chrdev(major, NAME, &fops);
    if (major < 0)
    {
        printk("register_chrdev err.");
        return -EINVAL;
    }
    return 0;
}

//出口函数-释放资源
static void __exit printk_exit(void)
{
    //注销设备驱动
    unregister_chrdev(major, NAME);
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");

user_app.c:​​​​​​​

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, const char *argv[])
{
	int fd = open("./led", O_RDWR);

	char buf[32];  
	char buf_r[32];
	fgets(buf, sizeof(buf), stdin); //hello world
	read(fd, buf_r, sizeof(buf_r));

	printf("buf_r=%s\n", buf_r);

	write(fd, buf, sizeof(buf));

	read(fd, buf_r, sizeof(buf_r));

	printf("buf_r=%s\n", buf_r);

	close(fd);
	return 0;
}

Makefile.c code:


#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = chrdev.o

  • How the driver operates registers

The register of the rgb_led light is a physical address . After the linux kernel is started, when using the address, all operations are virtual addresses . Physical addresses need to be converted to virtual addresses . The virtual address operated in the driver code is equivalent to the actual physical address operated .

physical address <------> virtual address

  • void * ioremap(phys_addr_t offset, unsigned long size)

( When __iomen tells the compiler, it is a byte size when fetched )

Function: Map physical address to virtual address

parameter:

@offset : The first address of the physical object to be mapped

@size : size (bytes) ( the mapping is in units of jobs, one page is 4K, that is, when you are less than 4k, the mapped area is 4k )

Return value: return virtual address successfully, return NULL((void *)0); 

  • void iounmap(void  *addr)

Function: Unmap

parameter:

@addr : virtual address

Return value: None

#define ENOMEM 12 /* Out of memory */

15. Lit

  • The idea of ​​software programming to control hardware:

We only need to write or read values ​​to the control registers to allow our processor to complete certain functions.

RGB_led 

1》GPIOxOUT: control pin output high and low level

RED_LED--->GPIOA28

GPIOAOUT --->  0xC001A000

GPIOA28 output high level:

GPIOAOUT[28] <-- write-- 1

GPIOA28 output low level:

GPIOAOUT[28] <-- write -- 0

2 "GPIOxOUTENB: Control the input and output mode of the pin

GPIOAOUTENB  ---> 0xC001A004

Set the GPIOA28 pin to output mode:

GPIOAOUTENB[28] <-- write -- 1

3》GPIOxALTFN: Control pin function selection

GPIOAALTFN1  ---> 0xC001A024

Set GPIOA28 pin as GPIO function:

GPIOAALTFN1[25:24]  <--写-- 0b00

00 = ALT Function0   

01 = ALT Function1 

10 = ALT Function2   

11 = ALT Function3 

GPIO pin function selection: every two bits control a GPIO pin

red  :gpioa28

GPIOXOUT: 0xC001A000 to control high and low levels

GPIOxOUTENB: input and output mode 0xC001A004

GPIOxALTFN1: function register 0xC001A024

( 36 bytes per register )

green:gpioe13

0xC001e000

blue :gpiob12

0xC001b000

practise:

1. Character device driver to realize running water lamp (30 minutes)

// read, modify, write

writel(v,c)

Function: Write a value to the address

parameter:

@ v : the value to write

@ c : address

readl(c)

Function: read an address and return the value in the address

parameter:

@c : address

head.h代码:
#ifndef __HEAD_H__
#define __HEAD_H__
#include <asm-generic/ioctl.h>

#define RED_ON _IO('A',1)
#define RED_OFF _IO('A',0)

#endif



chrdev.c代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/device.h>

#include "head.h"

#define NAME "chrdev_led"

//定义宏表示实际物理首地址
#define RED_BASE 0xc001a000  //#GPIOA28
#define GREE_BASE 0xc001e000 // #GPIOE13
#define BLUE_BASE 0xc001b000 //#GPIOB12

unsigned int major = 0;

char kbuf[32] = "welcome to hqyj";

//定义指针保存映射后的虚拟地址的首地址
unsigned int *red_addr = NULL;
unsigned int *gree_addr = NULL;
unsigned int *blue_addr = NULL;

struct class *cls = NULL;
struct device *dev = NULL;

int chrdev_open(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}
/*ssize_t chrdev_read(struct file *file_t, char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    //将内核空间的数据拷贝到用户空间
    if (sizeof(kbuf) < n)
        n = sizeof(kbuf);
    if (copy_to_user(ubuf, kbuf, n) != 0)
    {
        printk("copy_to_user err.");
        return -EINVAL;
    }
    return 0;
}
ssize_t chrdev_write(struct file *file_t, const char __user *ubuf, size_t n, loff_t *off_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    if (sizeof(kbuf) < n)
        n = sizeof(kbuf);
    //将用户空间的数据拷贝到内核空间
    if (copy_from_user(kbuf, ubuf, n) != 0)
    {
        printk("copy_from_user err.");
        return -EINVAL;
    }
    printk("kbuf=%s\n", kbuf);
    if (kbuf[0] == 1)
    {
        //红灯亮
        *red_addr |= (1 << 28);
    }
    else if (kbuf[0] == 0)
    {
        //红灯灭
        *red_addr &= (~(1 << 28));
    }
    if (kbuf[1] == 1)
    {
        *gree_addr |= (1 << 13);
    }
    else if (kbuf[1] == 0)
    {
        *gree_addr &= (~(1 << 13));
    }
    if (kbuf[2] == 1)
    {
        *blue_addr |= (1 << 12);
    }
    else if (kbuf[2] == 0)
    {
        *blue_addr &= (~(1 << 12));
    }
    return 0;
}*/

long chrdev_ioctl(struct file *file_t, unsigned int request, unsigned long n)
{
	switch(request)
	{
	case RED_ON:
        *red_addr |= (1 << 28);
		break;
	case RED_OFF:
        *red_addr &= (~(1 << 28));
		break;

	}
	return 0;
}

int chrdev_close(struct inode *node_t, struct file *file_t)
{
    printk("%s %s %d\n", __FILE__, __func__, __LINE__);
    return 0;
}

struct file_operations fops = {
    .open = chrdev_open,
   // .read = chrdev_read,
   // .write = chrdev_write,
    .unlocked_ioctl= chrdev_ioctl,
    .release = chrdev_close,
};
//入口函数-申请资源
static int __init printk_init(void)
{
    //注册字符设备驱动
    major = register_chrdev(major, NAME, &fops);
    if (major < 0)
    {
        printk("register_chrdev err.");
        return -EINVAL;
    }
    //初始化灯-引脚功能(GPIO) 输出功能  灭
    //1.基于操作系统开发。建立灯物理地址和虚拟地址之间映射
    //红灯
    red_addr = (unsigned int *)ioremap(RED_BASE, 40);
    if (red_addr == NULL)
    {
        printk("ioremap err.");
        return -EINVAL;
    }
    //gree
    gree_addr = (unsigned int *)ioremap(GREE_BASE, 40);
    if (gree_addr == NULL)
    {
        printk("ioremap gree err.");
        return -EINVAL;
    }
    blue_addr = (unsigned int *)ioremap(BLUE_BASE, 40);
    if (blue_addr == NULL)
    {
        printk("ioremap blue err.");
        return -EINVAL;
    }

    //通过虚拟地址操作实际物理地址向对应寄存器写值
    //配置引脚GPIO
    *(red_addr + 9) &= (~(3 << 24));
    *(red_addr + 1) |= (1 << 28); //输出模式
    *red_addr &= (~(1 << 28));

    *(gree_addr + 8) &= (~(3 << 26));
    *(gree_addr + 1) |= (1 << 13);
    *gree_addr &= (~(1 << 13));

    *(blue_addr + 8) |= (1 << 25);
    *(blue_addr + 8) &= (~(1 << 24));
    *(blue_addr + 1) |= (1 << 12);
    *blue_addr &= (~(1 << 12));

    //设置自动创建设备节点
    //1.提交目录信息
    cls = class_create(THIS_MODULE, NAME);
    if (IS_ERR(cls))
    {
        printk("class_create err.");
        return -EINVAL;
    }
    //2.提交文件信息
    dev = device_create(cls, NULL, MKDEV(major, 0), NULL, "led");
    if (IS_ERR(dev))
    {
        printk("class_create err.");
        return -EINVAL;
    }

    return 0;
}

//出口函数-释放资源(先申请的后释放,后申请先释放)
static void __exit printk_exit(void)
{
    //销毁创建的设备节点
    device_destroy(cls,MKDEV(major,0));
    class_destroy(cls);
    //取消映射
    iounmap(blue_addr);
    iounmap(gree_addr);
    iounmap(red_addr);
    //注销设备驱动
    unregister_chrdev(major, NAME);
}

module_init(printk_init);
module_exit(printk_exit);
MODULE_LICENSE("GPL");




user_app.c代码:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "head.h"

int main(int argc, const char *argv[])
{
	int fd = open("/dev/led", O_RDWR);

	while(1)
	{
		ioctl(fd,RED_ON);
		sleep(1);
		ioctl(fd,RED_OFF);
		sleep(1);
	}

	close(fd);
	return 0;
}




Makefile.c:


KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
#KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = chrdev.o

16. Device node creation problem (udev/mdev)

(mknod hello c 243 0, manually create device node hello)

( The macro has a return value : the result of executing the last sentence)

#include 

Automatically create device nodes:

struct class *cls;

  • cls = class_create(owner, name) /void class_destroy(struct class *cls)//销毁

Function: Submit directory information to user space (creation of kernel directory)

parameter:

@owner :THIS_MODULE ( add THIS_MODULE when you see owner )

@name : directory name

Return value: successfully return struct class * pointer

Failure to return error code pointer  int (-5)

if(IS_ERR(cls)){

return PTR_ERR(cls); ( PTR_ERR : Convert error code pointer to error code)

}

IS_ERR() : The return value is 0, not in the error code address range, non-zero, in the error code address range

Starting from address 0xffffffff, the kernel reserves 4K space as the address of the error code in the direction of address reduction.

  • struct device *device_create(struct class *class, struct device *parent,dev_t devt, void *drvdata, const char *fmt, ...) (creation of kernel files), each file corresponds to a peripheral (hardware device)

/void device_destroy(struct class *class, dev_t devt)//销毁

Function: Submit file information to user space

parameter:

@class : directory name

@parent:NULL

@devt : device number (major<< 12 |0 <=> MKDEV(major,0) )

@drvdata :NULL

@fmt : the name of the file

Return value: successfully return struct device * pointer

Failure returns error code pointer int (-5)

----------------------------------------------------------------

17. ioctl function

Note: What the user program does is tell the driver what it wants to do through the command code. As for how to explain these commands and how to implement them, this is what the driver has to do. The driver provides support for ioctl, and the user can use the ioctl function in the user program to control the I/O channel of the device   // GRE RED_ON E_ON BLUE_ON

(Function: control of input and output )

  • user:

#include 

  • int ioctl(int fd, int request, ...);(RED_ON

( Let the lighting code become concise )

parameter:

@fd : ​​the file descriptor generated by opening the file

@request: request code (reading and writing | the number of bytes passed by the third parameter),

: There is a definition method of this request code in sys/ioctl.h.

@... :Writable or not, if you want to write, write a memory address

--------------------------------------------------------

  • Kernel

( In the body of the ioctl function implemented in the driver, there is actually a switch{case} structure, each case corresponds to a command code, and some corresponding operations are performed. How to implement these operations is the responsibility of each programmer ; )

fops:

  • long (*unlocked_ioctl) (struct file *file, 

unsigned int request, unsigned long args);

When using the ioctl function, the main thing is the design of the request code, which is mainly designed in the sys/ioctl.h file.

Indicates the size of the bytes I read or write this time; look down how to combine the four fields when calling _IOC .

Look one by one, put the mouse on _IOC_DIRSHIFT , jump, and the following students will appear

#define _IO(type,nr)

_IOC(_IOC_NONE,(type),(nr),0)

#define _IOR(type,nr,size)

_IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOWR(type,nr,size)        _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define RDE_LED  _IO(type,nr)

These macros help you complete the encapsulation of the request code.

#define _IOC(dir,type,nr,size) \

(((dir)  << _IOC_DIRSHIFT) | \

 ((type) << _IOC_TYPESHIFT) | \

 ((nr)   << _IOC_NRSHIFT) | \

 ((size) << _IOC_SIZESHIFT))

dir << 30 | size<<16 | type << 8 | nr << 

 2           14         8          8

Direction Size Type Serial No.

(Direction: 00 01 10 11 related to reading and writing,)

         (size: sizeof(variable name))

        (Type: Combined into a unique non-overlapping integer, usually pass a character)

        (Serial number: indicates the number of the same type, write 0 when the light is turned on, and do not write 0 when it is turned off).

#define RLED_ON _IOWR('a',0,int) // Light on

#define RLED_OFF  _IOWR('a',1,int) //灭灯

The fields of command codes used in the kernel are declared in the following documents.

vi kernel-3.4.39/Documentation/ioctl$ vi ioctl-number.txt 

( The 2^32 power = 4G number, so it can be used. The idea of ​​the kernel is: each number represents one, and the function corresponds to the number one by one, but it is also possible to use the same when different drivers are used )

practise:

  1. The use of ioctl function
  2. request.h代码:
    
    #ifndef __REQUEST_H__
    #define __REQUEST_H__
    
    #include <asm-generic/ioctl.h>
    
    //命令码的约定,相当于获取唯一的一个key
    #define RED_type 'A'
    #define RED_ON _IO(RED_type,1)
    #define RED_OFF _IO(RED_type,0)
    
    #define GREE_type 'B'
    #define GREE_ON _IO(GREE_type,1)
    #define GREE_OFF _IO(GREE_type,0)
    
    #define BLUE_type 'C'
    #define BLUE_ON _IO(BLUE_type,1)
    #define BLUE_OFF _IO(BLUE_type,0)
    
    
    #endif
    
    
    
    
    
    chrdev.c代码:
    
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/printk.h>
    #include <linux/fs.h>
    #include <linux/uaccess.h>
    #include <asm/io.h>
    #include <linux/device.h>
    #include "request.h"
    
    #define NAME "chrdev_led"
    
    //用宏保存物理地址,物理地址不可更改
    #define RED_BASE 0xc001a000  //GPIOA28
    #define GREE_BASE 0xc001e000 //GPIOE13
    #define BLUE_BASE 0xc001b000 //GPIOB12
    
    //定义指针保存映射后的虚拟地址
    unsigned int *red_addr = NULL;
    unsigned int *gree_addr = NULL;
    unsigned int *blue_addr = NULL;
    
    struct class *cls = NULL;
    struct device *dev = NULL;
    
    int major = 0;
    char kbuf[32];
    int ret;
    
    int myopen(struct inode *node_t, struct file *file_t)
    {
        printk("%s %s %d\n", __FILE__, __func__, __LINE__);
        return 0;
    }
    
    int myclose(struct inode *inode_t, struct file *file_t)
    {
        printk("%s %s %d\n", __FILE__, __func__, __LINE__);
        return 0;
    }
    long myioctl(struct file *file_t, unsigned int request, unsigned long args)
    {
        //判断是那个请求完成对应的硬件控制
        switch (request)
        {
        case RED_ON:
            *red_addr |= (1 << 28);
            break;
        case RED_OFF:
            *red_addr &= (~(1 << 28));
            break;
        case GREE_ON:
            *gree_addr |= (1 << 13);
            break;
        case GREE_OFF:
            *gree_addr &= (~(1 << 13));
            break;
        case BLUE_ON:
            *blue_addr |= (1 << 12);
            break;
        case BLUE_OFF:
            *blue_addr &= (~(1 << 12));
            break;
        }
        return 0;
    }
    
    //点等法赋值
    struct file_operations fop = {
        .open = myopen,
        .unlocked_ioctl = myioctl,
        .release = myclose,
    };
    
    static int __init hello_init(void)
    {
        //注册字符设备驱动
        major = register_chrdev(major, NAME, &fop);
        if (major < 0)
        {
            printk("register chrdev error.\n");
            return -EINVAL;
        }
    
        //灯 - 操作硬件需要建立虚拟地址和物理地址的映射关系
        //红灯
        red_addr = (unsigned int *)ioremap(RED_BASE, 40);
        if (red_addr == NULL)
        {
            printk("ioremap red led err.\n");
            return -EINVAL;
        }
        //绿灯
        gree_addr = (unsigned int *)ioremap(GREE_BASE, 40);
        if (gree_addr == NULL)
        {
            printk("ioremap gree led err.\n");
            return -EINVAL;
        }
        //蓝灯
        blue_addr = (unsigned int *)ioremap(BLUE_BASE, 40);
        if (blue_addr == NULL)
        {
            printk("ioremap blue led err.\n");
            return -EINVAL;
        }
    
        //初始化灯,全部初始化为关闭状态
        *(red_addr + 9) &= (~(3 << 24));
        *(red_addr + 1) |= (1 << 28);
        *red_addr &= (~(1 << 28)); //灭
    
        *(gree_addr + 8) &= (~(3 << 26));
        *(gree_addr + 1) |= (1 << 13);
        *gree_addr &= (~(1 << 13));
    
        *(blue_addr + 8) &= (~(1 << 24));
        *(blue_addr + 8) |= (1 << 25);
        *(blue_addr + 1) |= (1 << 12);
        *blue_addr &= (~(1 << 12));
    
        //设置自动创建设备节点
        //提交目录信息
        cls = class_create(THIS_MODULE, NAME);
        if (IS_ERR(cls))
        {
            printk("class create err.\n");
            return -EINVAL;
        }
        //提交文件信息
        dev = device_create(cls, NULL, MKDEV(major, 0), NULL, NAME);
        if (IS_ERR(dev))
        {
            printk("device create err.\n");
            return -EINVAL;
        }
        return 0;
    }
    
    static void __exit hello_exit(void)
    {
        device_destroy(cls, MKDEV(major, 0));
        class_destroy(cls);
        //取消映射
        iounmap(blue_addr);
        iounmap(gree_addr);
        iounmap(red_addr);
        //注销字符设备驱动
        unregister_chrdev(major, NAME);
    }
    
    module_init(hello_init); //入口:申请资源  本质-回调一个自己写的函数
    module_exit(hello_exit); //出口:释放资源
    MODULE_LICENSE("GPL");   //许可证  :公共许可协议(开源协议)
    
    
    
    use_app.c代码:
    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    
    #include "request.h"
    
    int main(int argc, const char *argv[])
    {
    	int fd=open("/dev/chrdev_led",O_RDWR);
    	if(fd < 0)
    	{
    		perror("open led err.");
    		return -1;
    	}
    
    	while(1)
    	{
    		ioctl(fd,RED_ON,NULL);
    		sleep(1);
    		ioctl(fd,RED_OFF,NULL);
    		sleep(1);
    		ioctl(fd,GREE_ON,NULL);
    		sleep(1);
    		ioctl(fd,GREE_OFF,NULL);
    		sleep(1);
    		ioctl(fd,BLUE_ON,NULL);
    		sleep(1);
    		ioctl(fd,BLUE_OFF,NULL);
    		sleep(1);
    	}
    
    	close(fd);
    
    	return 0;
    }
    
    
    
    Makefile代码;
    
     KERNELDIR = /home/hq/kernel/kernel-3.4.39   #开发板路径
     #KERNELDIR=/lib/modules/$(shell uname -r)/build  #pc机
     
     PWD=$(shell pwd)
     
     all:
         make -C $(KERNELDIR) M=$(PWD)  modules      
         #基于内核框架将驱动代码编译生成驱动模块
         #需要在内核的顶层目录下执行make modules.
         #-C:指定到那个路径下执行这个命令
         #M赋值:要将那个路径下的驱动文件编译生成驱动模块
     
     .PHONY:clean
     clean:
         make -C $(KERNELDIR) M=$(PWD)  clean     
     
     obj-m += chrdev.o 
    

18. Linux kernel interrupt

Eg:

When the button is pressed in ARM , it will first execute the irq in the exception vector table in the assembly file start.s , and perform some operations in the irq .

Then jump to C's do _irq();

Operation: 1) Judge the sequence number of the interrupt; 2) Process the interrupt; 3) Clear the interrupt;

The principles of Linux kernel implementation and ARM logic implementation of interrupt are the same.

Kernel: When the button is pressed, it still goes to the exception vector table, and then to the handler_irq function (hard-coded). An array is defined in handler_irq. Each member of the array stores a structure. There is a function pointer in the structure. This function pointer points to the name of the function we submitted; (the subscript of the array is the soft interrupt number of the Linux kernel, and there is a mapping relationship between it and the hardware interrupt number). When the kernel implements interrupts, the interrupt registers are initialized in the handler_irq function. We only need to get the soft interrupt number and bind my interrupt processing function.

  • int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

const char *name, void *dev)

Function: Register Interrupt

parameter:

@irq :  soft interrupt number 

gpio soft interrupt number

Soft interrupt number = gpio_to_irq( gpi no number ); //160--"0-159  

gpiono = m*32+n (n: serial number in the group)

m: Which group A B C D E (5 groups)

  0 1 2 3 4

gpioa28 = 0*32+28   

gpiob8 =1*32+8      gpiob16 = 1*32+16

Controller Interrupt Number (A DC ):

find -name irqs.h (find in the kernel source code)

find -name irqs.h ./arch/arm/mach-s5p6818/include/mach/irqs.h

find -name s5p6818_irq.h    ./arch/arm/mach-s5p6818/include/mach/s5p6818_irq.h

#define IRQ_PHY_ADC (41 + 32) //IRQ_PHY_ADC soft interrupt number

@handler: interrupt handler

irqreturn_t (*irq_handler_t)(int irqno, void *dev);

IRQ_NONE //The interrupt has not been processed

IRQ_HANDLED //Interrupt normal processing completed

@flags : How the interrupt is triggered

#define IRQF_DISABLED 0x00000020 //Quick interrupt (write it in the processing function, handle this interrupt first)

#define IRQF_SHARED 0x00000080 // Shared interrupt ( There are fewer interrupt interfaces, but all devices want to be interrupted, so two pins need to be connected externally. There is an interrupt status flag in the register. Check whether the interrupt status flag is set . One port cannot link two buttons, and the buttons cannot be distinguished )

#define IRQF_TRIGGER_RISING 0x00000001 ( rising edge trigger )

#define IRQF_TRIGGER_FALLING 0x00000002 ( falling edge trigger )

#define IRQF_TRIGGER_HIGH 0x00000004

#define IRQF_TRIGGER_LOW 0x00000008

@name :名字   cat /proc/interrupts

@dev  :向中断处理函数中传递参数 ,不想传就写为NULL

返回值:成功0,失败返回错误码

  • void free_irq(unsigned int irq, void *dev_id)

功能:注销中断

参数:

@irq :软中断号

@dev_id:向中断处理函数中传递的参数,不想传就写为NULL

  • Eg:按键所对应的中断号是多少?及找所对应的GPIO
  • 第一步:找底板原理图,找到按键

  • 第二步:拷贝网络标号,到核心板

及对应的软中断号为:gpio_to_irq gpiob8 = 1*32+8);gpio_to_irq gpiob16 = 1*32+16

ARRAY_SIZE计算数组里面元素的个数;

  • 问题解决方法

[root@farsight]#insmod farsight_irq.ko 

[   21.262000] request irq146 error

insmod: can't insert 'farsight_irq.ko': Device or resource busy

通过 cat /proc/interrupts

146:        GPIO  nxp-keypad

154:        GPIO  nxp-keypad

说明中断号已经被占用了

  • 解决办法:在内核中将这个驱动删掉

How to determine who is the name of the driver file?

grep "nxp-keypad" * -nR

arch/arm/mach-s5p6818/include/mach/devices.h:48:

#define DEV_NAME_KEYPAD  "nxp-keypad"

grep "DEV_NAME_KEYPAD" * -nR

drivers/input/keyboard/nxp_io_key.c:324: .name = DEV_NAME_KEYPAD,

The name of the driver file is nxp_io_key.c

Find the name of the macro, know it in Makefine;

How to remove him from the kernel?

The name of the options menu? Kconfig

config KEYBOARD_NXP_KEY

    tristate "SLsiAP push Keypad support"

make menuconfig

<>SLsiAP push Keypad support

After removing the * in the graphical interface, you can delete nxp _io_key . o , so that when you compile the kernel again, you can see whether nxp _io_key . c is ready for compilation. If it is compiled, the corresponding .

make uImage recompiles the kernel

cp  arch/arm/boot/uImage ~/tftpboot

Reboot the board;

install driver:

Then press the key to test;

interrupt.c代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>

#define GPIO_NO(n,m) (n*32+m)
#define GPIONO_B8  GPIO_NO(1,8)
#define GPIONO_B16  GPIO_NO(1,16)

int i;
int gpiono[]={GPIONO_B8,GPIONO_B16};

char *name[]={"interrupt_b8","interrupt_b16"};

//中断处理函数
irqreturn_t handler_irq(int irqno, void *dev)
{
    if(irqno==gpio_to_irq(GPIONO_B8))
    {
        printk(KERN_ERR "++++++++++++++++++++++++++++++++++\n");
    }
   if(irqno==gpio_to_irq(GPIONO_B16))
    {
        printk(KERN_ERR "-----------------------------------\n");
    }
    return IRQ_HANDLED;
}

static int __init interrupt_init(void)
{
    //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        if(request_irq(gpio_to_irq(gpiono[i]),handler_irq,IRQF_TRIGGER_FALLING,name[i],NULL)!=0)
        {
            printk("request irq err.");
            return -EINVAL;
        }
    }
    return 0;
}
static void __exit interrupt_exit(void)
{
   //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        free_irq(gpio_to_irq(gpiono[i]),NULL);
    }
}

module_init(interrupt_init);
module_exit(interrupt_exit);
MODULE_LICENSE("GPL");



Makefile.c 代码:

KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
#KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = interrupt.o

19. Linux kernel timer

  • How to get the current time of the timer?

jiffies: number of kernel clock ticks

jiffies start counting at the moment the board is powered on, as long as

The board is constantly powered on, and this value keeps increasing (64 bits). exist

It can be used directly in the driver code.

  • How long does it take to add 1 to the timer?

在内核顶层目录下有.config

CONFIG_HZ=1000

周期 = 1/CONFIG_HZ

周期是1ms;

  • 分配的对象

struct timer_list mytimer;

  • 对象的初始化

struct timer_list {

unsigned long expires;   //定时的时间

void (*function)(unsigned long); //定时器的处理函数

unsigned long data;      //向定时器处理函数中填写的值

};

void timer_function(unsigned long data) //定时器的处理函数

{

}

mytimer.expries = jiffies + 1000;  //1s

mytimer.function = timer_function;

mytimer.data = 0;

init_timer(&mytimer);  //内核帮你填充你未填充的对象

  • 对象的添加定时器

void add_timer(struct timer_list *timer);

//同一个定时器只能被添加一次,

//在你添加定时器的时候定时器就启动了,只会执行一次

int mod_timer(struct timer_list *timer, unsigned long expires)

//再次启动定时器                          jiffies+1000

  • 4.对象的删除

int del_timer(struct timer_list *timer)

//删除定时器

 Int gpio_get_value(int gpiono);//通过gpiono获取当权gpio的所处状态

  返回0,低电平   非0:高电平

interrupt_time.c代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/timer.h>

#define GPIO_NO(n,m) (n*32+m)
#define GPIONO_B8  GPIO_NO(1,8)
#define GPIONO_B16  GPIO_NO(1,16)

int i;
int gpiono[]={GPIONO_B8,GPIONO_B16};
char *name[]={"interrupt_b8","interrupt_b16"};

//分配一个定时器对象
struct timer_list mytimer;

//中断处理函数
irqreturn_t handler_irq(int irqno, void *dev)
{
    //再次启动定时器
    mod_timer(&mytimer,jiffies+120);
    return IRQ_HANDLED;
}
//定时处理函数,时间到就调用这个函数
void time_fun(unsigned long data)
{
    //1.获取引脚状态 0 - 处理中断
    if(gpio_get_value(GPIONO_B8)==0)
    {
      printk(KERN_ERR "++++++++++++++++++++++++++++++++++\n"); 
    }
    if(gpio_get_value(GPIONO_B16)==0)
    {
      printk(KERN_ERR "----------------------------------\n"); 
    }
}

static int __init interrupt_init(void)
{
    //初始化定时器
    mytimer.expires=jiffies+120;
    mytimer.function=time_fun;
    mytimer.data=0;
    init_timer(&mytimer);

    //添加定时器
    add_timer(&mytimer);

    //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        if(request_irq(gpio_to_irq(gpiono[i]),handler_irq,IRQF_TRIGGER_FALLING,name[i],NULL)!=0)
        {
            printk("request irq err.");
            return -EINVAL;
        }
    }
    return 0;
}
static void __exit interrupt_exit(void)
{
   //注册中断
    for(i=0;i<sizeof(gpiono)/sizeof(int);i++)
    {
        free_irq(gpio_to_irq(gpiono[i]),NULL);
    }
}

module_init(interrupt_init);
module_exit(interrupt_exit);
MODULE_LICENSE("GPL");



Makefile。c代码:

KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
#KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = interrupt.o

20. 模块导出符号表

Thinking 1 : There are two app programs in the application layer, app1 has an add function, can app2 call the add function in app1 when app1 is running? No, because the space where the application layer app runs is private (0-3G) and not shared.

Thinking 2 : Two driver modules, can the functions in module1 be called by module2? Yes, they share (3-4G) kernel space, just need to find the address of the function . Benefits: Reduce code redundancy, and code will not be loaded repeatedly in memory. The code is more streamlined, some codes do not need to be written, just call the functions written by others directly.

When writing driver code to find functions in other drivers, you need to use the module export symbol table to export the function, and the recipient can use this function. He is a macro function .

In one module of the driver, to use a function/variable in another module, only need to use EXPORT_SYMBOL_GPL to export the address of the variable or function. Users can use this address to call it.

EXPORT_SYMBOL_GPL(sym)

sym: variable name or function name

Code Example 1 : Two independent code-driven modules

Code example 2: The provider is a driver that has been installed and used by the kernel

moudule1.c代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>

int number=1000;

int add(int a,int b)
{
    return a+b;
}

int sub(int a,int b)
{
    return a-b;
}

EXPORT_SYMBOL_GPL(number);
EXPORT_SYMBOL_GPL(add);
EXPORT_SYMBOL_GPL(sub);

static int __init module1_init(void)
{
    printk("%s add=%d sub=%d\n",__func__,add(2,3),sub(2,3));
    return 0;
}
static void __exit module1_exit(void)
{
  printk("module1 exit.\n");
}

module_init(module1_init);
module_exit(module1_exit);
MODULE_LICENSE("GPL");




Makefile.c代码:

#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = module1.o
Moudle2.c代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>

extern int number;
extern int add(int a,int b);
extern int sub(int a,int b);

static int __init module2_init(void)
{
    printk("%s add=%d sub=%d\n",__func__,add(5,2),sub(5,2));
    printk("number=%d\n",number);
    return 0;
}

static void __exit module2_exit(void)
{
  printk("bye~\n");
}

module_init(module2_init);
module_exit(module2_exit);
MODULE_LICENSE("GPL");





Makefile.c代码:

#KERNEL_PATH=/home/hq/kernel/kernel-3.4.39   #开发板内核路径
KERNEL_PATH=/lib/modules/$(shell uname -r)/build
      #pc电脑上的路径 gcc

PWD=$(shell pwd)
all:
	make -C $(KERNEL_PATH) M=$(PWD) modules       
	#在内核顶层目录执行make modules才可以将hello.c生成驱动
	#-C 路径:找到这个路径执行这个命令
.PHONY:clean
clean:
	make -C $(KERNEL_PATH) M=$(PWD) clean

obj-m = module2.o

Summary :

Compile:

1. Compile the provider first, and a Module.symvers will be generated after the compilation is completed

2. Copy Module.symvers to the directory of the caller

3. Just compile the caller

Install:

Install the provider first

reinstall caller

Uninstall:

Uninstall the caller first

Uninstall the provider again

If the caller and the provider are two independent (xx.ko) driver modules, when the address is passed between them, it is passed through Module.symvers.

If the provider is a kernel module (uImage), there is no need for the Module.symvers file to pass information between the caller and the provider .

 Summarize:

The bottom layer of linux: arm, linux system transplantation, driver development
   arm-program operation principle, hardware control principle
      
    c language, basic framework idea of ​​circuit
    
, and solution to the problem.
Practical operation - many questions - patience,
more theory - boring

LED light control: Through     analyzing
   the circuit diagram, the corresponding pin outputs a high level light       on the pin
   - multi-function pin (GPIO) input
   or output - output function
   output high or low level - output high level light on, low level light off     2.       Modify cpsr         1 "arm state         2" switch to the corresponding mode         3 "prohibit the corresponding interrupt 3.
   
   
   
Save        the return address to lr 4. pc jumps to the corresponding exception    handling       position in the exception vector table
   .       Exception vector table: 0x00000000 4 bytes are reserved for each exception to deal with. The corresponding exception         is a fixed address space, which specifies 0x00000000-0x0000001c
   










      

      
      Write by yourself: exception handling function (recovery process)
        push stack to save scene stmfd sp!,{r0-r12,lr}
        processing process
        pop out stack and restore scene-cpsr->spsr pc->lr ldmfd sp!,{r0-r12,pc}^

 The principle of controlling hardware:
    
 led - 
   1. Pin function selection
   2. Input or output - output function
   3. Output high or low level - output high level light on

   GPIOA28-red
   GPIOE13-gree
   GPIOB12-blue
   
Configure the ip address of the development board:
setenv ipaddr 192.168.1.66
setenv netmask 255.255.255.0
setenv gatewayip 192.168.1.1
setenv serverip 192.168.1.99
saveenv

开发阶段系统部署:
举例:手动验证
uImage放到tftpboot文件中
uboot命令终端:tftp 0x48000000 uImage   下载内核到开发板的内存中
               bootm 0x48000000         从开大坂内存中启动程序
   
设置自启动:
   bootcmd  - 设置自动下载内核镜像
   bootargs - 设置自动挂载跟文件系统
   
   setenv bootcmd  tftp 0x48000000 uImage\;bootm 0x48000000
   setenv bootargs root=/dev/nfs  nfsroot=192.168.1.99:/home/hq/nfs/rootfs rw console=ttySAC0,115200 init=/linuxrc ip=192.168.1.66
   saveenv
   
移植的代码:
  1.uboot - 第一段代码(裸机代码)
    作用:初始化部分硬件、引导加载内核、给内核传递参数、uboot命令
    uboot命令-setenv saveent  设置更改、保存(flash)环境变量的值
              ping ip  ->检测是否能对应ip通信
              loadb   ->串口下载代码
              go      ->启动程序
      uboot:         tftp 0x42000000 ubootpak.bin          update_mmc 2 2ndboot 0x42000000 0x200 0x78000





  





  



     


      


      uImage:
        tftp 0x42000000 uImage 
        mmc write 0x42000000 0x800 0x4000
        
      root file:
        tftp 0x42000000 ramdisk.img 
        mmc write 0x42000000 0x20800 0x20800
        
   /*You can start directly without network: read
     the code from flash Start the process of reading
     mmc read 0x48000000 0x800 0x4000
     mmc read 0x49000000 0x20800 0x20800
     bootm 0x48000000 0x49000000  in memory
     . Direct operation and file system startup fail. */
     
   Set self-starting parameters: bootcmd bootargs
   setenv bootcmd mmc read 0x48000000 0x800 0x4000\;mmc read 0x49000000 0x20800 0x20800\;bootm 0x48000000 0x49000000 setenv
   bootar gs root=/dev/ram rw initrd=0x49000040,0x1000000 rootfstype=ext4 init=/linuxrc console=ttySAC0,115200
   saveenv
     

The process of uboot compilation:
   preparation-configure code, configure compilation tools, modify Makefile
    to execute compilation instructions:
   1.make clean/distclean clear
   2.make fs6818_config support public board
   3.make compile
     

内核的编译过程:
  准备-配置代码、配置编译工具、修改Makefile
  指令:
    1.make clean/distclean 清除
    2.make fs6818_defconfig  支持公板
    3.make menuconfig        
        菜单选项(选择是否需要编译到内核的驱动,是否编译生成模块)
  // make
       4.make uImage     直接编译生成uImage镜像 
    
    三个文件:Makefile Kconfig .config 
   
  举例:将led灯的驱动
    1》将驱动编译到内核中 - 内核移植成功,直接使用
       1.将驱动放到对应文件
       2.vi Makefile 
         obj-$(CONFIG_RGB_LED)   += fs6818_led.o   
       3.vi Kconfig
         config RGB_LED
            tristate "rgb_led char driver"
            help
            this is led driver!!!    
            
       bool/tristate  两态/三态
       bool - y(编译到内核) n(不编译到内核)
       tristate  - y  M(编译生成模块)   N     
     4.回到内核顶层目录执行:
        make menuconfig 选择将驱动添加编译到内核
     5.make uImage 
        生成的uImage就有驱动     

    2 "Compile the driver separately to generate the driver module    
        - first transplant the kernel and start it successfully, then install the driver, and then the driver can use
        different steps:
          make menuconfig Select to compile the driver separately to generate a module
          
          make modules - get the driver module
           fs6818_led.ko
          
    The steps to install the driver: first transplant a kernel that does not include the led driver ,
      put fs6818_led.ko into the mounted root file system , and complete the transplant: execute        insmod fs6818_led.ko 
      on the development board to install the driver        lsmod Check the driver        rmmod fs6818_led Unloading the driver Compiling the driver  into the kernel or compiling and generating the driver module What are the respective advantages?  make Target Makefile:       Target: Dependency     <tab>Command.  PHONY: Target 1   Target 1:   <tab> Command    = - "Recursive Assignment   : = -" Immediate Assignment   += - "Append Recursive Assignment   ? = -" Ask Assignment



        
        

 
 

     



 



  




   
  make工具的特点->根据文件时间戳编译文件
  Makefile ->工程文本文件,make唯一读入配置文件
    内容是工程的编译过程。
    
 
 命令的三要素:
   命令名称 [选项 ...]  [参数] ....
   ls  -l  文件名 
 
 特殊makefile变量:一般情况下程序默认命令规则
   CC=编译器
   CFLAGS=选项 
   OBJS=目标
   RM=rm -f   
 
 -C 路径  ->到指定路径执行对应指令
  M=路径   ->到某个路径编译路径下的c程序
 
  变量=$(shell ls)   -》将命令执行的结果赋值给一个变量
 
 驱动:
    裸机开发和驱动开发都是操作硬件
    区别:
 
    内核五大功能:
      进程、网络、设备、内存、文件 
      设备驱动
        字符设备驱动:  
        块设备驱动: 
        网卡设备驱动:
 
  宏内核、微内核
     
  驱动三要素:
    入口:申请资源
    出口:释放资源 
    许可证:GPL
    
     Open read write close       will assign a unique device number when creating the driver, through the device number

  



 


       



 


     




     


     

     



      The created device node (device file). There is a one-to-one correspondence between them.
      
      Device number = major device number + minor device number
   
   to build a framework for character device drivers:
     register character device driver - register_chrdev
    major device number = register_chrdev(major device number (0-automatically assigned by the system), driver name, & fops) copy_from_user copy_to_user major
    
   
    device 
    number
    
    
     = register_chrdev
     initialize hardware - based on operating system development, the operation is virtual memory physical
       memory and virtual memory mapping
       virtual address first address = ioremap (physical address, size)
       io unmap (virtual address)
     
     automatically creates device nodes:
       cls=class_create(THIS_MODULE,NAME);
       dev=device_create(cls,NULL,MKDEV(major device number,0-minor device number),NULL,"led")
    
    
   

Guess you like

Origin blog.csdn.net/qq_52049228/article/details/131676532