Linux S3C2440 学习笔记02


(韦东山老师课程)


韦老师官网链接


开发板接口和串口连接
  1. 使用串口观察信息
  2. 使用JTAG烧写程序
MobaXterm设置

Session
Serial
Serial Port 设置
Speed(bps)115200
在这里插入图片描述
Flow control流量控制一定要设置为None

eoploadTool 安装

管理员打开
在这里插入图片描述
在这里插入图片描述
bin文件烧写
对应文件路径

oflash 文件名.bin

然后 01000 
0 OpenJTAG
1 S3C2440
0 Nand
0 Nand
0 0地址开始

开关设置为Nand
拔掉排线
按电源重启
USB下载

使用排线烧写u-boot到Nor
重启电源,立刻在MobaXterm中按下空格
输入n
使用zadig 安装libusb
使用dnw传输文件

系统启动流程

PC
BIOS
启动内容
windows操作系统
识别挂载C盘,内含APP
存储:硬盘

Linux
bootloader(uboot是其中最广泛的一种)
引导启动LInux内核
挂载根文件系统 内含APP
存储:flash
Nor flash 1~2M : bootloader
Nand flash 256M : bootloader params kernel rootfs

恢复出厂系统

  1. 烧写uboot Nor/Nand op/eop烧写
  2. 烧写 Kernal USB下载烧写 重启电源,立刻在MobaXterm中按下空格 输入k DNW传输uImage_3.5
  3. 烧写文件系统 输入y DNW传输fs_qtopia.yaffs2
  4. 删除para 使用默认参数 输入q 再输入mtd
nand erase params
Ping设置
set ipaddr 192.168.1.122
汇编指令
指令全称中文
ldrload读内存
strstore写内存
b跳转
movmove移动
BLbranch and link跳转到某一个地址并把返回地址(下一条指令的地址)保存在lr寄存器
ldmm->many读内存,写入多个寄存器
stmm->many把多个寄存器的值,写入内存
汇编指令示例

sub r0,r1,#4
r0 = r1-4


add r0,r1,#4
r0 = r1+4





MOV
1.可以寄存器与寄存器之间传递数据

2.可以常数传递到寄存器中(常数不能超过32位)

LDR

1.可以地址与寄存器之间的数据传递

2.也可以常数传递到寄存器中

在这里插入图片描述

R0~R14 CPU中的寄存器
a1~pc 寄存器的别名


pc Program Counter 程序计数器 只要把地址写进这个寄存器,程序就会跳到那个地址 用来存储指向下一条指令的地址,也即将要执行的指令代码
pc地址 = 当前指令+8
因为arm架构可以理解为流水线,在上一个命令执行a+4时,已经开始解释下一条命令a+8


lr Link Register 用来保存返回地址


sp Stack Pointer 栈指针

拓展:哈佛结构和冯诺依曼结构

引用自这里
哈佛结构认为CPU应该分别通过2组独立的总线来对接指令和数据


冯诺依曼结构认为CPU通过1组总线来分时获取指令和数据即可。


51单片机、典型的STM32单片机(核心是ARM Cortex-M系列的)都是哈佛结构


PC和服务器芯片(譬如Intel AMD那些出的),ARMCortex-A系列嵌入式芯片(譬如核心是ARM CortexA8的三星S5PV210,譬如华为的麒麟970等手机芯片)等都是冯诺依曼结构。这些系统都需要大量内存,所以工作内存都是DRAM,因为他们更适合使用冯诺依曼系统。


现代的CPU(准确说叫SoC)基本都不是纯粹的哈佛结构或冯诺依曼机构,而都是混合结构的。

ldr r0,[r1]
读取地址r1上的数据,存到r0

str r0,[r1]
把r0的值写到r1这个地址上面

mov r0,r1
把r0的值赋给r1 r0=r1

mov r0,#0x100
r0 = 0x100

ldr r0,=0x12345678

伪指令,它会拆分成几条指令
r0=0x12345678

ARM 指令值的大小<32位

汇编指令点灯示例
/* 
 * 点亮LED:gpf4
 */

/*代码段*/
.text

//.global关键字用来让一个符号对链接器可见,可以供其他链接对象模块使用。
//.global _start 让 _start 符号成为可见的标识符,这样链接器就知道跳转到程序中的什么地方并开始执行。linux寻找这个 _start 标签作为程序的默认进入点。
.global _start

_start:
/* 配置GPF4为输出引脚
 * 把0x100写到地址0x560000050
 */

// 使用伪指令把0x5600050放到r1里面
    ldr r1, =0x56000050
    ldr r0, =0x100

//  把r0赋值到r1这个地址上面
    str r0,[r1]


 /* 设置GPF4输出高电平
  * 把0x10写到地址0x56000054
  */
      ldr r1, =0x56000054
      ldr r0, =0
      str r0,[r1]

//死循环
halt:
    //跳转到死循环
    b halt
linux上编译指令

一定要注意空格

  1. 预编译
arm-linux-gcc -c -o led_on.o led_on.S

-c 预编译,只编译不链接
-o output 输出目标文件

  1. 链接
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf

arm-linux-ld
简称(Link editor)
是一个链接程序工具,其作用主要是将汇编过的多个二进制文件进行链接,成为一个可执行的二进制文件.
-Ttext ADDRESS 代码段链接地址

  1. 转换成二进制bin文件
 arm-linux-objcopy -O binary -S led_on.elf led_on.bin

arm-linux-objcopy用于复制一个目标文件的内容到另一个文件中,可以使用不同于原目标文件的格式来输出目的文件,即其可以进行格式转换。
-O 大写 output 输出

反汇编

	arm-linux-objdump -D led_on.elf > led_on.dis

查看目标文件(.o文件)和库文件(.a文件)信息
-D 显示文件中所有汇编信息
‘>’ 表示将这个程序的反汇编程序写入到led.dis这个文件中,在终端中不显示出来.

反编译后的文件分析


led_on.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:

  //从内存pc,#20中读取值,就是从地址1c读取56000050到r1里面
  //r1 = pc+20 因为pc 从当前指令0开始+8 r1=0+8+20=0x1c,去0x1c这个地址读取内存的值
   0:	e59f1014 	ldr	r1, [pc, #20]	; 1c <.text+0x1c>

   //把256放到r0 r0=0x100
   4:	e3a00c01 	mov	r0, #256	; 0x100

   //把r0即0x100写入r1对应的地址
   8:	e5810000 	str	r0, [r1]

   //从内存pc,#12中读取值,就是从地址20读取56000054到r1里面
   //r1 = pc+20 因为pc 从当前指令地址c开始,所以 r1=c+8+12=32=0x20,去0x20这个地址读取内存的值存到r1
   c:	e59f100c 	ldr	r1, [pc, #12]	; 20 <.text+0x20>
  10:	e3a00000 	mov	r0, #0	; 0x0
  14:	e5810000 	str	r0, [r1]

00000018 <halt>:
  18:	eafffffe 	b	18 <halt>
  1c:	56000050 	undefined
  20:	56000054 	undefined


在这里插入图片描述

Makefile
all:
	arm-linux-gcc -c -o led_on.o led_on.S
	arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
	arm-linux-objcopy -O binary -S led_on.elf led_on.bin
clean:
	rm *.bin *.o *.elf

进行编译

make

进行删除

make clean
windows快速在当前路径执行cmd命令的小技巧

在这里插入图片描述

程序上传过程

我个人感觉完全没必要使用网线通过nfs传输。。

程序项目文件使用vsCode编写,存到window&linux共享文件夹,然后在虚拟机ubuntu上面使用arm-gcc编译,编译好了在window共享文件夹上用oflash通过排线将bin文件烧录到开发板

linux环境作用:arm-linux gcc方便使用linux头文件进行编译
windows环境作用:日常电脑系统,主要使用上位机软件oflash烧录程序到开发板
在这里插入图片描述

字节序

小字节序存储:低位存在低地址
大字节序存储:高位存在低地址

C语言点灯示例

int main()
{
    unsigned int* pGPFCON = 0x56000050;
    unsigned int* pGPFDAT = 0x56000054;

    //配置GPF4引脚为输出
    *pGPFCON = 0x100;

    //设置GPF4输出0
    *pGPFDAT=0;

    return 0;
}
设置main入口


//.text 指定了后续编译出来的内容放在代码段
.text
.global _start

//_start是一个函数的起始地址,也是编译、链接后程序的起始地址。由于程序是通过加载器来加载的,必须要找到 _start名字的函数,
//因此_start必须定义成全局的,以便存在于编译后的全局符合表中,供属其它程序【如加载器】寻找到。
_start:

//设置内存 sp 栈 nand启动时,nand大小是4K,所以把栈设置在内存的顶部
    ldr sp, =4096
    
//nor 启动
    ldr sp, = 0x40000000+4096

//调用Main 跳转过去执行Main 并把返回的地址保存到lr里面
    bl main

// 死循环 b常用于不返回的跳转
halt:
    b halt


拓展:栈为什么是从高地址往低地址分配内存的?

示例交替点灯
int led_on(int which)
{
    unsigned int* pGPFCON = 0x56000050;
    unsigned int* pGPFDAT = 0x56000054;

    if(which==4)
    {
      *pGPFCON = 0x100;
    }
    else if(which==5)
    {
      *pGPFCON = 0x400;
    }

   *pGPFDAT=0;
}

void delay(int d)
{
    while(d--);
}

int main()
{


    return 0;
}

汇编文件


//.text 指定了后续编译出来的内容放在代码段
.text
.global _start

//_start是一个函数的起始地址,也是编译、链接后程序的起始地址。由于程序是通过加载器来加载的,必须要找到 _start名字的函数,
//因此_start必须定义成全局的,以便存在于编译后的全局符合表中,供属其它程序【如加载器】寻找到。
_start:

//设置内存 sp 栈 nand启动时,nand大小是4K,所以把栈设置在内存的顶部
    ldr sp, =4096
    
//nor 启动
    ldr sp, = 0x40000000+4096

//传参
    mov r0,#4 
//调用Main 跳转过去执行Main 并把返回的地址保存到lr里面
    bl led_on

    ldr r0,=100000
    bl delay

    mov r0,#5  
    bl led_on

// 死循环 b常用于不返回的跳转
halt:
    b halt

按键控制led示例
#include "s3c2440_soc.h"
void delay(int d)
{
    while(d--);
}

int main()
{

    int val=1;

    GPFCON &=~((3<<8) | (3<<10) |(3<<12));
    GPFCON |=((1<<8) | (1<<10) |(1<<12));
    
    //配置3个按键引脚为输入 GPF0,2,GPG3
    GPFCON &=~((3<<0) | (3<<4));
    GPGCON &=~(3<<6);

   
   
    while(1)
    {
      int i = 0;
      for (i = 0; i < 3; i++)
      {
        int keyOn[3] = {~GPFDAT&(0x1<<0),~GPFDAT&(0x1<<2),~GPGDAT&(0x1<<3)};
        if(keyOn[i])
        {
          GPFDAT&=~((i==0?1:i*2)<<4);
        }
        else
        {
          GPFDAT|=(7<<4);
        }
      }
      // if(~GPFDAT&(0x1<<0))
      // {
      //   GPFDAT&=~(1<<4);
      // }
      // else if(~GPFDAT&(0x1<<2))
      // {
      //   GPFDAT&=~(2<<4);  
      // }
      // else if(~GPGDAT&(0x1<<3))
      // {
      //   GPFDAT&=~(4<<4);
      // }
      // else
      // {
      //   GPFDAT|=(7<<4);
      // }

      
    }

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ou.cs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值