您好,欢迎来到飒榕旅游知识分享网。
搜索
您的当前位置:首页imx6ull.Linux4.15平台下开发 记录2(platform、Pinctrl、GPIO子系统及自带驱动)

imx6ull.Linux4.15平台下开发 记录2(platform、Pinctrl、GPIO子系统及自带驱动)

来源:飒榕旅游知识分享网
一、驱动的分离与分层
主机驱动                      :                   核心层                   :              设备驱动
驱动1                           :                                                :                设备1
驱动2                           :                                                :                设备2
驱动3                           :        < ——   总线 ——>         :                设备3
驱动n                           :                                                   :                  设备4         
半导体厂商提出                                                               设备厂商提供
目的:各层处理不同事务
二、PlatForm平台
实现设备和驱动之间匹配,platform属于bus_type实例(deivers/base/platform.c)
  1. 在platform_match中有四种驱动、设备匹配方法:(1)of_driver_match_device(dev,drv)
(2) acpi_driver_match_device(dev,drv)   (3)platform_match_id(pdrv->id_table,pdev) 
(4) strcmp(pdev_name,drv->name)
  1. platform_driver结构体platform驱动(include/linux/platform_device.h)
当驱动与设备匹配,执行int (*probe)(struct platform_device *)
(1) of_match_table 采用设备树时使用的匹配表 of_device_id在include/linux/mod_devicetable.h
(2) int platform_driver_register(strucy platform_driver *) 向内核注册platform驱动、
  1. platform设备
(1)platform_device 表示platform设备(如果使用设备树,无需使用platform_device)
(2)在使用设备树中创建设备节点: 注意compatible属性需要与驱动.compatible一致
(3)MODULE_DEVICE_TABLE(of,leds_of_match)声明了leds_of_match设备匹配表
eg:向设备树中添加pinctrl节点模板
(1)创建对应节点:在imx6ull_borrow_emmc.dts中的iomuxc节点中的“imx6ul-evk”子节点下添加pinctrl_text:textgrp{/具体节点};
(2)添加“fsl,pins”属性:pinctrl_text:textgrp{fsl,pins=<设备使用pin信息>;};
(3)在“fsl,pins”属性中添加pin配置信息:pinctrl_text:textgrp{fsl,pins=<MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config>};
其中config具体设置值
三、pinctrl子系统
1.工作内容:(1)获取设备树中pin信息 (2)根据获取pin信息设备pin的复用功能 (3)根据获取pin信息设置pin的电气属性
(4)目录drviers/pinctrl
2.pin配置信息:imx6ull.dtsi文件内iomuxc, imx6ull_borrow_emmc.dts文件中&iomuxc向iomuxc追加的数据
eg: 设置GPIO1_IO19为普通I/O
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19    0x17059
UART1_RTS_B的复用功能为 GPIO1_IO19, 电气属性:0x17059
在imx6ul-pinfunc.h中定义MX6UL_PAD_UART1_RTS_B_GPIO1_IO19(#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0)
(1) 在《imx6ul参考手册》30.5.32(page.1342)中描述SW_MUX_CTL_PAD_UART1_RTS_B sw mux control
(2) 0x0090是sw mux control的偏移地址 (mux_reg) address:20E_0000H base + 90h offset = 20E_0090h
(3) 在《imx6ul参考手册》30.5.195(page:1608) SW_PAD_CTL_PAD_UART1_RTS_B sw pad control reg 
定义了UART1_RTS_B的conf_reg偏移地址(31ch),Address:20E_0000h base+31ch offset = 20E_031CH
(4) 0x0000:input_reg,GPIO1_19无input_reg寄存器,无需设置;
(5) 0x05:根据SW MUX control表设置GPIO1_IO19(0101 ALT5)
(6) 0x00: input_reg的值,无效
(7) 0x17059是SW_PAD_Control的值
四、 gpio子系统
1.目的:方便用户开发gpio,用户在设备树中添加gpio相关信息后,可使用gpio子系统提供的API操作GPIO
2.gpio子系统驱动:(1)设备树中的GPIO信息 
eg:将UART1_RTS_B复用为SD卡检测管脚
1)在imx6ull_borrow_emmc.dts中设置PIN的电气属性: pinctrl_hog_1:hoggrp-1{ fsl,pins=<MX6UL_PAD_UART_RTS_B__GPIO1_IO19 0x17059>;};
2)因为SD连接在usdhc1接口,在imx6ull_borrow_emmc.dts中找到“usdhc1”的节点(&usdhc1)cd-gpios=<gpio1 19 GPIO_ACTIVE_LOW>; 描述了SD卡的CD引脚使用了哪个I/O
3.驱动简介:在linux内核问价搜索“fsl,imx6ul-gpio”,"fsl,imx35-gpio"查找GPIO文件(drivers/gpio/gpio_mxc.c)
4.API接口: (1)申请GPIO管脚,int gpio_request(unsigned ,const char *)
(2)释放GPIO : void gpio_free(unsigned gpio)
(3)设置gpio输入:int gpio_direction_input(unsigned gpio)
(4)设置GPIO输出:int gpio_direction_output(unsigned,int s) int s 是默认值
(5)读gpio值:gpio_get_value(unsigned gpio)
(6)设置gpio的值: gpio_set_value(unsigned , int value)
5.设备树中GPIO模板
(1)在跟文件“/”创建text 设备子节点,text{节点内容};
(2)增加pinctrl信息:text{pinctrl-names ="default" ; pinctrl-0=<&pinctrl-text>/*(表示pinctrl-0用的I/O属性在pinctrl-text节点中定义)*/;};
6.应用:重点检查PIN是否被其他外设使用。在imx6ull-borrow-emmc.dts中(1)检查pinctrl设置(2)如果设置为gpio,检查是否被其他外设占用
五、自带驱动:1.配置menuconfig 2.修改设备树
六、UART5 使用
1.设备树: /*add by dy at 21-08-16 for add uart4*/
        pinctrl_uart4: uart4grp {
            fsl,pins = <
                MX6UL_PAD_UART4_TX_DATA__UART4_DCE_TX 0x1b0b1
                MX6UL_PAD_UART4_RX_DATA__UART4_DCE_RX 0x1b0b1
            >;    
        };
/*add by dy at 21-08-16 for add uart4*/
&uart4 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_uart4>;
    status = "okay";
};
2.App:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>    //定义数据类型,如 ssiz e_t off_t 等
#include <sys/stat.h>    //文件状态
#include <fcntl.h>        //文件控制定义
#include <termios.h>    //终端I/O
#include <errno.h>        //与全局变量 errno 相关的定义
#include <getopt.h>        //处理命令行参数
#include <string.h>        //字符串操作
#include <time.h>        //时间
#include <sys/select.h>    //select函数
int setOpt(int fd, int nSpeed, int nBits, int nParity, int nStop)
{
    struct termios newtio, oldtio;
    // 保存测试现有串口参数设置,在这里如果串口号等出错,会有相关的出错信息
    if (tcgetattr(fd, &oldtio) != 0)
    {
        perror("SetupSerial 1");
        return -1;
    }
    bzero(&newtio, sizeof(newtio));        //新termios参数清零
    newtio.c_cflag |= CLOCAL | CREAD;    //CLOCAL--忽略 modem 控制线,本地连线, 不具数据机控制功能, CREAD--使能接收标志
    // 设置数据位数
    newtio.c_cflag &= ~CSIZE;    //清数据位标志
    switch (nBits)
    {
        case 7:
            newtio.c_cflag |= CS7;
        break;
        case 8:
            newtio.c_cflag |= CS8;
        break;
        default:
            fprintf(stderr, "Unsupported data size\n");
            return -1;
    }
    // 设置校验位
    switch (nParity)
    {
        case 'o':
        case 'O':                     //奇校验
            newtio.c_cflag |= PARENB;
            newtio.c_cflag |= PARODD;
            newtio.c_iflag |= (INPCK | ISTRIP);
            break;
        case 'e':
        case 'E':                     //偶校验
            newtio.c_iflag |= (INPCK | ISTRIP);
            newtio.c_cflag |= PARENB;
            newtio.c_cflag &= ~PARODD;
            break;
        case 'n':
        case 'N':                    //无校验
            newtio.c_cflag &= ~PARENB;
            break;
        default:
            fprintf(stderr, "Unsupported parity\n");
            return -1;
    }
    // 设置停止位
    switch (nStop)
    {
        case 1:
            newtio.c_cflag &= ~CSTOPB;
        break;
        case 2:
            newtio.c_cflag |= CSTOPB;
        break;
        default:
            fprintf(stderr,"Unsupported stop bits\n");
            return -1;
    }
    // 设置波特率 2400/4800/9600/19200/38400/57600/115200/230400
    switch (nSpeed)
    {
        case 2400:
            cfsetispeed(&newtio, B2400);
            cfsetospeed(&newtio, B2400);
            break;
        case 4800:
            cfsetispeed(&newtio, B4800);
            cfsetospeed(&newtio, B4800);
            break;
        case 9600:
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
        case 19200:
            cfsetispeed(&newtio, B19200);
            cfsetospeed(&newtio, B19200);
            break;
        case 38400:
            cfsetispeed(&newtio, B38400);
            cfsetospeed(&newtio, B38400);
            break;
        case 57600:
            cfsetispeed(&newtio, B57600);
            cfsetospeed(&newtio, B57600);
            break;
        case 115200:
            cfsetispeed(&newtio, B115200);
            cfsetospeed(&newtio, B115200);
            break;
        case 230400:
            cfsetispeed(&newtio, B230400);
            cfsetospeed(&newtio, B230400);
            break;
        default:
            printf("\tSorry, Unsupported baud rate, set default 9600!\n\n");
            cfsetispeed(&newtio, B9600);
            cfsetospeed(&newtio, B9600);
            break;
    }
    // 设置read读取最小字节数和超时时间
    newtio.c_cc[VTIME] = 1;     // 读取一个字符等待1*(1/10)s
    newtio.c_cc[VMIN] = 1;        // 读取字符的最少个数为1
    tcflush(fd,TCIFLUSH);         //清空缓冲区
    if(tcsetattr(fd, TCSANOW, &newtio) != 0)    //激活新设置
    {
        perror("SetupSerial 3");
        return -1;
    }
    printf("Serial set done!\n");
    return 0;
}
/**@brief 串口读取函数
* @param[in]  fd         打开的串口文件句柄
* @param[in]  *rcv_buf 接收缓存指针
* @param[in]  data_len    要读取数据长度
* @param[in]  timeout     接收等待超时时间,单位ms
* @return     返回设置结果
* - >0      设置成功
* - 其他      读取超时或错误
*/
int UART_Recv(int fd, char *rcv_buf, int data_len, int timeout)
{
    int len=0, fs_sel=0;
    fd_set fs_read;
    struct timeval time;
    time.tv_sec = timeout / 1000;              //set the rcv wait time
    time.tv_usec = timeout % 1000 * 1000;    //100000us = 0.1s
    FD_ZERO(&fs_read);        //每次循环都要清空集合,否则不能检测描述符变化
    FD_SET(fd, &fs_read);    //添加描述符
    // 超时等待读变化,>0:就绪描述字的正数目, -1:出错, 0 :超时
    fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);
    printf("fs_sel = %d\n", fs_sel);
    if(fs_sel)
    {
        len = read(fd, rcv_buf, data_len);
        return len;
    }
    else
    {
        printf("Sorry,rev tout! \n");
        return -1;
    }
}
/**@brief 串口发送函数
* @param[in]  fd            打开的串口文件句柄
* @param[in]  *send_buf     发送数据指针
* @param[in]  data_len     发送数据长度
* @return     返回结果
* - data_len    成功
* - -1            失败
*/
int UART_Send(int fd, char *send_buf, int data_len)
{
    ssize_t ret = 0;
    ret = write(fd, send_buf, data_len);
    if (ret == data_len)
    {
        printf("send data is %s\n", send_buf);
        return ret;
    }
    else
    {
        printf("write device error\n");
        tcflush(fd,TCOFLUSH);
        return -1;
    }
}
int main(int argc,char *argv[])
{
    int fd = 0;
    char buf[16]="123456hellotty\n";
    char rbuf[16]={0x00};
    int len=0;
    
    fd =  open("/dev/ttymxc3",O_RDWR | O_NOCTTY | O_NDELAY );
    if(fd < 0)
    {
        perror("/dev/ttymxc3");
        printf("/dev/ttymxc3 open err \n");
        return -1;
    }
    printf("open uart4 \n");
    // 设置串口阻塞, 0:阻塞, FNDELAY:非阻塞
    if (fcntl(fd, F_SETFL, 0) < 0)    //阻塞,即使前面在open串口设备时设置的是非阻塞的
    {
        printf("fcntl failed!\n");
    }
    printf("fcntl=%d\n", fcntl(fd, F_SETFL, 0));
    
    if (isatty(fd) == 0)
    {
        printf("standard input is not a terminal device\n");
        close(fd);
        return -1;
    }
    else
    {
        printf("is a tty success!\n");
    }
    printf("fd-open=%d\n", fd);
     // 设置串口参数
    if (setOpt(fd, 115200, 8, 'N', 1)== -1)    //设置8位数据位、1位停止位、无校验
    {
        fprintf(stderr, "Set opt Error\n");
        close(fd);
        exit(1);
    }
    tcflush(fd, TCIOFLUSH);    //清掉串口缓存
    fcntl(fd, F_SETFL, 0);    //串口阻塞
    while(1)    //循环读取数据
    {
        len = UART_Recv(fd, rbuf, sizeof(rbuf), 10000);
        if(len ==-1)
        {
            UART_Send(fd, buf,sizeof(buf) );
        }
        else
        {
            UART_Send(fd, rbuf,sizeof(rbuf) );
            int ii=0;
            for(ii=0;ii<len;ii++)
            {
                printf("%02x ",rbuf[ii]);
            }
            memset(rbuf,0x00,sizeof(rbuf));
            len=0;
        }
    
    }
    
    close(fd);
    return 0;
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- sarr.cn 版权所有 赣ICP备2024042794号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务