目录
树莓派登录
一、串口
默认情况,树莓派的串口和蓝牙连接。
想办法断开蓝牙连接,把串口用来数据通信
https://blog.csdn.net/syjsxxjy/article/details/80774262
dtoverlay=pi3-miniuart-bt
dwc_otg.lpm_enable=0 console=tty1 console=serial0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
解除串口登录,用作串口通信:
1. /*修改cmdline.txt文件*/
sudo vim /boot/cmdline.txt
dwc_otg.lpm_enable=0 console=tty1 console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
删除console=ttyAMA0,115200
2. /*修改inittab文件*/
sudo vim /etc/inittab
注释最后一行 #T0:23.. .. 115200 vt100
3.sudo reboot
二、上网
2.1
进入文档
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
添加wifi 名称密码
network={
ssid="*****"
psk="****"
}
配置完毕,树莓派会自动连接到配置的WIFI网络,如果没有,使用
sudo wpa_cli reconfigure
命令启动连接。
pi@raspberrypi:~ $ sudo wpa_cli reconfigure
Selected interface 'wlan0'
OK
——tips————————————————————————————————————————————————————————————————————
使用以下命令扫描树莓派周围的无线网络。
sudo iwlist wlan0 scan
也可手动连接wifi
sudo raspi-config
2.2固定树莓派IP地址
sudo nano /etc/rc.local
三、
3.1 shh登录树莓派
要先在树莓派打开ssh功能
sudo raspi-config
3.2 图形界面登录树莓派()
先安装xrdp
sudo apt-get install xrdp
用windows自带的“远程桌面连接”
更新VIM
默认的源是国外的,apt-get 安装的时候失败,需要更新成国内能用源
https://blog.csdn.net/Liu_959185/article/details/86636906
1.
deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi
2.
sudo apt-get update更新源
3.
sudo apt-get install vim 安装新版vim
wiringPi库
https://cnblogs.com/lulipro/p/5992172.html
/*继电器使用*/
#include <stdio.h>
#include <wiringPi.h>
#define switcher 7
int main()
{
int cmd;
//硬件初始化函数
if(wiringPiSetup() ==-1){//初始化树莓派引脚,使用的是wiringPi引脚编号表。引脚编号为0-16
printf("初始化接口出错\n");
return -1;
}
pinMode(switcher,OUTPUT);//配置引脚的IO模式
digitalWrite(switcher,HIGH);//输出高电平信号,开始就处于断开
while(1){
printf("请输入0/1,0-关灯,1-开灯\n");
scanf("%d",&cmd);
if(cmd == 1){
digitalWrite(switcher,LOW);//输出低电平信号
printf("cmd:1,开!\n");
}else if(cmd == 2){
digitalWrite(switcher,HIGH);//输出高电平信号
printf("cmd:0,关!\n");
}else{
printf("输入错误\n")
交叉编译
1. 交叉编译是什么?
编译:是在一个平台上生成在该平台上的可执行代码
交叉编译 是在一个平台上生成另一个平台上的可执行代码。
(1)在windows上面编写C51代码,并编译成可执行代码,如xx.hex,是在c51上面运行,不是在windows上面运行。 C51 交叉编译的发生在keil(集成环境上面)
(2)在ubuntu上面编写树莓派的代码,并编译成可执行代码,如a.out,是在树莓派上面运行,不是在ubuntu linux上面运行
2.为什么要交叉编译?
平台上不允许或不能够安装我们所需要的编译器比如C51
(1).因为目的平台上的资源贫乏,无法运行我们所需要编译器
(2).但是树莓派也需要交叉编译
树莓派有时又是因为目的平台还没有建立,连操作系统都没有,根本谈不上运行什么编译器。况且操作系统也是代码,也要编译!
平台运行需要两样至少东西:bootloader(启动引导代码)以及操作系统核心
宿主机(host) :编辑和编译程序的平台,一般是基于X86的PC机,通常也被称为主机。
目标机(target):用户开发的系统,通常都是非X86平台。host编译得到的可执行代码在target上运行。
3.交叉编译需要用到什么工具?
交叉编译器、交叉编译工具链
交叉编译工具链的安装和使用
(1)下载 https://github.com/raspberrypi/
(2)将压缩包放到工作目录
(3)解压
unzip tools-master.zip
先进入编译器所在文件
cd /home/user/pi/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
(5)使用
- 临时有效,配置环境变量:
pwd获得现在路径即交叉编译器路径;
echo $PATH 获得当前环境变量的值;
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/FriendlyARM/toolschain/4.5.1/bin
export PATH=环境变量+当前路径
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/CLC/lessonPI/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
- 永久有效,配置命令终端文件
修改工作目录下的.bashrc, 配置命令终端的隐藏文件。
vi /home/CLC/.bashrc
在文件最后一行加入:
export
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/CLC/lessonPI/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
加载配置文件,马上生效配置。
source /home/CLC/.bashrc
(6)如何把编译生成的可执行文件下载到开发板:
scp clientInPi pi@192.168.43.30:/home/pi
指令 文件名 开发板用户名@开发板地址:开发板的绝对路径
【软链接】
参考文章: https://www.cnblogs.com/zhangna1998517/p/11347364.html
概念:
1. 软链接文件有类似于Windows的快捷方式。
2. 在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。
3. 你选定的位置上生成一个文件的镜像,不会占用磁盘空间
如何生成:
ln -s libwiringPi.so.2.50 libwiringPi.so
指令 参数 要被链接的文件 软链接文件名字
【 硬链接】
ln libwiringPi.so.2.50 libwiringPi.so
它会在你选定的位置上生成一个和源文件大小相同的文件
ubuntu下交叉编译带wiringPi库
arm-linux-gnueabihf-gcc jidianqi.c -I ./WiringPi/wiringPi -L. -lwiringPi -o jidianqi
交叉编译器 指定路径下找头文件.h 在当前路径找库
操作系统启动过程
C51,STM32(裸机)》》》》》》C直接操控底层寄存器实现相关业务。 业务流程型的裸机代码
X86,Intel windows
启动过程: 电源 -》 BIOS -》windows内核-》C,D盘-》 程序启动(QQ)
嵌入式产品: 树莓派,mini2440, mini6410,nanopi,海思,RK(瑞芯微)------人脸识别打卡器,智能家居主控。。。
启动过程: 电源-》BootLoader(引导操作系统启动)-》Linux内核-》文件系统(根据功能性来组织文件夹,带访问权限)-》KTV点歌机,
安卓
启动过程: 电源-》 fastBoot/Bootloader/-》linux内核-》文件系统-》虚拟机-》HOME应用程序-》点某图标打开某APP
BootLoader: 一阶段 让CPU 跟内存,FLASH, 串口,IIC,IIS, 数据段,打交道,驱动这些设备(汇编和C结合)
二阶段: 引导Linux内核启动 (纯C)
树莓派Linux源码目录树分析
https://www.cnblogs.com/senior-engineer/p/4929703.html
大约1.3w个C文件 1100w行代码
Linux是开源,免费,LInux开源社区工作者共同维护,爱好
Linux是一个开源的,支持多架构多平台代码 =非常牛逼
可移植性非常高
但是Linux内核编译出来一般就几M. 4M
因为支持多平台,多架构,所以编译之前要配置,配置成适合的目标平台来用
ARM
海思 友善之臂 RK 树莓派 nanoPi
X86
PowerPC
MIPS
树莓派Linux源码配置
驱动代码的编写
驱动代码的编译需要一个提前编译好的内核,编译内核就必须配置!
配置的最终目标会生成 .config文件,该文件指导Makefile去把有用东西组织成内核
https://blog.csdn.net/nicekwell/article/details/78482833
厂家会配linux内核源码,比如说买了树莓派,树莓派linux内核源码
第一种方式:
cp 厂家.config .config
第二种方式:
make menuconfig 一项项配置,通常是基于厂家的config来配置
第三种方式:
完全自己来
如何配置树莓派的Linux内核
驱动两种加载方式:
1. * 代表编译进内核 zImage包含了驱动
2. M 代表模块方式生成驱动文件xxx.ko 系统启动后,通过命令inmosd xxx.ko 加载
内核配置:(在内核源码树路径下)
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make bcm2709_defconfig
指定ARM架构 指定编译器 根据树莓派的版本 核心指令
生成了.config文件
树莓派Linux内核编译
1.编译:
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 zImage modules dtbs
-j 指定用多少电脑资源进行编译
zImage生成内核镜像
modules要生成驱动模块
dtbs生成配置文件
2.编译成功后,看到源码树目录多了vmlinux,失败则无此文件
成功后,目标zImage镜像arch/arm/boot底下
3.打包zImage成树莓派可用的xxx.img
./scripts/mkknlimg arch/arm/boot/zImage ./kernel_new.img
4.数据拷贝
4.1
mkdir data1 data2创建两个文件夹
挂载U盘
sudo mount /dev/sdb1 data1 一个fat分区,是boot相关的内容,kernel的img
sudo mount /dev/sdb2 data2 一个是ext4分区,也就是系统的根目录分区。
4.2
安装modules, 设备驱动文件: hdmi usb wifi io ...
sudo ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make INSTALL_MOD_PATH=[ext4] modules_install
4.3
安装更新 kernel.img 文件,注意镜像名字是kernel7.img
先备份
cd /home/pi/data1
cp kernel7.img kernel7old.img
再把编译新生成的拷贝到data1,起名kernel7.img
cp kernel_new.img /home/chenlc/data1/kernel7.img
拷贝配置文件
cp arch/arm/boot/dts/.*dtb* /home/pi/data1
cp arch/arm/boot/dts/overlays/.*dtb* /home/pi/data1/overlays/
cp arch/arm/boot/dts/overlays/README /home/pi/data1/overlays/
文件系统
1. 什么是文件系统?
常规认知: 根目录
文件系统是操作系统用于明确存储设备组织文件的方法。
以上说的方法:就是文件管理系统(程序),简称文件系统
2. 文件系统(文件管理系统的方法)的种类有哪些?
FAT VFAT NTFS EXT1/2/3/4 HFS ....
树莓派查看文件系统的命令: df -T
vfat : boot(bootloader, kernel)
ext4 : 根目录
tmpfs : 内存文件系统
3. 什么是分区?
windows: 随意(面向普通用户PC),同一目录即同一分区
C盘、D盘
Linux: 按照功能来分区,每个分区严格存放文件
嵌入式系统可以分为4个区,分别是
bootloader、 启动代码
para、 启动代码向内核传递参数的位置
kernel、 内核分区
根分区等 文件系统结构
所以同一目录的文件夹可能来自不同分区
2. 什么是文件系统目录结构?
https://cnblogs.com/yihr/p/12954340.html
在Linux系统中,目录被组织成一个:单根倒置树结构,文件系统从根目录开始,用/来表示。
cd ~工作目录,pwd /home/pi
cd / 顶点目录,pwd /
/bin 指令,/boot 开启启动,/dev 设备驱动,/home 普通用户,/mnt 提供挂载,例如共享文件夹
2. 什么虚拟文件系统Virtual File System ?
vfs就是对各种文件系统的一个抽象,它为各种文件系统提供了一个通用的接口,
3. 虚拟文件系统有什么作用?
封装了打开不同文件系统的方法, 用一个接口访问所用文件。
简化应用程序员的开发
为用户上层应用开发,提供文件系统的接口,不管是什么文件类型,不管文件是磁盘还是设备,都只用open read write统一操作
内核结构
Linux将内核的功能接口制作成系统调用,方便用户使用内核。
c库提供应用程序支配内核干活的接口,但不是直接的。例如应用程序调用c库的open ,c库通过系统调用接口里面的sys_open操作底层,如访问不同文件系统(ext4、tmpfs、vfat),进程间通信。
shell是一个应用程序,是提供用户去操作内核的工具。
驱动认知
用户态通过系统调用接口再通过虚拟文件系统(可让上层用户直接打开不同类型的文件系统)来控制内核去调用驱动。 用户空间(open)-》系统调用接口(sys_call)-》虚拟文件系统 -》驱动程序 -》硬件
open函数会触发一个系统异常产生软中断,然后进入到内核态调用sys_call,sys_call调用vfs的sys_open,sys_open根据文件名找到设备号,通过设备号在驱动链表找到驱动,驱动调用open,最后返回fd给上层。
内核用链表存放所有驱动文件,管理驱动
添加:编写完驱动,加载到内核驱动链表,成为一个节点;内核加载了驱动,并且在dev目录下生成设备供上层调用
编写驱动包括:1.设备名字;2.设备号;3.设备驱动函数(操作寄存器驱动io口)
查找: 用户空间(open)-》系统调用接口(sys_call)-》虚拟文件系统 -》在内核通过设备号找到驱动程序
驱动信息:1.文件名;2.设备号(主、次);
驱动代码编译
编译:
1.写好的驱动代码如pin4dirver2.c放到内核源码 /linux-rpi-4.14.y/drivers/char
2.当前路径vi Makefile 修改文件,加入“obj-m += pin4driver2.o”,
告诉编译器要编译该文件。m表示以模块的方式编译。
3.编译模块,ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make modules
4.发送到树莓派,scp drivers/char/pin4driver2.ko pi@192.168.3.157:/home/pi 。 md5sum 看每个文件的编码,检查文件是否一致。
5.加载内核驱动:sudo insmod pin4driver2.ko;作用:把结构体加到驱动链表去,并且在/dev/下生成pin4。
查看内核驱动模块:lsmod;
卸载驱动模块:sudo rmmod pin4driver2。
验证:
6.指令查看:ls /dev/pin4 -l
crw------- 1 root root 231, 0 Feb 24 08:27 /dev/pin4
7.sudo chmod 666 /dev/pin4 给所有用户可读可写权限。
8.然后写个上层的demo测试,例如:open("/dev/pin4",O_RDWR)。运行后通过dmesg可以看到内核态的printk,说明写的驱动被调用。
/*驱动框架*/
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno; //设备号
static int major =231; //主设备号
static int minor =0; //次设备号
static char *module_name="pin4"; //模块名
//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n"); //内核的打印函数和printf类似
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
printk("pin4_write\n"); //内核的打印函数和printf类似
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
};
int __init pin4_drv_init(void)
{
int ret;
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
return 0;
}
void __exit pin4_drv_exit(void)
{
device_destroy(pin4_class_dev,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin4_drv_init); //入口
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");
编写引脚驱动代码
根据芯片手册配寄存器。
1.声明几个要用的寄存器。如:volatile unsigned int* GPSET0 = NULL; //输出设置(置1)寄存器,选择第一组,引脚0-31
2.在加载驱动init函数里把IO口寄存器映射成普通内存单元供访问,物理地址转换成虚拟地址;卸载驱动时也要解除映射。
3.配置寄存器。
/*实现引脚驱动*/
#include <linux/fs.h> //file_operations声明
#include <linux/module.h> //module_init module_exit声明
#include <linux/init.h> //__init __exit 宏定义声明
#include <linux/device.h> //class devise声明
#include <linux/uaccess.h> //copy_from_user 的头文件
#include <linux/types.h> //设备号 dev_t 类型声明
#include <asm/io.h> //ioremap iounmap的头文件
static struct class *pin4_class;
static struct device *pin4_class_dev;
static dev_t devno; //设备号
static int major =231; //主设备号
static int minor =0; //次设备号
static char *module_name="pin4"; //模块名
/**/
volatile unsigned int* GPFSEL0 = NULL;//功能选择寄存器(输入或输出模式),选择第一组,引脚0-9
volatile unsigned int* GPSET0 = NULL; //输出设置(置1)寄存器,选择第一组,引脚0-31
volatile unsigned int* GPCLR0 = NULL; //输出清零寄存器,选择第一组,引脚0-31
//led_open函数
static int pin4_open(struct inode *inode,struct file *file)
{
printk("pin4_open\n"); //内核的打印函数和printf类似
//选择引脚4为输出模式,14--12 = 001
*GPFSEL0 &= ~(0x3 << 13);//bit14-13 = 00 “与1不变,与0为0”
*GPFSEL0 |= (0x1 <<12); //bit12 = 1
*GPSET0 |= (0x1 << 4);//初始化引脚4为输出高电平,bit4为1,
return 0;
}
//led_write函数
static ssize_t pin4_write(struct file *file,const char __user *buf,size_t count, loff_t *ppos)
{
char userCmd[128] = {'\0'};
printk("pin4_write\n"); //内核的打印函数和printf类似
//获取上层write函数的值
copy_from_user(userCmd,buf,count);
//根据值操作io口
if(userCmd[0] == '1'){
printk("set 1\n");
*GPSET0 |= (0x1 << 4);//bit4为1就置1
}else if(userCmd[0] == '0'){
printk("set 0");
*GPCLR0 |= (0x1 << 4);//bit4为1就清零
}else{
printk("wrong cmd\n");
}
return 0;
}
static struct file_operations pin4_fops = {
.owner = THIS_MODULE,
.open = pin4_open,
.write = pin4_write,
};
int __init pin4_drv_init(void)
{
int ret;
printk("insmod driver pin4 success\n");
devno = MKDEV(major,minor); //创建设备号
ret = register_chrdev(major, module_name,&pin4_fops); //注册驱动 告诉内核,把这个驱动加入到内核驱动的链表中
pin4_class=class_create(THIS_MODULE,"myfirstdemo");
pin4_class_dev =device_create(pin4_class,NULL,devno,NULL,module_name); //创建设备文件
//初始化寄存器地址
GPFSEL0 =(volatile unsigned int*)ioremap(0x3f200000,4);//物理地址转换成虚拟地址,io口寄存器映射成普通内存单元进行访问
GPSET0 = (volatile unsigned int*)ioremap(0x3f20001c,4);
GPCLR0 = (volatile unsigned int*)ioremap(0x3f200028,4);
return 0;
}
void __exit pin4_drv_exit(void)
{
iounmap(GPFSEL0);//解除地址映射
iounmap(GPSET0);
iounmap(GPCLR0);
device_destroy(pin4_class,devno);
class_destroy(pin4_class);
unregister_chrdev(major, module_name); //卸载驱动
}
module_init(pin4_drv_init); //入口
module_exit(pin4_drv_exit);
MODULE_LICENSE("GPL v2");