[imx6ull]Linux下TTY-串口编程

这篇具有很好参考价值的文章主要介绍了[imx6ull]Linux下TTY-串口编程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


一、Linux TTY

1.TTY介绍

TTY 是Teletype或Teletypewriter的缩写,原来是指电传打字机,在以前计算机体积很大,所以用teletype这个设备来连接到计算机,后来这种设备键盘显示器取代,但是他们都作为计算机的终端设备所存在,所以TTY沿用至今,用来泛指计算机的终端设备,它作为一个子系统既支持串口,也支持键盘,显示器,还支持更复杂的功能。

[imx6ull]Linux下TTY-串口编程

老式电传打字机

2.控制台和终端

控制台(Console)和终端(Terminal)在我们如今的平时使用中以经很少回去区分他们,但是在计算机的早期时代它们却不同,我们知道早期计算机价格昂贵,座椅我们会使用专门的设备也就是电传打字机(TTY)通常通过串口连接计算机,然后来使用计算机,这样一台只有键盘和显示器通过串口连接到计算机的设备就是终端,而早期直接连接在计算机上,而并不是用电传打字机通过串口连接计算机的那套的键盘和显示器就是控制器。控制器是计算机本身的设备,一个计算机只有一个控制台,在计算机启动时内核与后台服务等消息,都可以显示到控制台上,而不会显示到终端上,如下:

[imx6ull]Linux下TTY-串口编程

由于计算机的硬件价格越来越便宜,导致如今通常都是一个人用一台计算机,用不上了早期的那种终端设备了,所以渐渐的控制台和终端从硬件的概念演化成了软件的概念,所以现在把直接显示系统消息的终端称为控制台,而其他的则称为终端,但是在我们使用的Linux系统中已经很少区分控制台与终端了。

注意:控制台也是一种终端,不过它相对权限更大,它可以查看内核打印的信息,我们可以从多个终端中指定一个作为控制台

终端就是处理主机输入和输出的一套设备,用来显示主机的运算输出,以及接受主机要求的输入,只要能够提供给计算机输入和输出功能的就是终端,与所在的位置无关,可以是真实设备也可能是虚拟设备
终端的分类包括,本地终端,用串口连接的终端以及基于网络的远程终端

  1. 本地终端,对于个人 pc 机,连接了显示器,键盘,鼠标等设备就可以称作一个本地终端
  2. 用串口连接的终端,也就是将开发板连接到一个带显示器和键盘的 pc 机,然后 pc 机通过运行一个终端模拟程序,从而实现数据收发
  3. 基于网络的远程终端,则是利用 ssh 协议远程登录到一个主机

3.TTY 设备节点

在根文件系统的 /dev 路径下可以看到很多和 tty 相关的设备节点,如下所示:

设备节点 含义
/dev/ttyN dev/tty0 代表前台程序的终端,/dev/tty 代表自己所使用的终端,剩余的从 /dev/tty1 开始的 /dev/ttyX 分别代表一个虚拟终端
/dev/pts/N 这类设备节点是伪终端对应的设备节点,伪终端对应的设备节点都在 /dev/pts 目录下,以数字编号命名,通过 ssh 或者 telnet 这些远程登录协议登录到开发板,那么开发板就会在 /dev/pts 目录下生成一个设备节点
/dev/ttymxcN imx6ull 的串口终端,以此命名
/dev/console 通过内核的配置可以指定 console 是哪一个 tty 设备

注意:N代表编号如0、1、2等等

二、TTY应用编程

串口的应用编程其实就是通过ioctl对串口进行配置,然后调用read读取串口的数据,再使用write向串口写入数据,但是linux为上层用户做了一层封装,也就是将ioctl操作操作封装成了一套标准API,我们直接使用这一套标准API编写自己的串口应用程序。

1.termios 结构体

在应用编程中包含两方面,读写和配置,而配置中termios结构体十分重要,该结构体定义如下:

struct termios
{
    tcflag_t c_iflag; 	/* input mode flags */
    tcflag_t c_oflag; 	/* output mode flags */
    tcflag_t c_cflag; 	/* control mode flags */
    tcflag_t c_lflag; 	/* local mode flags */
    cc_t c_line; 		/* line discipline */
    cc_t c_cc[NCCS];	/* control characters */
    speed_t c_ispeed; 	/* input speed */
    speed_t c_ospeed; 	/* output speed */
};

c_iflag 输入模式控制输入数据在被传递给应用程序之前的处理方式

含义
IGNBRK 忽略输入终止条件
BRKINT 当检测到输入终止条件时发送 SIGINT 信号
IGNPAR 忽略帧错误和奇偶校验错误
PARMRK 对奇偶校验错误做出标记
INPCK 对接收到的数据执行奇偶校验
ISTRIP 将所有接收到的数据裁剪为 7 比特位,也就是去除第八位

c_oflag 输出模式控制字符的处理方式,也就是由应用程序发出去的字符在传递到串口之前是如何处理的

含义
OPOST 启用输出处理功能,如果不设置该标志则其他标志都被忽略
OLCUC 将输出字符中的大写字符转换成小写字符
ONOCR 在第 0 列不输出回车符
ONLRET 不输出回车符
OFILL 发送填充字符以提供延时

c_cflag 控制模式控制终端设备的硬件特性,例如对于串口而言该字段可以设置串口波特率,数据位,校验位,停止位等硬件特性,在一些系统中也可以使用 c_ispeed 和 c_ospeed 这两个成员来指定串口的波特率

含义
B4800 4800 波特率
B9600 9600 波特率
B19200 19200 波特率
B38400 38400 波特率
B57600 57600 波特率
B115200 115200 波特率
CS5 5 个数据位
CS6 6 个数据位
CS7 7 个数据位
CS8 8 个数据位
CSTOPB 2 个停止位,如果不设置该标志则默认是一个停止位
CREAD 接收使能
PARENB 使能奇偶校验
PARODD 使用奇校验、而不是偶校验

c_lflag 本地模式用于控制终端的本地数据处理和工作模式

含义
ISIG 若收到信号字符,则会产生相应的信号
ICANON 启用规范模式
ECHO 启用输入字符的本地回显功能,当我们在终端输入字符的时候,字符会显示出来,这就是回显功能
ECHOE 若设置 ICANON,则允许退格操作
ECHOK 若设置 ICANON,则 KILL 字符会删除当前行
ECHONL 若设置 ICANON,则允许回显换行符
ECHOPRT 若设置 ICANON 和 IECHO,则删除字符和被删除的字符都会被显示
ECHOKE 若设置 ICANON,则允许回显在 ECHOE 和 ECHOPRT 中设定的 KILL字符
TOSTOP 若一个后台进程试图向它的控制终端进行写操作,则系统向该后台进程的进程组发送 SIGTTOU 信号
IEXTEN 启用输入处理功能

c_cc 特殊控制字符是一些字符组合,例如 ctrl + c 或者 ctrl + z,当用户键入这样的组合键终端采取特殊处理的方式

含义
VTIME 非规范模式下, 指定读取的每个字符之间的超时时间(以分秒为单位) TIME
VMIN 在非规范模式下,指定最少读取的字符数 MIN

2.终端的三种工作模式

终端的三种工作模式,分别是规范模式 canonical mode,非规范模式 non-canonical mode 和原始模式 raw mode,通过设置 c_lflag 设置 ICANNON 标志来定义终端是以规范模式还是非规范模式工作,默认为规范模式

  • 规范模式:
    所有输入是基于行处理的,在用户输入一个行结束符之前,系统调用 read 函数是无法读到用户输入的任何字符的,除了 eof 之外的行结束符与普通字符一样会被 read 函数读取到缓冲区中
    在规范模式下,行编辑是可行的,而且一次调用 read 最多只能读取一行数据
  • 非规范模式:
    所有输入及时有效,不需要用户另外输入行结束符
    在非规范模式下,对参数 MIN(c_cc[VMIN])和 TIME(c_cc[VTIME])的设置决定 read 函数的调用方式,MIN 和 TIME 的取值不同,会有以下四种不同的情况:
MIN TIME 说明
=0 =0 read 调用总是会立即返回,若有可读数据,则读数据并返回被读取的字节数,否则读取不到数据返回 0
>0 =0 read 函数会被阻塞,直到有 MIN 个字符可以读取时,read 才返回,返回值为读取的字节数
=0 >0 只要有数据可读或者经过 TIME 个十分之一秒的时间,read立即返回,返回为读取的字节数
>0 >0 当有 MIN 个字节可读或者两个输入字符之间的时间间隔超过 TIME 个十分之一秒,read 才返回,因为在输入第一个字符后系统才会启动定时器,所以,read 至少读取一个字节后才返回
  • 原始模式:
    是一种特殊的非规范模式,所有的输入数据以字节为单位被处理,即有一个字节输入时,触发输入有效,但是终端不可回显,并且禁用终端输入和输出字符的所有特殊处理

3.应用代码

uart.c

/*********************************************************************************
 *      Copyright:  (C) 2023 Deng Yonghao<dengyonghao2001@163.com>
 *                  All rights reserved.
 *
 *       Filename:  uart.c
 *    Description:  This file uart TTY  
 *                 
 *        Version:  1.0.0(2023年03月29日)
 *         Author:  Deng Yonghao <dengyonghao2001@163.com>
 *      ChangeLog:  1, Release initial version on "2023年03月29日 15时21分00秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <signal.h>

#define	READ_FLAG 0			/*读标志*/
#define	WRITE_FLAG 1		/*写标志*/

struct uart_parameter {
    unsigned int    baudrate;       // 波特率 
    unsigned char   dbit;           // 数据位 
    char            parity;         // 奇偶校验 
    unsigned char   sbit;           // 停止位 
};

static struct termios   oldtio;     // 用于保存终端的配置参数
static int              fd_uart;  	// 串口终端对应的文件描述符

static int uart_init(const char *device);//串口初始化
static int uart_configuration(const struct uart_parameter *para);//串口配置
static void async_io_init(void);//异步i/o初始化函数
static void io_handler(int sig, siginfo_t *info, void *context);//信号处理函数,当串口有数据可读时,会跳转到该函数执行
static void program_usage(char *progname);//提示信息

int main(int argc, char *argv[])
{
    struct uart_parameter   uart_para;
    char                    device[64];
    int                     rw_flag = -1;
    unsigned char           write_buf[10] = {0x11, 0x22, 0x33, 0x44,0x55, 0x66, 0x77, 0x88};    
    int                     n;
    int                     opt;
	char					*progname=NULL;

    memset(&uart_para, 0x0, sizeof(struct uart_parameter));
    memset(device, 0x0, sizeof(device));

    struct option           long_options[] = {
        {"device", required_argument, NULL, 'D'},
        {"type", required_argument, NULL, 'T'},
        {"brate", no_argument, NULL, 'b'},
        {"dbit", no_argument, NULL, 'd'},
        {"parity", no_argument, NULL, 'p'},
        {"sbit", no_argument, NULL, 's'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    memset(&uart_para, 0x0, sizeof(struct uart_parameter));
	progname = (char *)basename(argv[0]);

    while((opt = getopt_long(argc, argv, "D:T:b:d:p:s:h", long_options, NULL)) != -1)
    {
        switch(opt)
        {
            case'D':
                strcpy(device, optarg);
                break;
                
            case'T':
                if (!strcmp("read", optarg))
                {
                    rw_flag = READ_FLAG;   
                }       
                else if (!strcmp("write", optarg))
                {
                    rw_flag = WRITE_FLAG;   
                }
                break;
                
            case'b':
                uart_para.baudrate = atoi(optarg);
                break;

            case'd':
                uart_para.dbit = atoi(optarg);
                break;

            case'p':
                uart_para.parity = *optarg;
                break;

            case's':
                uart_para.sbit = atoi(optarg);
                break;

            case'h':
                program_usage(progname);
                return 0;

            default:
                break;
        }
    }

    if (NULL == device || -1 == rw_flag) 
    {
        program_usage(progname);
        return -1;
    }

    /* 串口初始化 */ 
    if (uart_init(device))
    {
        printf("fail to execute uart_init\n");
        return -2;
    }

    /* 串口配置 */ 
    if (uart_configuration(&uart_para)) 
    {
        /* 恢复之前的配置 */
        tcsetattr(fd_uart, TCSANOW, &oldtio);   
        return -3;
    }

    /* 通过读写标志判断读写,然后进行读写 */
    switch (rw_flag) 
    {
        case 0:  // 读串口数据
            async_io_init();	// 我们使用异步 i/o 方式读取串口的数据,调用该函数去初始化串口的异步 i/o
            for ( ; ; )         // 进入休眠,等待有数据可读,有数据可读之后就会跳转到 io_handler() 函数
            {
                sleep(1);
            }
            break;
        case 1:   // 向串口写入数据
            for ( ; ; ) 
            {   		
                write(fd_uart, write_buf, 8); 	
                sleep(1);       	
            }
            break;
    }

    tcsetattr(fd_uart, TCSANOW, &oldtio);  
    close(fd_uart);
    
    return 0;
}

static int uart_init(const char *device)
{
    fd_uart = open(device, O_RDWR | O_NOCTTY);
    if (0 > fd_uart)
    {
        printf("fail to open uart file\n");
        return -1;
    }

    /* 获取串口当前的配置参数 */
    if (0 > tcgetattr(fd_uart, &oldtio))
    {
        printf("fail to get old attribution of terminal\n");
        close(fd_uart);
        return -2;
    }

    return 0;
}


static int uart_configuration(const struct uart_parameter *para)
{
    struct termios newtio;
    speed_t speed;

    /* 设置为原始模式
     * 配置为原始模式相当于已经对 newtio 做了如下配置
     * IGNBRK 忽略输入终止条件,BRKINT 检测到终止条件发送 SIGINT 信号,PARMRK 对奇偶校验做出标记
     * ISTRIP 裁剪数据位为 7 bit,去掉第八位,INLCR 换行符转换为回车符,IGNCR 忽略回车符
     * ICRNL 将回车符转换为换行符,IXON 启动输出流控
     * OPOST 启用输出处理功能
     * ECHO 使能回显,ICANON 规范模式,ISIG 收到信号产生相应的信号,IEXTEN 输入处理
     * CSIZE 数据位掩码,PARENB 使能校验,CS8 8 个数据位
     * termios_p->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP| INLCR | IGNCR | ICRNL | IXON);
     * termios_p->c_oflag &= ~OPOST;
     * termios_p->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
     * termios_p->c_cflag &= ~(CSIZE | PARENB);
     * termios_p->c_cflag |= CS8;
     */
    memset(&newtio, 0x0, sizeof(struct termios));
    cfmakeraw(&newtio);

    /* CREAD 使能接受 */
    newtio.c_cflag |= CREAD;

    /* 设置波特率 */
    switch (para->baudrate)
    {
        case 1200:
            speed = B1200;
            break;
        case 1800:
            speed = B1800;
            break;
        case 2400:
            speed = B2400;
            break;
        case 4800:
            speed = B4800;
            break;
        case 9600:
            speed = B9600;
            break;
        case 19200:
            speed = B19200;
            break;
        case 38400:
            speed = B38400;
            break;
        case 57600:
            speed = B57600;
            break;
        case 115200:
            speed = B115200;
            break;
        case 230400:
            speed = B230400;
            break;
        case 460800:
            speed = B460800;
            break;
        case 500000:
            speed = B500000;
            break;
        default:
            speed = B115200;
            printf("default baud rate is 115200\n");
            break;
    }

    /* cfsetspeed 函数,设置波特率 */
    if (0 > cfsetspeed(&newtio, speed))
    {
        printf("fail to set baud rate of uart\n");
        return -1;
    }

	/* 设置数据位大小
     * CSIZE 是数据位的位掩码,与上掩码的反,就是将数据位相关的比特位清零
     * CSX (X=5,6,7,8) 表示数据位位数
     */
    newtio.c_cflag &= ~CSIZE;
    switch (para->dbit)
    {
        case 5:
            newtio.c_cflag |= CS5;
            break;
        case 6:
            newtio.c_cflag |= CS6;
            break;
        case 7:
            newtio.c_cflag |= CS7;
            break;
        case 8:
            newtio.c_cflag |= CS8;
            break;
        default:
            newtio.c_cflag |= CS8;
            printf("default data bit size is 8\n");
            break;
   	}

	/* 设置奇偶校验
     * PARENB 用于使能校验
     * INPCK 用于对接受的数据执行校验
 	 * PARODD 指的是奇校验
     */
    switch (para->parity)
    {
        case 'N':   //无校验
            newtio.c_cflag &= ~PARENB;
            newtio.c_iflag &= ~INPCK;
            break;
        case 'O':   //奇校验
            newtio.c_cflag |= (PARODD | PARENB);
            newtio.c_iflag |= INPCK;
            break;
        case 'E':   //偶校验
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            newtio.c_iflag |= INPCK;
            break;
        default:    //默认配置为无校验
            newtio.c_cflag &= ~PARENB;
            newtio.c_iflag &= ~INPCK;
            printf("default parity is N (no check)\n");
            break;
    }

	/* 设置停止位
     * CSTOPB 表示设置两个停止位
     */
    switch (para->sbit)
    {
        case 1:     //1个停止位
            newtio.c_cflag &= ~CSTOPB;
            break;
        case 2:     //2个停止位
            newtio.c_cflag |= CSTOPB;
            break;
        default:    //默认配置为1个停止位
            newtio.c_cflag &= ~CSTOPB;
            printf("default stop bit size is 1\n");
            break;
   	}

	/* 将 MIN 和 TIME 设置为 0,通过对 MIN 和 TIME 的设置有四种 read 模式
     * read 调用总是会立即返回,若有可读数据,则读数据并返回被读取的字节数,否则读取不到数据返回 0
     */
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    /* 清空输入输出缓冲区 */
    if (0 > tcflush(fd_uart, TCIOFLUSH))
    {
        printf("fail to flush the buffer\n");
        return -3;
    }

    /* 写入配置,使配置生效 */
    if (0 > tcsetattr(fd_uart, TCSANOW, &newtio))
    {
        printf("fail to set new attribution of terminal\n");
        return -4;
    }

    return 0;
}

/* 异步 i/o 初始化函数 */
static void async_io_init(void)
{
    struct sigaction    sigatn;
    int                 flag;

    /* 使能异步 i/o,获取当前进程状态,并开启当前进程异步通知功能 */
    flag = fcntl(fd_uart, F_GETFL);
    flag |= O_ASYNC;
    fcntl(fd_uart, F_SETFL, flag);

    /* 设置异步 i/o 的所有者,将本应用程序进程号告诉内核 */
    fcntl(fd_uart, F_SETOWN, getpid());

    /* 指定实时信号 SIGRTMIN 作为异步 i/o 通知信号 */
    fcntl(fd_uart, F_SETSIG, SIGRTMIN);

    /* 为实时信号 SIGRTMIN 注册信号处理函数
     * 当串口有数据可读时,会跳转到 io_handler 函数
     */
    sigatn.sa_sigaction = io_handler;
    sigatn.sa_flags = SA_SIGINFO;

    /* 初始化信号集合为空 */
    sigemptyset(&sigatn.sa_mask);

    /* sigaction 的功能是为信号指定相关的处理程序,但是它在执行信号处理程序时
     * 会把当前信号加入到进程的信号屏蔽字中,从而防止在进行信号处理期间信号丢失
     */
    sigaction(SIGRTMIN, &sigatn, NULL);
}

/* 信号处理函数,当串口有数据可读时,会跳转到该函数执行 */
static void io_handler(int sig, siginfo_t *info, void *context)
{
    unsigned char   buf[10];
    int             ret;
    int             n;

    memset(buf, 0x0, sizeof(buf));

    if(SIGRTMIN != sig)
    {
        return;
    }

    /* 判断串口是否有数据可读 */
    if (POLL_IN == info->si_code)
    {
        ret = read(fd_uart, buf, 8);
        printf("[ ");
        for (n = 0; n < ret; n++)
        {
            printf("0x%hhx ", buf[n]);
        }
        printf("]\n");
    }
}

static void program_usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);

	printf("-D device\n");
    printf("-T type\n");
    printf("-b brate\n");
    printf("-d dbit\n");
    printf("-p parity\n");
    printf("-s sbit\n");
    printf("-h help\n");

    return;

}

4.串口测试

硬件连接如下:

[imx6ull]Linux下TTY-串口编程

使用tftp命令把我们交叉编译好的可执行文件下载到我们的开发板上,并赋予可执行权限,然后运行此程序,可以看见我们的参数提示:

[imx6ull]Linux下TTY-串口编程

打开串口调试工具后通过参数-D选择对应的串口终端与写功能来发送数据:

[imx6ull]Linux下TTY-串口编程

然后可以在串口调试工具中接收到我们所发送的十六进制信息:

[imx6ull]Linux下TTY-串口编程

然后我们执行程序来接收数据,使用程序阻塞监听然后发送十六进制的数据后如下:

[imx6ull]Linux下TTY-串口编程
[imx6ull]Linux下TTY-串口编程
成功的我们通过TTY串口编程发送和接收到了数据,成功完成了串口编程。文章来源地址https://www.toymoban.com/news/detail-402910.html

到了这里,关于[imx6ull]Linux下TTY-串口编程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于IMX6ULL的智能车载终端项目(代码开源)_imx6ull 开源 linux 项目 车载终端

    1.4 车载终端:杂项功能 车载终端是一个非常复杂且常用的装置,随着电车与自动驾驶技术的不断升级迭代,需求也与日俱增!我们往往需要在终端上附加许许多多其他的功能,比如:智能家居联动、车内环境监测、天气预报与网上浏览等! 作者强调:考虑到篇幅有限,作者

    2024年04月08日
    浏览(38)
  • IMX6ULL采用YOCTO构建嵌入式Linux系统

    切换至工程项目文件夹,如:/home/user/Linux/Yocto/fsl-release-yocto,在该文件夹下使用如下命令: 在本地环境变量中指定repo服务器地址,可以使用清华的镜像源进行更新,即将如下内容复制到你的~/.bashrc里: 切换至Yocto工作路径/home/user/Linux/Yocto/fsl-release-yocto,然后使用如下repo命

    2024年02月02日
    浏览(32)
  • 【IMX6ULL驱动开发学习】12.Linux驱动之设备树

    承接上一篇博客 【IMX6ULL驱动开发学习】11.驱动设计之面向对象_分层思想(学习设备树过渡部分) 代码获取: https://gitee.com/chenshao777/imx6-ull_-drivers 我后面将三个层合并了(实际上只有前两层),合并成一个dev_drv.c了,暂时没有加GPIO操作,只是个框架 合并前的代码在 11.butt

    2024年02月13日
    浏览(34)
  • 【IMX6ULL驱动开发学习】11.Linux之SPI驱动

    参考:驱动程序开发:SPI设备驱动_spi驱动_邓家文007的博客-CSDN博客 目录 一、SPI驱动简介 1.1 SPI架构概述 1.2 SPI适配器(控制器)数据结构 1.2 SPI设备数据结构 1.3 SIP设备驱动 1.4 接口函数  二、SPI驱动模板 SPI驱动框架和I2C驱动框架是十分相似的,不同的是因为SPI是通过片选引

    2024年02月11日
    浏览(37)
  • 嵌入式Linux实战开发之项目总体概述(基于IMX6ULL)

    现在越来越多的智能设备融入到我们的生活,然而,丰富的设备之下也是有一套基本的运行框架,只是根据不同的需求增减相应的功能从而满足自身的需要。而电子产品量产工具项目,就是实现基础的功能,可以作为一个基础的模版,学成之后便可进行扩展,根据自身需求,

    2024年02月13日
    浏览(44)
  • iMX6ULL驱动开发 | 让imx6ull开发板支持usb接口FC游戏手柄

    手边有一闲置的linux开发板iMX6ULL一直在吃灰,不用来搞点事情,总觉得对不住它。业余打发时间就玩起来吧,总比刷某音强。从某多多上买来一个usb接口的游戏手柄,让开发板支持以下它,后续就可以接着在上面玩童年经典游戏啦。  我使用的是正点原子的I.MX6U-ALPHA 开发板,

    2024年02月14日
    浏览(39)
  • 【IMX6ULL驱动开发学习】08.IMX6ULL通过GPIO子系统函数点亮LED

    通过GPIO子系统函数点亮LED 1、GPIO子系统函数 1.1 确定 led 的GPIO标号,查看内核中的gpiochip 查看 gpiochip ,以正点原子的IMX6ULL阿尔法开发板为例 查看原理图,发现led接的引脚是 GPIO1_IO3,对应 /sys/kernel/debug/gpio 中的 gpiochip0 组,gpiochip0 组从0开始算起, 所以 GPIO1_IO3 对应的标号就

    2024年02月10日
    浏览(62)
  • 【IMX6ULL驱动开发学习】22.IMX6ULL开发板读取ADC(以MQ-135为例)

    IMX6ULL一共有两个ADC,每个ADC都有八个通道,但他们共用一个ADC控制器 在imx6ull.dtsi文件中已经帮我们定义好了adc1的节点部分信息 注意 num-channels = 2; ,这个表示指定使用ADC1的两个通道,即通道1和通道2 如果你要使用多个ADC通道,修改这个值即可 配置ADC引脚的 pinctrl ,在自己的

    2024年02月12日
    浏览(39)
  • 正点原子 imx6ull linux 更新内核与设备树 通过nfs挂载

    通过修改linux正点原子imx6ull设备树,防止led灯占用 编译内核与设备树 1.将原子的镜像解压到ubuntu虚拟机 2.使用tar -xf linux…解压到某一文件夹 3.修改设备树文件(imx6ull-alientek-emmc.dts) 在leds下面添加 4.编译设备树 在源码 根目录 下 (非必须)4-2.编译内核 在源码 根目录 下 如果已经

    2024年02月08日
    浏览(34)
  • 【IMX6ULL驱动开发学习】05.IMX6ULL驱动开发_编写第一个hello驱动【熬夜肝】

    经过以下四个步骤,终于可以开始驱动开发了 01.安装交叉编译环境【附下载地址】 02.IMX6ULL烧写Linux系统 03.设置IMX6ULL开发板与虚拟机在同一网段 04.IMX6ULL开发板与虚拟机互传文件 一、获取内核、编译内核 二、创建vscode工作区,添加内核目录和个人目录 三、了解驱动程序编写

    2024年02月06日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包