Linux LED 实验

news2025/2/9 8:18:09

一、Linux 下 LED 灯驱动原理

其实跟裸机实验很相似,只不过要编写符合 Linux 的驱动框架。

1. 地址映射

MMU全称 Memory Manage Unit,即内存存储单元。

MMU主要功能为:

1)完成虚拟空间到物理空间的映射;

2)内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。

Linux 内核启动的时候会初始化 MMU,设置好内存映射。

原来的裸机实验,是直接对 GPIO1_IO03 引脚的复用寄存器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址0X020E0068写入数据。

但是现在开启了 Linux 内核后,MMU设置了内存映射,不能向原先的地址写入数据了。那么必须要得到 0X020E0068 对应的虚拟地址。

1.1 ioremap 函数

该函数用于获取物理地址空间对应的虚拟地址空间,定义在 arch/arm/include/asm/io.h 文件中。

我们查看 ioremap 函数原型

#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE) 

void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size, unsigned int mtype) 
{ 
    return arch_ioremap_caller(phys_addr, size, mtype, __builtin_return_address(0)); 
} 

phys_addr:要映射的物理起始地址。

size:要映射的内存空间大小。

mtype:ioremap 的类型,可以选择 MT_DEVICE、MT_DEVICE_NONSHARED、MT_DEVICE_CACHED 和 MT_DEVICE_WC,ioremap 函数选择 MT_DEVICE。

返回值:__iomem 类型的指针,指向映射后的虚拟空间首地址。

要获取IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器对应 的虚拟地址,使用如下代码即可:

#define SW_MUX_GPIO1_IO03_BASE (0X020E0068) 
static void __iomem* SW_MUX_GPIO1_IO03; 
SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); 

宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址,SW_MUX_GPIO1_IO03 是映射后 的虚拟地址。对于 I.MX6ULL 来说一个寄存器是 4 字节(32 位)的,因此映射的内存长度为 4。

1.2 iounmap 函数

卸载驱动的时候需要使用 iounmap 函数释放掉 ioremap 函数所做的映射。

比如上面的驱动进行卸载

iounmap(SW_MUX_GPIO1_IO03); 

2. IO内存访问函数

Linux 内核提供一组操作函数对映射后的内存进行读写操作。

2.1 读操作函数

u8 readb(const volatile void __iomem *addr) 
u16 readw(const volatile void __iomem *addr) 
u32 readl(const volatile void __iomem *addr)

readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作,参数 addr 就是要 读取写内存地址,返回值就是读取到的数据。 。

2.2 写操作函数

void writeb(u8 value, volatile void __iomem *addr) 
void writew(u16 value, volatile void __iomem *addr) 
void writel(u32 value, volatile void __iomem *addr) 

writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作,参数 value 是要 写入的数值,addr 是要写入的地址。

二、 硬件原理

STM32和IM6ULL的裸机实验有介绍了,就不赘述了。

三、实验程序编写

1. 驱动程序编写

首先寄存器的物理地址

#define CCM_CCGR1_BASE (0X020C406C) 
#define SW_MUX_GPIO1_IO03_BASE (0X020E0068) 
#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4) 
#define GPIO1_DR_BASE (0X0209C000) 
#define GPIO1_GDIR_BASE (0X0209C004)

虚拟地址指针

static void __iomem *IMX6U_CCM_CCGR1; 
static void __iomem *SW_MUX_GPIO1_IO03; 
static void __iomem *SW_PAD_GPIO1_IO03; 
static void __iomem *GPIO1_DR; 
static void __iomem *GPIO1_GDIR; 

LED 灯的开关

void led_switch(u8 sta) 
{ 
    u32 val = 0; 
    if(sta == LEDON) { 
    val = readl(GPIO1_DR); 
    val &= ~(1 << 3); 
    writel(val, GPIO1_DR); 
    }else if(sta == LEDOFF) { 
    val = readl(GPIO1_DR); 
    val|= (1 << 3); 
    writel(val, GPIO1_DR); 
    } 
}

打开设备

static int led_open(struct inode *inode, struct file *filp) 
{ 
    return 0; 
} 

从设备读取数据

static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) 
{ 
    return 0; 
} 

向设备写数据

static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) 
{ 
    int retvalue; 
    unsigned char databuf[1]; 
    unsigned char ledstat; 

    retvalue = copy_from_user(databuf, buf, cnt); 
    if(retvalue < 0) { 
        printk("kernel write failed!\r\n"); 
        return -EFAULT; 
    } 
 
    ledstat = databuf[0]; /* 获取状态值 */ 
    
    if(ledstat == LEDON) { 
        led_switch(LEDON); /* 打开 LED 灯 */ 
    } else if(ledstat == LEDOFF) { 
        led_switch(LEDOFF); /* 关闭 LED 灯 */ 
    } 
    return 0; 
 } 

关闭/释放设备

121 static int led_release(struct inode *inode, struct file *filp) 
122 { 
123     return 0; 
124 } 

设备操作函数

static struct file_operations led_fops = { 
    .owner = THIS_MODULE, 
    .open = led_open, 
    .read = led_read, 
    .write = led_write, 
    .release = led_release, 
}; 

驱动入口函数

static int __init led_init(void) 
{ 
    int retvalue = 0; 
    u32 val = 0; 

    /* 初始化 LED */ 
    /* 1、寄存器地址映射 */ 
    IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4); 
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4); 
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4); 
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4); 
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4); 

    /* 2、使能 GPIO1 时钟 */ 
    val = readl(IMX6U_CCM_CCGR1); 
    val &= ~(3 << 26); /* 清除以前的设置 */ 
    val |= (3 << 26); /* 设置新值 */ 
    writel(val, IMX6U_CCM_CCGR1); 

    /* 3、设置 GPIO1_IO03 的复用功能,将其复用为 
    * GPIO1_IO03,最后设置 IO 属性。 
    */ 
    writel(5, SW_MUX_GPIO1_IO03); 

    /* 寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性 */ 
    writel(0x10B0, SW_PAD_GPIO1_IO03); 

    /* 4、设置 GPIO1_IO03 为输出功能 */ 
    val = readl(GPIO1_GDIR); 
    val &= ~(1 << 3); /* 清除以前的设置 */ 
    val |= (1 << 3); /* 设置为输出 */ 
    writel(val, GPIO1_GDIR); 

    /* 5、默认关闭 LED */ 
    val = readl(GPIO1_DR); 
    val |= (1 << 3); 
    writel(val, GPIO1_DR); 

    /* 6、注册字符设备驱动 */ 
    retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops); 
    if(retvalue < 0){ 
        printk("register chrdev failed!\r\n"); 
        return -EIO; 
    } 
    return 0; 
}

驱动出口函数

static void __exit led_exit(void) 
{ 
    /* 取消映射 */ 
    iounmap(IMX6U_CCM_CCGR1); 
    iounmap(SW_MUX_GPIO1_IO03); 
    iounmap(SW_PAD_GPIO1_IO03); 
    iounmap(GPIO1_DR); 
    iounmap(GPIO1_GDIR); 

    /* 注销字符设备驱动 */ 
    unregister_chrdev(LED_MAJOR, LED_NAME); 
    } 

module_init(led_init); 
module_exit(led_exit); 
MODULE_LICENSE("GPL"); 
MODULE_AUTHOR("Prover"); 

2. 编写测试程序

这时候需要用的 Linux C了,在 Linux 系统下,一切皆文件。应用层如何操控底层硬件,同样也是通过文件 I/O 的方式来实现。设备文件通常在/dev/目录下,我们也把/dev 目录下的文件称为设备节点。除此之外,我们还可以通过 sysfs 文件系统对硬件设备进行 操控。

对于 ALPHA/Mini I.MX6U 开发板出厂系统来说,此 LED 设备使用的是 Linux 内核标准 LED 驱动框架 注册而成,在/dev 目录下并没有其对应的设备节点,其实现使用 sysfs 方式控制。

2.1 sysfs 文件系统

请注意,驱动开发先别看这里,因为需要先 mfgtool 把系统文件烧好,然后直接进入系统,而不是和平时驱动开发那样,根文件是挂载的情况。

sysfs 是一个基于内存的文件系统,同 devfs、proc 文件系统一样,称为虚拟文件系统;它的 作用是将内核信息以文件的方式提供给应用层使用。

sysfs 文件系统挂载在/sys 目录下

进入到/sys/class/leds 目录 下,如果找不到 sys-led 文件,说明你移植的根文件系统是原产的。

brightness、max_brightness 以及 trigger 三个文件,这三个文件都是 LED 设备的 属性文件:

brightness:翻译过来就是亮度的意思,该属性文件可读可写;

max_brightness:该属性文件只能被读取,不能写,用于获取 LED 设备的最大亮度等级。

trigger:触发模式,该属性文件可读可写,读表示获取 LED 当前的触发模式,写表示设置 LED 的 触发模式。通过 cat 命令查看该属性文件。

方括号([heartbeat])括起来的表示当前 LED 对应的触发模式,none 表示无触发,常用的触发模式包括 none(无触发)、mmc0(当对 mmc0 设备发起读写操作的时候 LED 会闪烁)、timer(LED 会有规律的一 亮一灭,被定时器控制住)、heartbeat(心跳呼吸模式,LED 模仿人的心跳呼吸那样亮灭变化)。

通过 echo 命令进行控制:

echo timer > trigger //将 LED 触发模式设置为 timer 
 
echo none > trigger //将 LED 触发模式设置为 none 
echo 1 > brightness //点亮 LED echo 0 > brightness//熄灭 LED

编写应用程序

#include <stdio.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <string.h> 
 
#define LED_TRIGGER "/sys/class/leds/sys-led/trigger" 
#define LED_BRIGHTNESS "/sys/class/leds/sys-led/brightness" 
#define USAGE() fprintf(stderr, "usage:\n" \ 
 " %s <on|off>\n" \ 
 " %s <trigger> <type>\n", argv[0], argv[0]) 
 
int main(int argc, char *argv[]) 
{ 
 int fd1, fd2; 
 
 /* 校验传参 */ 
 if (2 > argc) { 
 USAGE(); 
 exit(-1); 
 } 
 /* 打开文件 */ 
 fd1 = open(LED_TRIGGER, O_RDWR); 
 if (0 > fd1) { 
 perror("open error"); 
 exit(-1); 
 } 
 
 fd2 = open(LED_BRIGHTNESS, O_RDWR); 
 if (0 > fd2) { 
 perror("open error"); 
 exit(-1); 
 } 
 
 /* 根据传参控制 LED */ 
 if (!strcmp(argv[1], "on")) { 
 write(fd1, "none", 4); //先将触发模式设置为 none 
 write(fd2, "1", 1); //点亮 LED 
 } 
 else if (!strcmp(argv[1], "off")) { 
 write(fd1, "none", 4); //先将触发模式设置为 none 
 write(fd2, "0", 1); //LED 灭 
 } 
 else if (!strcmp(argv[1], "trigger")) { 
 if (3 != argc) { 
 USAGE(); 
 exit(-1); 
 } 
 
 if (0 > write(fd1, argv[2], strlen(argv[2]))) 
 perror("write error"); 
 } 
 else 
 USAGE(); 
 
 exit(0); 
} 

然后使能编译环境

source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

CC变量就是交叉编译工具。

编译程序,并把可执行文件拷贝到开发板上。然后运行并添加参数。

2.2 dev 文件系统

这里是正统的驱动方式,跟上个字符设备驱动开发的方式是一样的。

用内核源码的 include,以及开发板的内核和驱动是同源的。

编译成功以后就会生成一个名为“led.ko”的驱动模块文件。

led 驱动加载后,手动创建 /dev/led 节点,然后向 /dev/led 文件写 0 关闭灯,写 1 开启灯。

ledApp.c 的源码如下:

#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"


#define LEDOFF 	0
#define LEDON 	1

/*
 * @description		: main主程序
 * @param - argc 	: argv数组元素个数
 * @param - argv 	: 具体参数
 * @return 			: 0 成功;其他 失败
 */
int main(int argc, char *argv[])
{
	int fd, retvalue;
	char *filename;
	unsigned char databuf[1];
	
	if(argc != 3){
		printf("Error Usage!\r\n");
		return -1;
	}

	filename = argv[1];

	/* 打开led驱动 */
	fd = open(filename, O_RDWR);
	if(fd < 0){
		printf("file %s open failed!\r\n", argv[1]);
		return -1;
	}

	databuf[0] = atoi(argv[2]);	/* 要执行的操作:打开或关闭 */

	/* 向/dev/led文件写入数据 */
	retvalue = write(fd, databuf, sizeof(databuf));
	if(retvalue < 0){
		printf("LED Control Failed!\r\n");
		close(fd);
		return -1;
	}

	retvalue = close(fd); /* 关闭文件 */
	if(retvalue < 0){
		printf("file %s close failed!\r\n", argv[1]);
		return -1;
	}
	return 0;
}

以及 Makefile 文件

KERNELDIR := /home/prover/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)

obj-m := led.o

build: kernel_modules

kernel_modules:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

测试文件同样使用交叉编译器来编译。

arm-linux-gnueabihf-gcc ledApp.c -o ledApp

最后,将 .ko 和 测试文件 拷贝到 rootfs 的 lib/modules/4.1.15 下。

sudo cp ledApp led.ko /home/prover/linux/nfs/rootfs/lib/modules/4.1.15/

由于是第一次加载驱动,先 depmod 然后再 modprobe led.ko。

然后创建 /dev/led 设备节点:

mknod /dev/led c 200 0 

 随后,测试:

./ledApp /dev/led 1

最后把驱动卸载

rmmod led.ko 

 

2.3 Qt 应用

其实就是通过 sysfs 文件系统来控制 led 的亮灭。

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPushButton>
#include <QFile>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    /* 按钮 */
    QPushButton *pushButton;

    /* 文件 */
    QFile file;

    /* 设置lED的状态 */
    void setLedState();

    /* 获取lED的状态 */
    bool getLedState();

private slots:
    void pushButtonClicked();
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include <QDebug>
#include <QGuiApplication>
#include <QScreen>
#include <QRect>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    /* 获取屏幕的分辨率,Qt官方建议使用这
     * 种方法获取屏幕分辨率,防上多屏设备导致对应不上
     * 注意,这是获取整个桌面系统的分辨率
     */
    QList <QScreen *> list_screen =  QGuiApplication::screens();

    /* 如果是ARM平台,直接设置大小为屏幕的大小 */
#if __arm__
    /* 重设大小 */
    this->resize(list_screen.at(0)->geometry().width(),
                 list_screen.at(0)->geometry().height());
    /* 默认是出厂系统的LED心跳的触发方式,想要控制LED,
     * 需要改变LED的触发方式,改为none,即无 */
    system("echo none > /sys/class/leds/sys-led/trigger");
#else
    /* 否则则设置主窗体大小为800x480 */
    this->resize(800, 480);
#endif

    pushButton = new QPushButton(this);

    /* 居中显示 */
    pushButton->setMinimumSize(200, 50);
    pushButton->setGeometry((this->width() - pushButton->width()) /2 ,
                            (this->height() - pushButton->height()) /2,
                            pushButton->width(),
                            pushButton->height()
                            );
    /* 开发板的LED控制接口 */
    file.setFileName("/sys/devices/platform/leds/leds/sys-led/brightness");

    if (!file.exists())
        /* 设置按钮的初始化文本 */
        pushButton->setText("未获取到LED设备!");

    /* 获取LED的状态 */
    getLedState();

    /* 信号槽连接 */
    connect(pushButton, SIGNAL(clicked()),
            this, SLOT(pushButtonClicked()));
}

MainWindow::~MainWindow()
{
}

void MainWindow::setLedState()
{
    /* 在设置LED状态时先读取 */
    bool state = getLedState();

    /* 如果文件不存在,则返回 */
    if (!file.exists())
        return;

    if(!file.open(QIODevice::ReadWrite))
        qDebug()<<file.errorString();

    QByteArray buf[2] = {"0", "1"};

    /* 写0或1 */
    if (state)
        file.write(buf[0]);
    else
        file.write(buf[1]);

    /* 关闭文件 */
    file.close();

    /*重新获取LED的状态 */
    getLedState();
}

bool MainWindow::getLedState()
{
    /* 如果文件不存在,则返回 */
    if (!file.exists())
        return false;

    if(!file.open(QIODevice::ReadWrite))
        qDebug()<<file.errorString();

    QTextStream in(&file);

    /* 读取文件所有数据 */
    QString buf = in.readLine();

    /* 打印出读出的值 */
    qDebug()<<"buf: "<<buf<<endl;
    file.close();
    if (buf == "1") {
        pushButton->setText("LED点亮");
        return true;
    } else {
        pushButton->setText("LED熄灭");
        return false;
    }
}

void MainWindow::pushButtonClicked()
{
    /* 设置LED的状态 */
    setLedState();
}


通过 Qt 编译(或者直接编写Makefile)的可执行文件,传输到开发板上。

在串口 xtem 终端上运行可执行文件。会发现界面直接覆盖了开发板的整个 UI。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2295160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】redis 存储的列表如何分页和检索

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…

2025.2.6 数模AI智能体大更新,更专业的比赛辅导,同提示词效果优于gpt-o1/o3mini、deepseek-r1满血

本次更新重新梳理了回复逻辑规则&#xff0c;无任何工作流&#xff0c;一共3.2k字细节描述。具体效果可以看视频&#xff0c;同时也比对了gpt-o1、gpt-o3mini、deepseek-r1-67BI&#xff0c;从数学建模题目解答上来看&#xff0c;目前我的数模AI智能体具有明显优势。 AI智能体优…

cursor指令工具

Cursor 工具使用指南与实例 工具概览 Cursor 提供了一系列强大的工具来帮助开发者提高工作效率。本指南将通过具体实例来展示这些工具的使用方法。 1. 目录文件操作 1.1 查看目录内容 (list_dir) 使用 list_dir 命令可以查看指定目录下的文件结构: 示例: list_dir log…

【玩转全栈】----Django模板语法、请求与响应

目录 一、引言 二、模板语法 三、传参 1、视图函数到模板文件 2、模板文件到视图函数 四、引入静态文件 五、请求与响应 ?1、请求 2、响应 六、综合小案例 1、源码展示 2、注意事项以及部分解释 3、展示 一、引言 像之前那个页面&#xff0c;太过简陋&#xff0c;而且一个完整…

C++,设计模式,【单例模式】

文章目录 一、模式定义与核心价值二、模式结构解析三、关键实现技术演进1. 基础版(非线程安全)2. 线程安全版(双重检查锁)3. 现代C++实现(C++11起)四、实战案例:全局日志管理器五、模式优缺点深度分析✅ 核心优势⚠️ 潜在缺陷六、典型应用场景七、高级实现技巧1. 模板化…

基于yolov11的阿尔兹海默症严重程度检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv11的阿尔兹海默症严重程度检测系统是一种创新的医疗辅助工具&#xff0c;旨在通过先进的计算机视觉技术提高阿尔兹海默症的早期诊断和病情监测效率。阿尔兹海默症是一种渐进性的神经退行性疾病&#xff0c;通常表现为认知障碍、记忆丧失和语言障碍等症状…

设计模式-生产者消费者模型

阻塞队列&#xff1a; 在介绍生产消费者模型之前&#xff0c;我们先认识一下阻塞队列。 阻塞队列是一种支持阻塞操作的队列&#xff0c;常用于生产者消费者模型&#xff0c;它提供了线程安全的队列操作&#xff0c;并且在队列为空或满时&#xff0c;能够阻塞等待&#xff0c;…

RabbitMQ介绍以及基本使用

文章目录 一、什么是消息队列&#xff1f; 二、消息队列的作用&#xff08;优点&#xff09; 1、解耦 2、流量削峰 3、异步 4、顺序性 三、RabbitMQ基本结构 四、RabbitMQ队列模式 1、简单队列模式 2、工作队列模式 3、发布/订阅模式 4、路由模式 5、主题模式 6、…

嵌入式硬件篇---OpenMV的硬件流和软件流

文章目录 前言一、硬件流控制&#xff08;Hardware Flow Control&#xff09;1. 基本原理RTSCTS 2. OpenMV中的实现• 硬件要求• 代码配置• 工作流程 二、软件流控制&#xff08;Software Flow Control&#xff09;1. 基本原理XONXOFF 2. OpenMV中的实现• 代码配置• 工作流…

【AIGC提示词系统】基于 DeepSeek R1 + ClaudeAI 易经占卜系统

上篇因为是VIP&#xff0c;这篇来一个免费的 提示词在最下方&#xff0c;喜欢的点个关注吧 引言 在人工智能与传统文化交融的今天&#xff0c;如何让AI系统能够传递传统易经文化的智慧&#xff0c;同时保持易经本身的神秘感和权威性&#xff0c;是一个极具挑战性的课题。本文将…

OpenAI 实战进阶教程 - 第十节 : 结合第三方工具的向量数据库Pinecone

面向读者群体 本节课程主要面向有一定编程基础和数据处理经验的计算机从业人员&#xff0c;如后端开发工程师、数据工程师以及对 AI 应用有浓厚兴趣的技术人员。即使你之前没使用过向量数据库&#xff0c;也可以通过本节的实操内容快速上手&#xff0c;为企业或个人项目构建强…

深入Linux系列之进程地址空间

深入Linux系列之进程地址空间 1.引入 那么在之前的学习中&#xff0c;我们知道我们创建一个子进程的话&#xff0c;我们可以在代码层面调用fork函数来创建我们的子进程&#xff0c;那么fork函数的返回值根据我们当前所处进程的上下文是返回不同的值&#xff0c;它在父进程中返…

AWK系统学习指南:从文本处理到数据分析的终极武器 介绍

目录 一、AWK核心设计哲学解析 1.1 记录与字段的原子模型 1.2 模式-动作范式 二、AWK编程语言深度解析 2.1 控制结构 说明&#xff1a; 2.2 关联数组 代码说明&#xff1a; 示例输入和输出&#xff1a; 注意事项&#xff1a; 2.3 内置函数库 三、高级应用技巧 3.1…

250207-MacOS修改Ollama模型下载及运行的路径

在 macOS 上&#xff0c;Ollama 默认将模型存储在 ~/.ollama/models 目录。如果您希望更改模型的存储路径&#xff0c;可以通过设置环境变量 OLLAMA_MODELS 来实现。具体步骤如下&#xff1a; 选择新的模型存储目录&#xff1a;首先&#xff0c;确定您希望存储模型的目标目录路…

半导体行业跨网文件交换系统

在当今这个数字化转型的时代&#xff0c;半导体行业作为技术密集型产业&#xff0c;正面临着前所未有的信息安全挑战。随着企业内外网隔离措施的加强&#xff0c;如何实现既安全又高效的跨网文件交换&#xff0c;成为了众多半导体企业的一大难题。 特别是在研发和生产过程中产生…

使用GD32F470的硬件SPI读写W25Q64

代码简单改下引脚定义便可以使用&#xff01; 使用的单片机具体型号&#xff1a;GD32F470ZGT6 简单介绍下W25Q64&#xff1a; /* W25Q64 性能参数 */ /* 容量&#xff1a;8MByte 64Mbit */ /* 有128个块&#xff0c;每个块有64KByte */ /* 每个块有16个扇区&#xff0c;每个…

02为什么 OD门和 OC门输出必须加上拉电阻?

为什么 OD&#xff08;开漏&#xff09;门和 OC&#xff08;开集&#xff09;门输出必须加上拉电阻&#xff1f; 1、首先一点&#xff0c;知道OD是说的MOS管&#xff0c;OC是说的三极管&#xff0c;二者的区别与联系大家应该都懂。 2、以OC门举例&#xff0c;芯片的OC门内部结…

AI方案调研与实践 (不定期补充)

目录 说明 1. AI云主机准备 1.1 Ollama配置 设置模型保存路径 配置模型驻留内存时间 查看GPU状况命令: nvidia-smi 2. Deepseek 2.1 安装与使用 3. LobeChat配置 参考 说明 调研并实例化各种AI方案&#xff0c;探索训练/使用方式的最佳实践。 1. AI云主机准备 可以去一…

人工智能大模型之模型蒸馏与知识蒸馏

一、背景介绍 随着人工智能技术的不断发展&#xff0c;大模型在各个领域的应用也越来越广泛。模型蒸馏&#xff08;Model Distillation&#xff09;和知识蒸馏&#xff08;Knowledge Distillation&#xff09;是两种非常重要的模型压缩技术&#xff0c;它们可以帮助我们将大型…

[手机Linux] onepluse6T 系统重新分区

一&#xff0c;刷入TWRP 1. 电脑下载 Fastboot 工具&#xff08;解压备用&#xff09;和对应机型 TWRP&#xff08;.img 后缀文件&#xff0c;将其放入前面解压的文件夹里&#xff09; 或者直接这里下载:TWRP 2. 将手机关机&#xff0c;长按音量上和下键 开机键 进入 fastbo…