树莓派——学习记录

目录

树莓派登录

更新VIM

wiringPi库

交叉编译 

交叉编译工具链的安装和使用

操作系统启动过程

树莓派Linux源码目录树分析

树莓派Linux源码配置          

树莓派Linux内核编译

文件系统

内核结构 

驱动代码编译

编写引脚驱动代码 


 


树莓派登录

一、串口

      默认情况,树莓派的串口和蓝牙连接。

想办法断开蓝牙连接,把串口用来数据通信

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");

 

 

 

 

 

 

 

 

 

 

 

 

                               

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值