回顾:
面试题:谈谈对嵌入式的理解
1.明确:如何向下位机部署软件
1.1.明确:嵌入式开发模式,画出简要的示意图
1.2.看图明确接下来要完成的内容
上位机搭建开发环境
各种安装
搞定下位机
掌控硬件信息
粗看
三大件
外围
细看
原理图
手册
部署uboot
TF卡
掌握uboot常见的命令
print
serverip
ipaddr
setenv
saveenv
ping
tftp
update_mmc
go
2.面试题:谈谈对ARM处理器裸板编程的认识
2.1.上来直接提出案例,阐述ARM裸板编程的流程。目前:先以LED开关灯为例,进行阐述解释说明 。
2.2.明确相关概念 。
计算机硬件层面包括:CPU和外设 。
CPU一天到晚都是在跟外设进行数据通信。CPU访问外设都是以地址指针的形式访问
2.3.紧接着分析用户需求。
目前需求就是:开关灯
2.4.掌控LED的硬件相关信息 。
粗看:
看位置
硬件工程师
细看:
1.LED的原理图:
通过原理图得到:
1.1.务必画出一个简要的CPU访问操作LED的示意图。
1.2.看图阐述CPU核访问LED的流程
CPU核以软件(地址指针)形式访问GPIO控制器内部的一大堆寄存器,本质就是给GPIO控制器发送控制操作命令 。
GPIO控制器根据命令由硬件上自动操作对应的引脚(管脚)(无非就是高低电平)。
引脚的状态(高/低电平)最终影响LED的状态(亮/灭)。
1.3.结论:
接下来只要掌握GPIO控制器以及内部的一大堆寄存器的操作即可完成最终的LED的操作。
2.问:GPIO控制器以及内部一大堆的寄存器如何访问呢?
答:由于GPIO控制器集成在S5P6818处理器的内部。要想知道GPIO控制器如何访问操作,只需看S5P6818处理器的芯片手册(说明书)。
3.打开S5P6818处理器的芯片手册:
resource/Datasheet/SEC_S5P6818X_Users_Manual_preliminary_Ver_0.00.pdf
切记:尤其是处理器的芯片手册,只看跟目前研究的内容(GPIO控制器)相关的章节。
结论:P739第16章讲解GPIO控制器
3.1.看GPIO控制器相关的说明,罗列重要的跟将来软件操作相关的内容:
1.S5P6818包含160个GPIO引脚
分五组:A/B/C/D/E
每一组:32个GPIO
LED1对应的引脚为:GPIOC12(C组的第12个引脚)
2.GPIO的输出操作,P742
GPIO复用功能选择寄存器:指定GPIO引脚的复用功能 。
GPIO输出使能寄存器:指定GPIO的输入或者输出功能 。
GPIO输出寄存器:指定GPIO的输出值(0:低电平;1:高电平) 。
结论:开关灯操作:
1.首先配置GPIO复用功能选择寄存器,配置为GPIOC12这种功能 。
2.然后配置GPIOC12为输出功能,配置GPIO输出使能寄存器 。
3.最后:
开灯:配置GPIO输出寄存器为0
关灯:配置GPIO输出寄存器为1
4.问:以上三个寄存器如何访问操作呢?
答:寄存器也是硬件,它同样是外设!
3.相关寄存器的特性(P745):
GPIOCOUT:输出寄存器
基地址:0xC001C000
BIT[12]=0:输出低电平,开灯
*(volatile unsigned long *)0xC001C000 &= ~(1 << 12);
美化:
#define GPIOCOUT *(volatile unsigned long *)0xC001C000
GPIOCOUT &= ~(1 << 12);
BIT[12]=1:输出高电平,关灯
GPIOCOUT |= (1 << 12);
GPIOCOUTENB:输出使能寄存器
基地址:0xC001C004
BIT[12]=1:配置为输出功能
#define GPIOCOUT (*(volatile unsigned long *)0xC001C004)
GPIOCOUTENB |= (1 << 12);
GPIOCALTFN0:复用功能选择寄存器0(P757)
基地址:0xC001C020
BIT[25:24]=01 //注意在P68
GPIOCALTFN0 &= ~(3 << 24);
GPIOCALTFN0 |= (1 << 24);
至此:硬件信息的掌控完毕!
2.5.一旦硬件信息掌控完毕,编写LED的操作软件
1.明确:嵌入式软件开发分为两类
裸板程序:
下位机不会运行操作系统(linux/windows/vxworks),运行的程序就一个,单任务运行 。
标准C的库函数一律不能使用,例如:printf。
基于操作系统程序:后续慢慢讲解
结论:首先要明确LED的程序属于哪类。此时此刻,此LED程序为裸板程序。
2.明确:裸板程序的编程框架
开发语言:一般C语言或者汇编 。
“硬件初始化”:硬件外设正式工作之前,将其工作参数,工作状态配置完毕 。
框架:
//裸板程序的入口函数名随便定义,不一定非要是main
void xxx(void) {
//硬件初始化函数
xxx_init();
/*
led_init(); //初始化LED
uart_init(); //初始化串口
net_init(); //初始化网络
mmc_init(); //初始化EMMC
bt_init(); //初始化蓝牙
lcd_init(); //初始化LCD显示屏
...
*/
while(1) {
//做业务:根据用户需求,操作硬件外设
/*
led_on();
delay();
led_off();
delay();
*/
}
}
3.编写LED裸板程序,实现交替闪烁
实施步骤:
上位机执行:
sudo chown tarena /opt -R
sudo chgrp tarena /opt -R
mkdir /opt/arm/day03/1.0 -p
cd /opt/arm/day03/1.0
vim led.h //声明
vim led.c //定义
注意:vim的使用
1.vim多屏显示(命令行模式下)
vs 文件名 //左右分屏
sp 文件名 //上下分屏
切换快捷键:ctrl + ww
复制粘贴:
shift+v:行选
ctrl+v:列选
4.问:如何编译LED的程序呢?
答:明确:势必不能用gcc编译(针对于X86架构),所以:务必先在上位机部署添加交叉编译器。
上位机添加ARM架构交叉编译器的流程:
4.1.获取交叉编译器
resource/编译器/arm-cortex_a9-eabi-4.7-eglibc-2.18.tar
拷贝到linux和windows共享目录中
cp arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz /opt/
4.2.上位机添加
cd /opt/
tar -xvf arm-cortex_a9-eabi-4.7-eglibc-2.18.tar.gz
得到新目录:arm-cortex_a9-eabi-4.7-eglibc-2.18
mv arm-cortex_a9-eabi-4.7-eglibc-2.18 toolchains
ls /opt/toolchains/bin/
arm-cortex_a9-linux-gnueabi-gcc //ARM架构的gcc编译器
sudo vim /etc/environment
在PATH中添加/opt/toolchains/bin,例如:
PATH=/opt/toolchains/bin:...
保存退出
重启上位机linux系统
4.3.测试交叉编译器
上位机执行:
arm-cortex_a9-linux-gnueabi-gcc -v //查看交叉编译器的版本
4.4.交叉编译LED程序
cd /opt/arm/day03/1.0
arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c
说明:
-nostdlib:告诉编译器,此程序不使用标准C库
-c:只编译不链接
arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib -Ttext=0x48000000 -o led.elf led.o
说明:
arm...ld:链接器
-nostartfiles:告诉链接器,此代码无需启动文件
-Ttext=0x48000000:告诉链接器,代码段的起始地址为0x48000000(下位机的内存地址)
-o led.elf:链接生成ELF格式的可执行文件
切记:此时此刻led.elf不能在没有操作系统的环境中运行
arm-cortex_a9-linux-gnueabi-objcopy -O binary led.elf led.bin
说明:利用arm...objcopy工具将ELF格式的可执行文件
再次获取到其中的真正的二进制文件信息
led.elf:橘子(带皮)
led.bin:果肉
objcopy:去皮工具
cp led.bin /tftpboot //拷贝到下载目录
绝招:反汇编
arm-cortex_a9-linux-gnueabi-objdump -D led.elf > led.dis
led.dis:反汇编文件
vim led.dis //只需关注一个内容即可
<0x48000000>:led_test
如果看到以上信息说明:led_test函数的地址为0x48000000。说明程序的编译是对的!
5.下位机测试
重启下位机,进入uboot命令行模式,执行:
ping 192.168.1.8
tftp 48000000 led.bin
go 48000000
6.验证入口地址特性
cd /opt/arm/day03/1.0
vim led.c //将delay函数的定义放在led_test函数定义的前面
保存退出
①.arm-cortex_a9-linux-gnueabi-gcc -nostdlib -c -o led.o led.c
②.arm-cortex_a9-linux-gnueabi-ld -nostartfiles -nostdlib
-Ttext=0x48000000 -o led.elf led.o
③.arm-cortex_a9-linux-objcopy -O binary led.elf led.bin
④.arm-cortex_a9-linux-objdump -D led.elf > led.dis
⑤.vim led.dis
查看0x48000000这个地址对应的函数是:led_test?delay?
切记:编译器链接是从文件的头到尾进行链接(从上到下)
作业:实现一个跑马灯
LED1亮->LED2亮->LED3亮->LED4亮->LED1灭->...
作业:实现蜂鸣器的操作
************************************************
2.面试题:谈谈对UART的理解
2.1.首先要交待计算机中常见的几种硬件通信方式
硬件通信接口
明确:计算中包括:CPU和外设。
CPU一天到晚和外设进行数据交互通信
问:CPU和外设数据通信的方式方法有哪些呢?
答:计算机中CPU和外设进行硬件通信的方式
通信接口有以下几种:
1.GPIO通信方式,例如:LED灯,蜂鸣器等 。
2.UART串口通信方式,例如:GPS,GPRS,BT等 。
3.I2C总线通信方式,例如:重力传感器,三轴加速度传感器,触摸屏等 。
4.SPI总线通信方式,例如:Norflash闪存,触摸屏等 。
5.1-Wire(一线式)总线通信方式,例如:温度传感器,EEPROM存储器等 。
所以:这里开始讲解UART,如果可以,也可以阐述一下I2C总线 。
2.2.UART串口的定义
定义就九个字:通用串行异步收发器 。
进一步的对定义进行解释说明:
“通用”:UART串口应用非常广泛
”串行“:CPU和外设进行数据通信时,只需一根信号线即可 。
此信号线又称数据线,也就是说CPU和外设进行数据通信时,是一个bit位一个bit位的传输:
切记:UART数据传输从低位开始!
例如:CPU向BT发送一个0x95这个数据
数据线的操作如下:
高->低->高->低->高->低->低->高
1 0 1 0 1 0 0 1
顺便侃侃串行的死对头:并行
“并行”:CPU和外设数据传输时,需要多根信号线(数据线),8/16/32根,那么也就是一次数据传输可以同时传输8bit/16bit/32bit
顺便再看看并行和串行的对比:
传输速度:一般来说并行快于串行 。
传输距离:串行更适合远距离传输 。
抗干扰性:串行抗干扰性更好。
“异步”:首先要明确:CPU的数据处理速度要远远快于外设。
所以CPU和外设进行数据传输时,务必要考虑数据同步。
“数据同步”:CPU向设备发送数据以后,要确保外设能够正常的将数据接收到,接收完整!
计算机中数据同步的方法有两种:异步和同步 。
异步定义:双方在数据正式传输时,只要保证数据同步即可 。传输前和传输以后,无需考虑数据同步。异步具体如何实现数据同步关键在于协议中!这里待会儿讲讲UART的协议,即可了解何为异步!
同步定义:CPU和外设进行数据传输时,如果采用同步方式保证数据同步,那么CPU和外设之间不仅仅有数据线,还需要一根时钟控制信号线,此信号线就是用来实现双方的数据同步,此时此刻,务必画图举例子说明即可!
参见:uart2.bmp
以CPU向外设LM77发送1和0为例:
CPU在时钟线为电平时,将数据1放到数据线上(拉高数据线)
设备就会在同周期的高电平从数据线上读取数据
结论:低放高取
收发器:接收和发送数据的硬件单元。
CPU给外设发送数据:
CPU就是发送器,外设就是接收器。
外设给CPU发送数据:
外设是发送器,CPU是接收器 。