首页 > 编程语言 >飞腾派使用内核态编程完成LED20控制操作

飞腾派使用内核态编程完成LED20控制操作

时间:2023-11-01 20:36:01浏览次数:33  
标签:LED20 void 编程 内核 模块 寄存器 include make

1 基础知识

在该程序设计过程中我们首先需要学习如何在内核态编程。

1.1 内核态编程

在内核态中编写C语言程序和在用户态中编写C语言程序不同,在用户态中编写C语言程序,我们可以使用libc库,通过系统调用访问内核态的相关操作。

基础的内核态程序如下:

#include<linux/init.h>
#include<linux/module.h>
static int test_init(void)
{
    printk("hello kernel\n");
    return 0;
}
static void test_exit(void)
{
    printk("bye\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

首先是加载和卸载模块。

加载模块我们使用的是函数module_init,该函数的作用是告诉内核你编写模块程序是从哪里开始执行,module_init()中的参数就是入口函数的函数名。

卸载模块我们使用的函数时module_exit,该函数的作用是告诉内核你编写模块程序是从哪里离开,module_exit()中的参数名就是卸载函数的函数名。

在进行内核态编程的过程中,头文件为

#include<linux/init.h> //init&exit相关宏
#include<linux/module.h> //所有模块都需要的头文件

由于内核态编程和用户层编程所使用的库函数不一样,所以它的头文件也和我们在用户层编写程序时所使用的头文件不一样。

内核层头文件的位置:/usr/src/linux-x.y.z/include/

用户层头文件的位置:/usr/include/

printk是内核信息打印函数,功能和标准C库的printf类似。

MODULE_LICENSE("GPL") 模块声明,描述内核模块的许可权限。如果不声明LICENSE,模块被加载时,将收到内核的警告。

1.2 Makefile

obj-m :=file.o

all:
	make -C 你的Linux环境中Makefile所在的文件的位置路径 M=$(PWD) modules
clean:
	make -C 你的Linux环境中Makefile所在的文件的位置路径 M=$(PWD) modules clean

all和clean下面的命令要严格使用“tab”。

obj-m 后面的文件是你想要编译的文件,表示编译连接后将生成的file.ko模块。

因为在内核态编译过程中使用Makefile文件,实际上你在使用make的过程中,时经历了两次make。第一次make是你自己编写的Makefile文件,你使用了该文件进行了一次make;第二次make是Linux系统环境中自带的Makefile,你使用了该文件进行了第二次make。

-C 指定内核Kbuild Makefile所在路径。(Linux环境下Makefile路径)

M=指定模块所在路径。(自己编写的Makefile路径)

make modules:编译模块

make clean:清楚模块编译产生的文件

1.3 内核模块加载命令

sudo insmod hello.ko //加载模块
sudo rmmod hello //卸载模块
dmesg //查看日志(查看printk的输出)
//模块相关命令
lsmod //查看本机模块
modinfo //查看模块信息

rmmod后面要加用lsmod查看到的模块名字“hello”,而不是“hello.ko"。

1.4 硬件驱动原理

image-20231101195503588

我们通过程序控制LED20灯的流程如下:

我们写出代码后放入硬盘中,程序在执行过程中被调入内存,在CPU中执行。CPU通过执行程序中的命令来控制GPIO控制器,通过GPIO控制器控制我们想要控制的外设。

在编写程序的硬件驱动流程如下:
我们首先来看我们使用的LED灯的硬件:
image-20231101195740333

可以发现,如果我们想要控制灯灭的话,我们需要向CPU_RUN输入低电平信号,从而使得路径上没有电势差导致没有电流,最后灯灭。

如果我们想要控制CPU_RUN输入低电平信号的话,我们需要查看其在SOC中对应的引脚。

image-20231101195926306

我们可以发现,CPU_RUN对应的引脚是E37,所以我们接下来要看E37引脚。

image-20231101200009094

我们发现如果我们要向CPU_RUN输入低电平的话,我们需要使得E37引脚使用功能6:GPIO1_8。

那么我们就要使用PAD控制器,该控制器控制引脚的复用。

PAD复用有两个知识点:
1.用户可以通过配置控制寄存器来完成复用。

2.在PAD的内存空间中(也就是寄存器中)[2:0]低三位的空间用于配置复用功能。

所以我们首先需要找到PAD寄存器的基地址,然后找到E37引脚的偏移地址,就可以锁定该引脚的位置。然后对该引脚的数据进行更改,低三位的数值变为110,从而使用功能6。

image-20231101200353122

image-20231101200402227

然后我们控制GPIO控制器。

想让GPIO输出低电平需要控制两个寄存器:
1.配置端口方向控制寄存器GPIO_SWPORT_DDR方向为输出。

2.配置端口输出寄存器GPIO_SWPORT_DR。

首先我们需要找到这两个寄存器的地址。

image-20231101200547653

然后我们先配置方向控制寄存器为”输出“。

image-20231101200633695

配置GPIO1_8为输出方向,则修改GPIO_SWPORT_DDR第8位为“1”。

然后配置输出寄存器输出”低电平“。

image-20231101200725233

配置GPIO1_8输出低电平,则修改GPIO_SWPORT_DR第8位为“0”。

1.5 IO资源访问

在Linux内核下,所有的物理地址都会通过MMU(Memory Management Unit 内存管理单元)进行映射得到一个虚拟地址。

那么我们如果想要操作物理地址的话,就需要地址映射函数(ioremap)和解除映射函数(iounmap),帮助我们将物理地址映射为虚拟地址,然后对虚拟地址进行相关的操作。

函数演示如下:

#include<linux/io.h>
#include<linux/init.h>
#include<linux/module.h>
void __iomen* ioGPIO5_15_DR;
static int drv_led_init(void)
{
    //将物理地址与虚拟地址映射
    ioGPIO5_15_DR=ioremap(0x28039000,4);
    printk("%p",ioGPIO5_15_DR); //输出得到的虚拟地址
    return 0;
}
static void drv_led_exit(void)
{
    //解除映射
    iounmap(ioGPIO5_15_DR);
}

module_init(drv_led_init);
module_exit(drv_led_exit);
MODULE_LICENSE("GPL");

IO读写操作我们使用的函数时writel()和readl()。

writel()往内存映射的I/O空间上写数据,writel()I/O上写入32位数据(4字节)。

readl()往内存映射的I/O空间上读数据,readl()I/O上读取32位数据(4字节)。

典型的寄存器操作(读-改-写)

//定义临时变量
int val=0;
//读:获取GPIO5的数据寄存器的原值
val=readl(ioGPIO5_15_DR);
//改:将第15位修改位1
val |=(1<<15) //用与或操作修改数值
//写:将新值写入GPIO5的数据寄存器
writel(val,ioGPIO5_15_DR);

2 程序

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/uaccess.h>

#define E37_reg0 (0x32B30000+0x01F8)
#define GPIO1_8_DDR (0x28035000+0x04)
#define GPIO1_8_DR (0x28035000+0x00)

void __iomem* ioE37_reg0;
void __iomem* ioGPIO1_8_DDR;
void __iomem* ioGPIO1_8_DR;

void pad_set(void)
{
    int val = 0;
     
    val = readl(ioE37_reg0);
    
    val &= ~(0x7 << 0);
    val |= (0x6 << 0);
    
    writel(val, ioE37_reg0);
}

void gpio_set(void)
{
    int val = 0;
     
    val = readl(ioGPIO1_8_DDR);
    
    val |= (0x1 << 8);
    
    writel(val, ioGPIO1_8_DDR);
}

void led_off(void)
{
    int val = 0;
     
    val = readl(ioGPIO1_8_DR);
    
    val &= ~(0x1 << 8);
    
    writel(val, ioGPIO1_8_DR);
}

static int drv_led_init(void)
{
    ioE37_reg0 = ioremap(E37_reg0, 4);
    ioGPIO1_8_DDR = ioremap(GPIO1_8_DDR, 4);
    ioGPIO1_8_DR = ioremap(GPIO1_8_DR, 4);
    
    pad_set(); 
    gpio_set(); 
    
    led_off();
    
    return 0;
}
 
static void drv_led_exit(void)
{
    iounmap(ioGPIO1_8_DR);
    iounmap(ioGPIO1_8_DDR);
    iounmap(ioE37_reg0);
}
 
module_init(drv_led_init);
module_exit(drv_led_exit);

MODULE_LICENSE("GPL");

标签:LED20,void,编程,内核,模块,寄存器,include,make
From: https://www.cnblogs.com/Ly227/p/17804028.html

相关文章

  • shell编程
    H3目标:1.shell是什么2.shell脚本是什么3.理解为什么学习shell脚本4.shell解析器linux系统使用内核操作计算机硬件,通过编写shell命令发送给linux内核去执行,操作计算机硬件,shell是用户操作计算机硬件的桥梁,shell是指令shell是一门设计语言H5shell脚本就是通过shell命......
  • [Linux环境编程]Linux程序设置调度策略和优先级
    可以使用chrt命令设置进程的调度策略和优先级。以下命令将11528进程的调度策略设置为RR,调度优先级设置为20:chrt-r--pid2011528具体使用见其help信息,如下:ighthouse@cpp_template$chrt--helpShoworchangethereal-timeschedulingattributesofaprocess.Setpol......
  • Go 并发编程 - runtime 协程调度(三)
    GoRuntimeGoruntime可以形象的理解为Go程序运行时的环境,类似于JVM。不同于JVM的是,Go的runtime与业务程序直接打包在一块,是一个可执行文件,直接运行在操作系统上,效率很高。runtime包含了一些Go的一些非常核心的功能:协程调度、垃圾回收、内存分配等。本文将着重介绍......
  • 实验3_c语言函数应用编程
    task1#include<stdio.h>#include<stdlib.h>#include<time.h>#include<windows.h>#defineN80voidprint_text(intline,intcol,chartext[]);voidprint_spaces(intn);voidprint_blank_lines(intn);intmain(){intline,col......
  • C#编程工具Visual Studio2022新特性(持续。。。)
    VS从2017年开始变动比较大,目前最新的版本是2022年版,框架也比之前的高级一丢丢,如果是老用户,可能对它的新特性还不是很习惯,跟着我一起对VS2022新特性进行深入探索:一、之前的编码模板的改变(对比)在VS2022中如果要切换到旧版本模板呢,你可以在创建项目时选择“.NETFramework”项目......
  • 带有最小间隔时间的队列读取实现 —— Python异步编程
     (注:照片源自免费网站,地址:https://www.freepik.com/photos/angry-panda/13)  ==================================================   ==================================================......
  • 实验3 c语言函数应用编程
    task11源代码1#include<stdio.h>2#include<stdlib.h>3#include<time.h>4#include<windows.h>5#defineN8067voidprint_text(intline,intcol,chartext[]);8voidprint_spaces(intn);9voidprint_blank_lines(intn)......
  • 05. UDP编程
    一、什么是UDP协议  相对于TCP协议,UDP协议则是面向无连接的协议。使用UDP协议时,不需要建立连接,只需要知道对象的IP地址和端口号,就可以直接发数据包。但是,数据无法保证一定到达。虽然用UDP传输数据不可靠,但它的优点是比TCP协议的速度快。对于不要求可靠到达的数据而......
  • 学编程如何赚钱?翔知盈
    在技术公司工作 。当你掌握了编程技能,你可以在一些技术公司中找到工作,例如Google、Microsoft、IBM等,这些公司通常提供高薪和良好的福利。做游戏开发者 。如果你对游戏开发感兴趣,你可以尝试开发自己的游戏,并在Steam、App Store等平台上销售或提供付费服务。做网站开发者 。网......
  • 无代码:让编程不再有门槛
    随着数字化时代的到来,一种全新的开发方式正在崛起,它就是无代码开发。通过简单的拖拽和配置,无代码平台-kepler能让没有编程经验的人,快速构建出功能强大的应用程序,从而改变了传统软件开发的模式。Kepler无代码是一种可视化编程工具,让用户通过拖拽组件和配置参数等方式,快速构建应用程......