day1复习

1、学习完驱动  

2、复习标准IO和文件IO,知道原理,函数接口灵活使用

      标准io:有fopen,fclose等,是一种c库函数,在进行系统调用前,先操作缓冲区,操作对象是文件流指针(FILE),通过库函数提供的接口进行操作。优点是可移植性好,缺点效率较低

文件io:有open,close等函数,没有缓冲区,采用系统调用的方式可以直接操作底层,与内核进行交互,操作对象是文件描述符(fd)。优点效率高,缺点则是可移植性差。

3、复习进程和线程,知道原理,函数接口灵活使用

      进程:进程是执行中的程序的实例。每个进程都有一个唯一的进程标识符(PID)以及一组资源(如内存、文件描述符等)。进程是操作系统中的最小执行单位,操作系统通过管理和调度进程来实现多任务处理

pid_t fork(void);

      线程:线程是进程内的一个执行单元,是操作系统中最小的调度单位。线程共享进程的资源和内存,但每个线程拥有自己的栈空间和寄存器状态。

      int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 

                   void *(*start_routine) (void *), void *arg);

  • 成功返回0,失败返回错误码
  • thread表示线程对象
  • attr表示线程属性,NULL代表默认属性
  • routine表示线程执行的函数(为指针函数
  • arg表示传递给routine的参数 ,参数是void * ,需要注意传递参数格式

4、复习System V通信方式,知道原理,函数接口灵活使用

  1. 消息队列msgget,msgsnd,msgrcv,msgctl(各种控制,如删除)
  2. 共享内存
  3. 信号量;其中消息队列和共享内存都是来完成进程通信的,而信号量主要是保证同步和互斥的;

在UNIX和类UNIX系统中,信号量是通过信号量集(semaphore set)来管理的,这些信号量集位于内核中,并且可以通过特定的键(key)来访问。当两个进程想要共享同一个信号量时,它们需要使用相同的键来访问同一个信号量集。这样,两个进程就能对同一个信号量进行操作,从而实现同步。

共享内存的通信原理:
在物理内存开辟一份共享内存的空间,然后把这份空间映射到需要进行相互通信的虚拟地址空间中;这样使得不同的进程看到了同一份资源,这样就可以通过共享内存进行通信了;

1. 创建共享内存;  int shmget 和key_t ftok

在我们调用shmget函数之前,必须调用ftok来帮我们生成一个key,该key就是给shmget函数第一个参数使用了,表示表示该共享内存的唯一标识,这个key在内核中,会在共享内存的结构体,设置给共享内存的ID;


2. 挂接共享内存与进程之间的关联;shmat


3. 完成通信逻辑代码;
4. 去挂接共享内存与进程之间的关联;
shmdt


5. 释放共享内存;shmctl(shmid,IPC_RMID,NULL);

示例:

5、复习线程的同步和互斥(特别是条件变量),知道原理,函数接口

互斥:

临界资源:一次只允许一个任务(进程、线程)访问的共享资源

临界区:访问临界资源的代码

互斥机制:mutex互斥锁,任务访问临界资源前申请锁,访问完后释放锁

互斥锁初始化:pthread_mutex_init()函数(动态初始化)

#include <pthread.h>

int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t * attr);

互斥锁初始化(动态/静态)---->锁的申请和释放--->锁的销毁

读写锁:提高线程执行效率

同步:

条件变量:线程条件变量是为了解决生产者与消费者问题,是线程同步的一种手段;

必要性:为了实现等待某个资源,让线程进行休眠,从而提高运行效率;

举例taxi:

6、能保证给开发板移植操作系统

1. 环境准备

1.1 交叉编译环境搭建

  • 安装交叉编译器:根据目标开发板的处理器架构(如ARM、MIPS等),安装相应的交叉编译工具链。这些工具链允许在宿主机(如PC)上编译出可在目标板上运行的代码。
  • 设置环境变量:确保交叉编译器的路径被添加到系统的PATH环境变量中,以便在命令行中直接调用。

1.2 网络环境配置

  • TFTP服务器:搭建TFTP服务器,用于通过网络向开发板传输内核镜像、设备树等文件。
  • NFS服务器:如果计划通过网络挂载根文件系统,还需要搭建NFS服务器。

2. Bootloader移植:开源的引导加载程序

  • 获取Bootloader源码:常见的Bootloader有U-Boot、GRUB等,根据开发板需求选择合适的Bootloader并获取其源码。
  • 修改与编译:根据开发板的硬件配置修改Bootloader源码中的相关配置(如内存地址、串口配置等),并进行编译。
  • 烧写到开发板:将编译好的Bootloader烧写到开发板的指定存储介质(如SPI Flash、SD卡等)中。

3. Linux内核移植

3.1 获取内核源码

  • 从官方网站或版本控制系统(如Git)中获取Linux内核源码。

3.2 配置内核

  • 修改Makefile:设置ARCH和CROSS_COMPILE变量,以指定目标架构和交叉编译器。
  • 配置内核选项:使用make menuconfig、make xconfig等工具进行内核配置,选择与硬件平台相关的选项,如驱动支持、文件系统类型等。

3.3 编译内核

  • 使用交叉编译器编译内核源码,生成内核镜像(如zImage、uImage等)。

3.4 烧写内核

  • 将编译好的内核镜像通过TFTP服务器、串口或SD卡等方式烧写到开发板中。

4. 设备树(Device Tree)制作与移植

  • 创建或修改设备树文件:根据开发板的硬件配置编写或修改设备树文件(.dts),描述硬件信息。
  • 编译设备树:使用DTC(Device Tree Compiler)工具将.dts文件编译成.dtb格式的设备树二进制文件。
  • 烧写设备树:将编译好的设备树文件烧写到开发板中。

5. 根文件系统制作与移植

  • 创建根文件系统:可以使用BusyBox等工具创建一个简单的根文件系统,也可以从现有发行版中提取。
  • 配置根文件系统:根据需要配置根文件系统中的文件、目录和权限等。
  • 烧写或挂载根文件系统:将根文件系统烧写到开发板的存储介质中,或者通过网络(如NFS)挂载到开发板上。

6. 测试与调试

  • 启动开发板:按照Bootloader的启动流程启动开发板,观察是否能正确加载内核、设备树和根文件系统。
  • 系统测试:运行系统测试工具或脚本,检查各硬件设备是否正常工作,系统功能是否完善。

7、常见外设驱动能写出来,比如led、蜂鸣器、ADC、MPU6050等。

举例led和mpu6050:

Led
//头文件包含

#include "leddrv.h"

#define BUF_LEN 100

//主设备号,次设备号,设备号数量设置

//定义设备结构体

struct myled_dev

{

       struct cdev mydev;

       unsigned int led2gpio;

       unsigned int led3gpio;

       unsigned int led4gpio;

       unsigned int led5gpio;

};

struct myled_dev *pgmydev = NULL;

int myled_open (struct inode *pnode, struct file *pfile)//打开设备

{//}

int myled_close(struct inode *pnode, struct file *pfile)//关闭设备

{//}

/* f. 通过gpio_get_value函数可以获取某个GPIO引脚的当前电平 */

void led_on(struct myled_dev *pmydev,int ledno)

{

       switch(ledno)

       {

              case 2:

                     gpio_set_value(pmydev->led2gpio,1);

                     break;

              //省略345

       }

}

void led_off(struct myled_dev *pmydev,int ledno)

{

       switch(ledno)

       {

              case 2:

                     gpio_set_value(pmydev->led2gpio,0);

                     break;

              //同上

       }

}

//io引脚控制,利用_IO(LED_DEV_MAGIC, 0/1)的arg来控制第二位开关

long myled_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)

{

       struct myled_dev *pmydev = (struct myled_dev *)pfile->private_data;

      

       if(arg < 2 || arg > 5)

       {

              return -1;

       }

       switch(cmd)

       {

              case MY_LED_OFF:

                     led_off(pmydev, arg); //关灯

                     break;

              case MY_LED_ON:

                     led_on(pmydev, arg); //开灯

                     break;

              default:

                     return -1;

       }

       return 0;

}

struct file_operations myops = {

       //设置操作函数集

};

//申请gpio

void request_leds_gpio(struct myled_dev *pmydev,struct device_node *pnode)

{

       //从设备树中提取gpio口  @np - 设备节点指针 @propname - 属性名  @index - gpio口引脚标号

       pmydev->led2gpio = of_get_named_gpio(pnode,"led2-gpio",0);

       //向内核申请GPIO

       gpio_request(pmydev->led2gpio,"led2");

       //345同上

}

//通过gpio_direction_input和gpio_direction_output函数来设置某个GPIO的作用

void set_leds_gpio_output(struct myled_dev *pmydev)

{

       gpio_direction_output(pmydev->led2gpio,0);//0初始值,关灯

       //345同上

}

/*

功能:解除io管脚的映射

参数:addr:io管脚映射的地址

*/

void free_leds_gpio(struct myled_dev *pmydev)

{

       gpio_free(pmydev->led2gpio);

       //345同上

}

int myled_probe(struct platform_device *p_pltdev)

{

       int ret = 0;

       dev_t devno = MKDEV(major, minor);

       struct device_node *pnode = NULL;//设置pnode,设备节点信息

      

       //手动,自动申请设备号

      

       //为struct myled_dev结构体分配内存,并确保在使用之前该内存区域被清零

       pgmydev = (struct myled_dev *)kmalloc(sizeof(struct myled_dev), GFP_KERNEL);

       if(NULL == pgmydev)

       {

              unregister_chrdev_region(devno, char_num);

              printk("kmalloc for 'struct myled_dev' failed\n");

              return -1;

       }

       memset(pgmydev, 0, sizeof(struct myled_dev));

      

       /* 给struct cdev对象指定操作函数集 */

       cdev_init(&pgmydev->mydev, &myops);

       /* 将struct cdev对象添加到内核对应的数据结构中 */

       pgmydev->mydev.owner = THIS_MODULE;

       cdev_add(&pgmydev->mydev, devno, char_num);

       pnode = p_pltdev->dev.of_node;//设备树里面对应的

      

       /* ioremap */

       request_leds_gpio(pgmydev,pnode);

       /* con-register set output */

       set_leds_gpio_output(pgmydev);

      

      

       return 0;

}

int myled_remove(struct platform_device *p_pltdev)

{

       dev_t devno = MKDEV(major, minor);

      

       /* 从内核中移除一个字符设备 */

       cdev_del(&pgmydev->mydev);

       /* 回收设备号 */

       unregister_chrdev_region(devno, char_num);

      

       /* iounmap 解除io管脚的映射   addr:io管脚映射的地址*/

       free_leds_gpio(pgmydev);

      

       /* 释放内存 */

       kfree(pgmydev);

       pgmydev = NULL;

      

       return 0;

}

//使用设备树(Device Tree)来匹配和配置硬件设备

struct of_device_id myleddev_of_ids[] =

{

       [0] = {.compatible = "fs4412,led2-5"},

       [1] = {.compatible = "fs4412,key2"},

       [2] = {},

};

struct platform_driver myled_driver =

{

       .driver = {

              .name = "fs4412leds",

              .of_match_table = myleddev_of_ids,

       },

       .probe = myled_probe,//设备和驱动匹配成功之后调用该函数

       .remove = myled_remove,//设备卸载了调用该函数

};

//基础init,exit等配置

MPU6050

http://t.csdnimg.cn/nkV1v

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值