精华内容
下载资源
问答
  • 当验证接收数据时,通过PC上的串口助手,通过串口板CH340发送数据到开发板,观察如果接收到数据,证明Linux板 RX正常 当验证发送数据时,PC上的串口助手,会监视数据到来。并且过程中,一般会伴随串口板上数据指示灯...

    在工作中经常要跟串口打交道,假如有同事给过来一块硬件板,系统也是别人做的,我们只用来在上面开发应用程序,串口的验证更加重要。

    大概思路如下图

    在这里插入图片描述

    在开发板上跑一套应用程序,可以发送数据,可以接收数据。

    当验证接收数据时,通过PC上的串口助手,通过串口板CH340发送数据到开发板,观察如果接收到数据,证明Linux板 RX正常

    当验证发送数据时,PC上的串口助手,会监视数据到来。并且过程中,一般会伴随串口板上数据指示灯闪烁。

    代码从网上参考的

    //串口相关的头文件  
    #include<stdio.h>      /*标准输入输出定义*/  
    #include<stdlib.h>     /*标准函数库定义*/  
    #include<unistd.h>     /*Unix 标准函数定义*/  
    #include<sys/types.h>   
    #include<sys/stat.h>     
    #include<fcntl.h>      /*文件控制定义*/  
    #include<termios.h>    /*PPSIX 终端控制定义*/  
    #include<errno.h>      /*错误号定义*/  
    #include<string.h>  
       
       
    //宏定义  
    #define FALSE  -1  
    #define TRUE   0  
       
    /*******************************************************************
    * 名称:                  UART0_Open
    * 功能:                打开串口并返回串口设备文件描述
    * 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2)
    * 出口参数:        正确返回为1,错误返回为0
    *******************************************************************/  
    int UART0_Open(int fd,char* port)  
    {  
         
        fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);  
        if (FALSE == fd)  
        {  
            perror("Can't Open Serial Port");  
            return(FALSE);  
        }  
        //恢复串口为阻塞状态                                 
        if(fcntl(fd, F_SETFL, 0) < 0)  
        {  
            printf("fcntl failed!\n");  
            return(FALSE);  
        }       
        else  
        {  
            printf("fcntl=%d\n",fcntl(fd, F_SETFL,0));  
        }  
        //测试是否为终端设备      
        if(0 == isatty(STDIN_FILENO))  
        {  
            printf("standard input is not a terminal device\n");  
            return(FALSE);  
        }  
        else  
        {  
            printf("isatty success!\n");  
        }                
        printf("fd->open=%d\n",fd);  
        return fd;  
    }  
    /*******************************************************************
    * 名称:                UART0_Close
    * 功能:                关闭串口并返回串口设备文件描述
    * 入口参数:        fd    :文件描述符     port :串口号(ttyS0,ttyS1,ttyS2)
    * 出口参数:        void
    *******************************************************************/  
       
    void UART0_Close(int fd)  
    {  
        close(fd);  
    }  
       
    /*******************************************************************
    * 名称:                UART0_Set
    * 功能:                设置串口数据位,停止位和效验位
    * 入口参数:        fd        串口文件描述符
    *                              speed     串口速度
    *                              flow_ctrl   数据流控制
    *                           databits   数据位   取值为 7 或者8
    *                           stopbits   停止位   取值为 1 或者2
    *                           parity     效验类型 取值为N,E,O,,S
    *出口参数:          正确返回为1,错误返回为0
    *******************************************************************/  
    int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)  
    {  
         
        int   i;  
        int   status;  
        int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};  
        int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};  
               
        struct termios options;  
         
        /*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.
        */  
        if( tcgetattr( fd,&options)  !=  0)  
        {  
            perror("SetupSerial 1");      
            return(FALSE);   
        }  
        
        //设置串口输入波特率和输出波特率  
        for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)  
        {  
            if  (speed == name_arr[i])  
            {               
                cfsetispeed(&options, speed_arr[i]);   
                cfsetospeed(&options, speed_arr[i]);    
            }  
        }       
         
        //修改控制模式,保证程序不会占用串口  
        options.c_cflag |= CLOCAL;  
        //修改控制模式,使得能够从串口中读取输入数据  
        options.c_cflag |= CREAD;  
        
        //设置数据流控制  
        switch(flow_ctrl)  
        {  
            
            case 0 ://不使用流控制  
                  options.c_cflag &= ~CRTSCTS;  
                  break;     
            
            case 1 ://使用硬件流控制  
                  options.c_cflag |= CRTSCTS;  
                  break;  
            case 2 ://使用软件流控制  
                  options.c_cflag |= IXON | IXOFF | IXANY;  
                  break;  
        }  
        //设置数据位  
        //屏蔽其他标志位  
        options.c_cflag &= ~CSIZE;  
        switch (databits)  
        {    
            case 5    :  
                         options.c_cflag |= CS5;  
                         break;  
            case 6    :  
                         options.c_cflag |= CS6;  
                         break;  
            case 7    :      
                     options.c_cflag |= CS7;  
                     break;  
            case 8:      
                     options.c_cflag |= CS8;  
                     break;    
            default:     
                     fprintf(stderr,"Unsupported data size\n");  
                     return (FALSE);   
        }  
        //设置校验位  
        switch (parity)  
        {    
            case 'n':  
            case 'N': //无奇偶校验位。  
                     options.c_cflag &= ~PARENB;   
                     options.c_iflag &= ~INPCK;      
                     break;   
            case 'o':    
            case 'O'://设置为奇校验      
                     options.c_cflag |= (PARODD | PARENB);   
                     options.c_iflag |= INPCK;               
                     break;   
            case 'e':   
            case 'E'://设置为偶校验    
                     options.c_cflag |= PARENB;         
                     options.c_cflag &= ~PARODD;         
                     options.c_iflag |= INPCK;        
                     break;  
            case 's':  
            case 'S': //设置为空格   
                     options.c_cflag &= ~PARENB;  
                     options.c_cflag &= ~CSTOPB;  
                     break;   
            default:    
                     fprintf(stderr,"Unsupported parity\n");      
                     return (FALSE);   
        }   
        // 设置停止位   
        switch (stopbits)  
        {    
            case 1:     
                     options.c_cflag &= ~CSTOPB; break;   
            case 2:     
                     options.c_cflag |= CSTOPB; break;  
            default:     
                           fprintf(stderr,"Unsupported stop bits\n");   
                           return (FALSE);  
        }  
         
        //修改输出模式,原始数据输出  
        options.c_oflag &= ~OPOST;  
        
        options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);  
        //options.c_lflag &= ~(ISIG | ICANON);  
         
        //设置等待时间和最小接收字符  
        options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */    
        options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */  
         
        //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读  
        tcflush(fd,TCIFLUSH);  
         
        //激活配置 (将修改后的termios数据设置到串口中)  
        if (tcsetattr(fd,TCSANOW,&options) != 0)    
        {  
            perror("com set error!\n");    
            return (FALSE);   
        }  
        return (TRUE);   
    }  
    /*******************************************************************
    * 名称:                UART0_Init()
    * 功能:                串口初始化
    * 入口参数:        fd       :  文件描述符    
    *               speed  :  串口速度
    *                              flow_ctrl  数据流控制
    *               databits   数据位   取值为 7 或者8
    *                           stopbits   停止位   取值为 1 或者2
    *                           parity     效验类型 取值为N,E,O,,S
    *                       
    * 出口参数:        正确返回为1,错误返回为0
    *******************************************************************/  
    int UART0_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)  
    {  
        int err;  
        //设置串口数据帧格式  
        if (UART0_Set(fd,speed,0,8,1,'N') == FALSE)  
        {                                                           
            return FALSE;  
        }  
        else  
        {  
            return  TRUE;  
        }  
    }  
       
    /*******************************************************************
    * 名称:                  UART0_Recv
    * 功能:                接收串口数据
    * 入口参数:        fd                  :文件描述符     
    *                              rcv_buf     :接收串口中数据存入rcv_buf缓冲区中
    *                              data_len    :一帧数据的长度
    * 出口参数:        正确返回为1,错误返回为0
    *******************************************************************/  
    int UART0_Recv(int fd, char *rcv_buf,int data_len)  
    {  
        int len,fs_sel;  
        fd_set fs_read;  
         
        struct timeval time;  
         
        FD_ZERO(&fs_read);  
        FD_SET(fd,&fs_read);  
         
        time.tv_sec = 10;  
        time.tv_usec = 0;  
         
        //使用select实现串口的多路通信  
        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);  
            printf("I am right!(version1.2) len = %d fs_sel = %d\n",len,fs_sel);  
            return len;  
        }  
        else  
        {  
            printf("Sorry,I am wrong!");  
            return FALSE;  
        }       
    }  
    /********************************************************************
    * 名称:                  UART0_Send
    * 功能:                发送数据
    * 入口参数:        fd                  :文件描述符     
    *                              send_buf    :存放串口发送数据
    *                              data_len    :一帧数据的个数
    * 出口参数:        正确返回为1,错误返回为0
    *******************************************************************/  
    int UART0_Send(int fd, char *send_buf,int data_len)  
    {  
        int len = 0;  
         
        len = write(fd,send_buf,data_len);  
        if (len == data_len )  
        {  
            printf("send data is %s\n",send_buf);
            return len;  
        }       
        else     
        {  
                     
            tcflush(fd,TCOFLUSH);  
            return FALSE;  
        }  
         
    }  
       
       
    int main(int argc, char **argv)  
    {  
        int fd;                            //文件描述符  
        int err;                           //返回调用函数的状态  
        int len;                          
        int i;  
        char rcv_buf[100];         
        char send_buf[20]="data send";
        if(argc != 3)  
        {  
            printf("Usage: %s /dev/ttySn 0(send data)/1 (receive data) \n",argv[0]);  
            return FALSE;  
        }  
        fd = UART0_Open(fd,argv[1]); //打开串口,返回文件描述符  
        do
        {  
            err = UART0_Init(fd,115200,0,8,1,'N');  
            printf("Set Port Exactly!\n");  
        }while(FALSE == err || FALSE == fd);  
         
        if(0 == strcmp(argv[2],"0"))  
        {  
            for(i = 0;i < 10;i++)  
            {  
                 len = UART0_Send(fd,send_buf,10);  
                if(len > 0)  
                    printf(" %d time send %d data successful\n",i,len);  
                else  
                    printf("send data failed!\n");  
                                
                sleep(2);  
            }  
            UART0_Close(fd);               
        }  
        else  
        {                                        
            while (1) //循环读取数据  
            {    
                len = UART0_Recv(fd, rcv_buf,99);  
                  if(len > 0)  
                {  
                    rcv_buf[len] = '\0';  
                    printf("receive data is %s\n",rcv_buf);  
                    printf("len = %d\n",len);  
                }  
                else  
                {  
                    printf("cannot receive data\n");  
                }  
                sleep(2);  
            }              
            UART0_Close(fd);   
        }  
    } 
    
    

    可以将上述代码交叉编译到linux平台中,分别执行接收、发送

    至此,可以从软件的角度验证串口某一功能。Linux、Android平台均适用。

    展开全文
  • Linux串口编程分析

    2013-11-13 21:14:48
    串口是计算机上一种非常通用设备通信的协议,常用PC机上包含的是RS232规格的串口。在串口编程中,比较重要的是串口的设置,我们要设置的部分包括波特...主要讲关于如何让用C语言实现这些东西的设置和一些通用的设置代码
  • Linux下的串口助手(可以调AT指令)

    热门讨论 2011-11-30 09:50:46
    Linux下的串口助手,自己写的类通用信不好就改为使用的第三方类.可以发AT指令.代码注释也比较多. 1:串口打开格式一般为8N1(8位数据位,无校验位,1位的停止位)无流控制; 2:如果想让数据以16进制显示,请勾选HEX; 3:...
  • win7和linux串口通用QT代码 QT画图函数使用,多线程使用
  • 串口 ---linux-3.5.3

    2012-11-23 01:10:11
    刚开始看Linux 感觉不像之前vxworks代码那些东西。linux更讲究结构体的设计和使用,类似C++对象的继承什么的。   设计才是真思维。堆代码完全不是问题,设计好才是关键。     UART是指Universal ...
    刚开始看Linux 感觉不像之前vxworks代码那些东西。linux更讲究结构体的设计和使用,类似C++对象的继承什么的。
     
    设计才是真思维。堆代码完全不是问题,设计好才是关键。
     
     
    UART是指Universal Asynchronous Receiver Transmitter,通用异步收发器。主要包含发送模块、接收模块、波特率发生器模块、数据存储模块和总线接口模块。serial指串行,一般我们常用的就是异步串行通信接口,用于CPU和外设之前的通信。
     
     

    struct uart_port {  spinlock_t  lock;   /* port lock */  unsigned long  iobase;   /* in/out[bwl] */  unsigned char __iomem *membase;  /* read/write[bwl] */  unsigned int  (*serial_in)(struct uart_port *, int);  void   (*serial_out)(struct uart_port *, int, int);  void   (*set_termios)(struct uart_port *,                    struct ktermios *new,                    struct ktermios *old);  int   (*handle_irq)(struct uart_port *);  void   (*pm)(struct uart_port *, unsigned int state,           unsigned int old);  void   (*handle_break)(struct uart_port *);  unsigned int  irq;   /* irq number */  unsigned long  irqflags;  /* irq flags  */  unsigned int  uartclk;  /* base uart clock */  unsigned int  fifosize;  /* tx fifo size */  unsigned char  x_char;   /* xon/xoff char */  unsigned char  regshift;  /* reg offset shift */  unsigned char  iotype;   /* io access style */  unsigned char  unused1;

     

     unsigned int  read_status_mask; /* driver specific */  unsigned int  ignore_status_mask; /* driver specific */  struct uart_state *state;   /* pointer to parent state */  struct uart_icount icount;   /* statistics */

     struct console  *cons;   /* struct console, if any */ #if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)  unsigned long  sysrq;   /* sysrq timeout */ #endif

     upf_t   flags;

     

     unsigned int  mctrl;   /* current modem ctrl settings */  unsigned int  timeout;  /* character-based timeout */  unsigned int  type;   /* port type */  const struct uart_ops *ops;  unsigned int  custom_divisor;  unsigned int  line;   /* port index */  resource_size_t  mapbase;  /* for ioremap */  struct device  *dev;   /* parent device */  unsigned char  hub6;   /* this should be in the 8250 driver */  unsigned char  suspended;  unsigned char  irq_wake;  unsigned char  unused[2];  void   *private_data;  /* generic platform data pointer */ };

     
     

    struct uart_state {  struct tty_port  port;

     int   pm_state;  struct circ_buf  xmit;

     struct uart_port *uart_port; };

     
    struct tty_port {
     struct tty_struct *tty;  /* Back pointer */
     const struct tty_port_operations *ops; /* Port operations */
     spinlock_t  lock;  /* Lock protecting tty field */
     int   blocked_open; /* Waiting to open */
     int   count;  /* Usage count */
     wait_queue_head_t open_wait; /* Open waiters */
     wait_queue_head_t close_wait; /* Close waiters */
     wait_queue_head_t delta_msr_wait; /* Modem status change */
     unsigned long  flags;  /* TTY flags ASY_*/
     unsigned char  console:1; /* port is a console */
     struct mutex  mutex;  /* Locking */
     struct mutex  buf_mutex; /* Buffer alloc lock */
     unsigned char  *xmit_buf; /* Optional buffer */
     unsigned int  close_delay; /* Close port delay */
     unsigned int  closing_wait; /* Delay for output */
     int   drain_delay; /* Set to zero if no pure time
             based drain is needed else
             set to size of fifo */
     struct kref  kref;  /* Ref counter */
    };
    
     
     

    struct uart_driver {  struct module  *owner;  const char  *driver_name;  const char  *dev_name;  int    major;  int    minor;  int    nr;  struct console  *cons;

     struct uart_state *state;  struct tty_driver *tty_driver; };

     
     
     
    一些基本接口:
    set_baud
    port_init 
    config
    write
    startup shutdown
    serial_init / exit
     
     
    展开全文
  • 代码提供了如下功能:Linux平台下的串口通讯通用类实现,Ymodem协议的完整实现。其中的shell脚本中包含有如下功能:检查U盘和挂载U盘等等
  • 介绍了在基于Linux的EPICS系统下开发RS232串口通信驱动程序的过程,并以BEPCII实验电子枪控制系统中使用的OMRON CQM1 PLC为实例,分析了针对该设备的EPICS驱动程序,包括其层次结构和具体实现代码.提出了利用已完成...
  • 相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设备树配置,当需要改变波特率时,就不用修改驱动,直接修改配置文件则可。设备树源文件扩展名为.dts(device tree source),一般放置在内核的"arch/arm64/...

                            Linux kernel下c语言解析dts

     

    一、inux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设备树配置,当需要改变波特率时,就不用修改驱动,直接修改配置文件则可。设备树源文件扩展名为.dts(device tree source),一般放置在内核的"arch/arm64/boot/dts/"目录内。设备树源文件的通用部分用.dtsi文件描述,一般用于描述SOC的内部外设信息,如CPU架构,主频等。差异部分用.dts文件描述,一般用于描述设备上的其它设备,如IIC设备,SPI设备。

     

    二、kernel下c语言解析dts实例学习。

    1、添加dts panel节点。

    	panel: panel {
    		compatible = "simple-panel";
    		backlight = <&backlight>;
    		power-supply = <&vcc_lcd>;
    		enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
    		prepare-delay-ms = <20>;
    		enable-delay-ms = <20>;
    		reg = <0Xff8ff600 0x800>;
    		status = "disabled";
    		rockchip,key_table =
    			<0xFF	KEY_POWER>,
    			<0xF4	KEY_MENU>,
    			<0xF6	KEY_BACK>,
    			<0xE9	KEY_HOME>,
    			<0xF1	KEY_ENTER>,
    			<0xEE	KEY_VOLUMEUP>,
    			<0xEB	KEY_VOLUMEDOWN>,
    			<0xE9	KEY_REPLY>,
    			<0xF3	KEY_UP>,
    			<0xEF	KEY_DOWN>,
    			<0xF2	KEY_LEFT>,
    			<0xF0	KEY_RIGHT>,
    			<0xFA	KEY_PLAY>,
    			<0xE6	KEY_PLAYCD>,			
    			<0xE7	KEY_STOP>,			
    			<0xEC	KEY_MUTE>,
    			<0xE5	KEY_1>,
    			<0xE4	KEY_2>,
    			<0xE3	KEY_3>,
    			<0xE2	KEY_4>,
    			<0xE1	KEY_5>,
    			<0xE0	KEY_6>,
    			<0xBF	KEY_7>,
    			<0xBE	KEY_8>,
    			<0xBD	KEY_9>,
    			<0xBB	KEY_0>,
    			<0xE8	KEY_APPSELECT>,
    			<0xBC	KEY_NUMERIC_STAR>,
    			<0xBA	KEY_NUMERIC_POUND>,
    			<0xB6	KEY_TRIGGER_DUALSCREEN>; 
    
    		display-timings {
    			native-mode = <&timing0>;
    
    			timing0: timing0 {
    				clock-frequency = <200000000>;
    				//hactive = <1536>;
    				//vactive = <2048>;
    				hactive = <3840>;
    				vactive = <2160>;
    				//hactive = <1920>;
    				//vactive = <1080>;
    				hfront-porch = <12>;
    				hsync-len = <16>;
    				hback-porch = <48>;
    				vfront-porch = <8>;
    				vsync-len = <4>;
    				vback-porch = <8>;
    				hsync-active = <0>;
    				vsync-active = <0>;
    				de-active = <0>;
    				pixelclk-active = <0>;
    			};
    		};
    
    		ports {
    			panel_in: endpoint {
    				remote-endpoint = <&edp_out>;
    			};
    		};
    	};

    2、c语言解析代码

    
    	struct device_node *panel_nd;//设备节点
    	struct property *proper;
    	struct device_node *entry;
    	struct device_node *timings_np;
    	int res=-1;
    	unsigned int regdata[2];
    	const char *str;
    	int val = 0;
    	int loop;
    	//u32 *phy_config;
    	struct rkxx_remote_key_table key_table[50];
    
    	/*获取设备树中的属性数据*/
    	/*1.获取设备节点:panel*/
    	panel_nd = of_find_node_by_path("/panel");
    	if(panel_nd == NULL)
    	{
    		printk("panel_nd node not find\r\n");
    	}
    	else
    	{
    		printk("panel_nd node find\r\n");
    	}
    
    	/*2.获取compatible属性内容*/
    	proper =  of_find_property(panel_nd,"compatible",NULL);
    	if(proper == NULL)
    	{
    		printk("panel_nd compatible property find failed\r\n");
    	}
    	else
    	{
    		printk("panel_nd compatible =%s\r\n",(char *)proper->value);
    	}
    	/*3.获取status属性内容*/
    	res = of_property_read_string(panel_nd,"status",&str);
    	if(res < 0)
    	{
    		printk("panel_nd status read failed\r\n");
    	}
    	else
    	{
    		printk("panel_nd status =%s\r\n",str);
    	}
    	
    	/*4.获取reg属性内容*/
    	res = of_property_read_u32_array(panel_nd,"reg",regdata,2);
    	if(res <0 )
    	{
    		printk("panel_nd reg property read failed\r\n");
    	}
    	else
    	{
    		printk("panel_nd regdata[0]=0x%x\r\n",regdata[0]);
    		printk("panel_nd regdata[1]=0x%x\r\n",regdata[1]);
    	}
    
    	if (of_get_property(panel_nd, "rockchip,key_table", &val))
    	{
    	    printk("panel_nd rockchip,rockchip,key_table val=%d\r\n",val);
    		of_property_read_u32_array(panel_nd, "rockchip,key_table",
    					   (u32 *)key_table, val / sizeof(u32));
    		for (loop=0; loop<(val / sizeof(u32)/2); loop++) {
    			printk("key_table[%d]=[0x%x,0x%x]\n", loop,
    					key_table[loop].scancode,key_table[loop].keycode);
    		}
    		
    	}
    
    	timings_np = of_find_node_by_name(panel_nd, "display-timings");
    	if (!timings_np) {
    		printk("%s: could not find timings_np node\n",
    			of_node_full_name(panel_nd));
    	}else
    		printk("finded timings_np node\n");
    
    	entry = of_parse_phandle(timings_np, "native-mode", 0);
    	
    	if (!of_property_read_u32(entry, "hback-porch", &val))
    		    printk("\nhback-porch=%d\n",val);
    
    	if (!of_property_read_u32(entry, "hfront-porch", &val))
    		    printk("\nhfront-porch=%d\n",val);
    
    	if (!of_property_read_u32(entry, "hactive", &val))
    			printk("\nhactive=%d\n",val);
    
    	if (!of_property_read_u32(entry, "hsync-len", &val))
    			printk("\nhsync-len=%d\n",val);
    
    	if (!of_property_read_u32(entry, "vback-porch", &val))
    			printk("\nvback-porch=%d\n",val);
    
    	if (!of_property_read_u32(entry, "vfront-porch", &val))
    			printk("\nvfront-porch=%d\n",val);	
    
    	if (!of_property_read_u32(entry, "vactive", &val))
    			printk("\nvactive=%d\n",val);	
    
    	if (!of_property_read_u32(entry, "vsync-len", &val))
    			printk("\nvsync-len=%d\n",val);	
    	
    	if (!of_property_read_u32(entry, "clock-frequency", &val))
    			printk("\nclock-frequency=%d\n",val);
    
    
    

     

    3、运行后看打印信息如下,解析ok。

    展开全文
  • 其中最后一个例子step 5 是个通用驱动,应用层打开驱动设备文件,如同用串口设备一样简单就能读取到usb数据,usb应用层包协议可以自己定。 因为linux驱动屏蔽硬件层,并且该设备不是什么标准设备,所以没有类似IIC...
  • 其中lock.c是文件锁的源代码,seri.c和seri.h是串口设置的源代码,read_seri.c是读串口的源代码,write_seri.c是写串口的源代码。 程序代码\chapter10 文件夹中包含了第10章的示例源程序。其中alarm_read.c是设置...
  • Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设备树配置,当需要改变波特率时,就不用修改驱动,直接修改配置文件则可。 ...

    概念

    Linux内核从3.x开始引入设备树的概念,用于实现驱动代码与设备信息相分离。相当于从驱动代码分离出来的配置文件,比如串口的波特率通过设备树配置,当需要改变波特率时,就不用修改驱动,直接修改配置文件则可。
    设备树源文件扩展名为.dts(device tree source),一般放置在内核的"arch/arm/boot/dts/"目录内。设备树源文件的通用部分用.dtsi文件描述,一般用于描述SOC的内部外设信息,如CPU架构,主频等。差异部分用.dts文件描述,一般用于描述设备上的其它设备,如IIC设备,SPI设备。

    dts基本语法

    设备节点:dts文件中描述一个设备信息的内容。比如IIC0。
    根节点:设备树文件开头会有’/’就是根节点,每个设备树文件只有一个根节点,
    每个节点都有一堆属性,有的可以用户自定义,有的为标准属性。
    compatible:“兼容性”属性,用于将设备和驱动绑定起来,一般驱动程序都会有一个OF匹配,匹配相等,则可使用这个驱动。
    module:属性也是字符串,描述模块信息,如名字什么的。
    status:设备状态,可以是“okey”、“disable”,“fail”、“fail-sss”
    reg:一般reg都是和地址相关的内容,起始地址和地址长度。
    #address-cells:描述reg属性中的起始地址占用字长。
    #size-cells:描述reg属性中的地址长度所占用的字长。
    举例:

    apb {
    	compatible = "simple-bus";
    	#address-cells = <1>; //父节点的起始地址字长1,即uart0的reg属性中,第一个字为起始地址。
    	#size-cells = <1>;//父节点的地址长度字长为1。
    	ranges;
    	uart0: serial@f0024000 {//节点标签:节点名@设备地址或者寄存器首地址
    		compatible = "atmel,at91sam9260-usart";//<厂商,驱动名>
    		reg = <0xf0024000 0x100>;//uart0寄存器的起始地址为0xf002c000,可以在SOC的datasheet中查看到。
    		interrupts = <16 IRQ_TYPE_LEVEL_HIGH 5>;
    		pinctrl-names = "default";
    		pinctrl-0 = <&pinctrl_uart0>;
    		clocks = <&uart0_clk>;
    		clock-names = "usart";
    		status = "disabled";//状态为disable
    	};
    

    根节点中有两个特殊节点aliases(别名)和chosen。

    设备树在系统中的体现

    在根文件系统中/proc/device-tree目录下,包括根节点’/’的所有属性和子节点。
    在这里插入图片描述
    在设备树存放目录arch/arm/boot/dts/中搜索model内容,可以找到对应的.dts文件入口。可以看到model和compatible的属性值完全相同。
    在这里插入图片描述

    设备树和datasheet对应

    以下以调试串口打印配置为例,学习如何查看设备树和datasheet如何对应的。
    ① 通过原理图,可以知道调试串口连接的PB30和PB31.
    ② 在datasheet中,引脚功能描述,可以看到PB30和PB31功能为调试打印DBGU。
    在这里插入图片描述
    ③ 在datasheet的”Memory Mapping”中,可以看到DBGU的起始地址0Xffff ee00
    在这里插入图片描述
    ④ 从上一章节知道设备树的入口文件为”sama5d34ek.dts”,在“sama5d34ek.dts”文件中#include了很多dtsi文件。

    在这里插入图片描述
    ⑤ 进入相应的.dtsi文件,可以找到相关dbgu的设备节点。可以看到里面描述的内容与datasheet一致。
    在这里插入图片描述
    在这里插入图片描述
    ⑥ 在系统中,也可以找到dbgu的相关信息
    在这里插入图片描述

    基于设备树驱动LED

    大致可以分为三个步骤:
    ① 在.dts文件中创建相应的设备节点
    ② 编写驱动,获取设备树中的属性值
    ③ 使用获取的属性值初始化LED所使用的GPIO。
    一.根节‘/’下创建子节点
    添加内容如下:

    boyee_led{
    	#address-cells = <1>;
    	#size-cells = <1>;
    	compatible = "boyee-led";
    	status = "okay";
    	reg = <0Xfffff600 0x200>;//PIOC的基地址
    	};
    

    二.编译.dts生成.dtb
    在内核根目录下执行make dtbs,编译生成新的.dtb。或者执行make,也会对更改过的dtbs进行重新编译。然后使用新的.dtb启动linux内核。
    在这里插入图片描述
    三.驱动编写
    提取设备节点的属性,linux内核中使用device_node结构体来描述一个节点,此结构体定义在文件include/linux/of.h中。
    查找节点有关的OF函数(获取设备节点属性函数都以of_开头)有5个:
    of_find_node_by_name:通过节点名字查找指定的节点。
    of_find_nade_by_type:通过device_type属性查找指定的节点。
    of_find_compatible_node:根据device_type和compatibe两个属性查找指定的节点。
    of_find_matching_node_and_match:通过of_device_id查找指定节点。
    of_find_node_by_path:通过路径来查找指定的节点。

    #include <linux/fs.h> /*包含file_operation结构体*/
    #include <linux/init.h>/* 包含module_init module_exit */
    #include <linux/module.h>/* 包含LICENSE的宏 */
    #include <linux/miscdevice.h>/*包含miscdevice结构体*/
    #include <linux/io.h>/*包含ioremap等操作函数*/
    #include <linux/kernel.h>/*包含printk等操作函数*/
    #include <linux/of.h>/*设备树操作相关的函数*/
    
    
    /**************宏定义***************/
    #define PIO_SODR	(*(volatile unsigned long *)(virt_addr +0x0030))
    #define PIO_CODR	(*(volatile unsigned long *)(virt_addr +0x0034))
    #define PIO_PER		(*(volatile unsigned long *)(virt_addr +0x0000))
    #define PIO_MDDR	(*(volatile unsigned long *)(virt_addr +0x0054))
    #define PIO_OER		(*(volatile unsigned long *)(virt_addr +0x0010))
    #define PIO_OWDR	(*(volatile unsigned long *)(virt_addr +0x00A4))
    
    /*
    #define PIO_PDSR	(*(volatile unsigned long *)(virt_addr +0x003C))
    #define PIO_ODR		(*(volatile unsigned long *)(virt_addr +0x0014))
    #define PIO_PUER	(*(volatile unsigned long *)(virt_addr +0x0064))
    #define PIO_PUDR	(*(volatile unsigned long *)(virt_addr +0x0060 ))
    #define PIO_PPDER	(*(volatile unsigned long *)(virt_addr +0x0094))
    */
    
    #define 	LED2		1 << 29
    #define 	LED1		1 << 26
    #define 	LED_ON		1
    #define 	LED_OFF		0
    #define 	LED1_CTL	5
    #define 	LED2_CTL	6
    /**************内部变量***************/
    unsigned long virt_addr;
    
    /* 定义一个打开设备的,open函数 */
    static int led_open(struct inode *inode,struct file *file)
    {
    	return 0;
    }
    
    /* 定义一个打开设备的,ioctl函数 */
    static long led_ioctl(struct file *file,unsigned int data,unsigned long arg)
    {
    	switch(data)
    	{
    		case LED1_CTL:
    			if(arg == LED_ON){
    				PIO_SODR |= LED1;/*对芯片寄存器IO操作*/
    			}
    			else{
    				PIO_CODR |= LED1;
    			}
    		break;
    		case LED2_CTL:
    			if(arg == LED_ON){
    				PIO_SODR |= LED2;
    			}
    			else{
    				PIO_CODR |= LED2;
    			}
    		break;
    		default:
    		break;
    	}
    	return 0;
    }
    /*字符设备驱动程序就是为具体硬件的file_operations结构编写各个函数*/
    static const struct file_operations led_ctl={
             .owner          = THIS_MODULE,
             .unlocked_ioctl = led_ioctl,
             .open           = led_open,
    };
    
    /*杂项设备,主设备号为10的字符设备,相对普通字符设备,使用更简单*/
    static struct miscdevice led_miscdev = {
             .minor          = 255,
             .name           = "led_ctl",
             .fops           = &led_ctl,
    };
    
    static int __init led_init(void)
    {
    	int res;
    	struct device_node *led_nd;//设备节点
    	struct property *proper;
    	unsigned int regdata[2];
    	char str[30];
    	
    	/*获取设备树中的属性数据*/
    	/*1.获取设备节点:boyee_led*/
    	led_nd = of_find_node_by_path("/boyee_led");
    	if(led_nd == NULL)
    	{
    		printk("boyee_led node not find\r\n");
    	}
    	else
    	{
    		printk("boyee_led node find\r\n");
    	}
    	
    	/*2.获取compatible属性内容*/
    	proper =  of_find_property(led_nd,"compatible",NULL);
    	if(proper == NULL)
    	{
    		printk("compatible property find failed\r\n");
    	}
    	else
    	{
    		printk("compatible =%s\r\n",(char *)proper->value);
    	}
    	
    	/*3.获取status属性内容*/
    	res = of_property_read_string(led_nd,"status",&str);
    	if(res < 0)
    	{
    		printk("status read failed\r\n");
    	}
    	else
    	{
    		printk("status =%s\r\n",str);
    	}
    	
    	/*4.获取reg属性内容*/
    	res = of_property_read_u32_array(led_nd,"reg",regdata,2);
    	if(res <0 )
    	{
    		printk("reg property read failed\r\n");
    	}
    	else
    	{
    		printk("regdata[0]=%x\r\n",regdata[0]);
    		printk("regdata[1]=%x\r\n",regdata[1]);
    	}
    	
    	
    	/*注册杂项设备驱动*/
    	res = misc_register(&led_miscdev);
    	printk(KERN_ALERT"led_init %d\n",res);
    
    	
    	/*通过物理地址,得到寄存器的虚拟地址*/
    	virt_addr =(volatile unsigned long )ioremap(regdata[0],regdata[1]); 
    	
    	/*对芯片寄存器操作,输出IO使能*/
    	PIO_PER  |= LED1 | LED2;
    	PIO_MDDR |= LED1 | LED2;
    	PIO_OER  |= LED1 | LED2;
    	PIO_OWDR |= LED1 | LED2;
    	PIO_CODR |= LED1 | LED2;
    	return res;
    }
    
    static void __exit led_exit(void)
    {
    	/*释放杂项设备*/
    	misc_deregister(&led_miscdev);
    	/*取消虚拟地址映射*/
    	iounmap((unsigned long *)virt_addr);
    	printk(KERN_ALERT"led_exit\r\n");
    }
    
    /*驱动模块的加载和卸载入口*/
    module_init(led_init);
    module_exit(led_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("boyee");
    MODULE_DESCRIPTION("control output led");
    

    四.编译测试
    编译驱动生成xx.ko,insmod驱动,编写测试APP、Makefile,运行测试APP,查看现象。
    在这里插入图片描述

    展开全文
  • 嵌入式Linux C编程入门(第2版) PPT

    热门讨论 2011-03-11 20:48:34
    9.4 嵌入式linux串口应用开发 279 9.4.1 串口概述 279 9.4.2 串口设置详解 280 9.4.3 串口使用详解 284 本章小结 287 动手练练 287 第10章 arm linux进程线程开发实例 288 10.1 arm linux进程...
  • Linux从入门到精通

    2010-04-25 19:58:09
    E.8.13 我有一个微软串口鼠标, Linux不能使用它. 怎么办? E.9 系统管理 E.9.1 当我用usercfg创建用户, 他们不能登录. E.9.2 什么是PAM? 为什么要用它? E.9.3 我如何在我的Linux系统上设置Secure Shell (SSH)? E...
  • Linux 蓝牙协议栈的USB+设备驱动

    热门讨论 2010-12-28 15:16:53
    摘 要:基于对Linux 下蓝牙协议栈BlueZ 源代码的分析,给出BlueZ的组织结构和特点。分析蓝牙USB 传输驱动机制和数据处理过程, 给出实现蓝牙设备驱动的重要数据结构和流程,并总结Linux 下开发蓝牙USB 设备驱动的...
  • E.8.13 我有一个微软串口鼠标, Linux不能使用它. 怎么办? E.9 系统管理 E.9.1 当我用usercfg创建用户, 他们不能登录. E.9.2 什么是PAM? 为什么要用它? E.9.3 我如何在我的Linux系统上设置Secure Shell (SSH)? E...
  • linux从入门到精通.chm

    2010-05-17 09:11:20
    E.8.13 我有一个微软串口鼠标, Linux不能使用它. 怎么办? E.9 系统管理 E.9.1 当我用usercfg创建用户, 他们不能登录. E.9.2 什么是PAM? 为什么要用它? E.9.3 我如何在我的Linux系统上设置Secure Shell (SSH)? E...
  • Linux从入门到精通》

    热门讨论 2008-09-04 17:05:49
    E.8.13 我有一个微软串口鼠标, Linux不能使用它. 怎么办? E.9 系统管理 E.9.1 当我用usercfg创建用户, 他们不能登录. E.9.2 什么是PAM? 为什么要用它? E.9.3 我如何在我的Linux系统上设置Secure Shell (SSH)? E...
  • 6.5.2 通用的一些选项 6.5.3 和模块相关的选项 6.5.4 和块相关的选项 6.5.5 和系统类型相关的选项 6.5.6 和总线相关的选项 6.5.7 和内核特性相关的选项 6.5.8 和系统启动相关的选项 6.5.9 和浮点运算相关的...
  • vc源代码合集2244.rar

    2012-06-11 23:15:54
    2012-06-11 22:35 295,571 带中文注释的Linux+0.11+源代码.rar 2012-06-11 22:32 276,912 成功需要“三心二意”.pdf 2012-06-11 22:19 107,008 数值分析实验报告.doc 2012-06-11 22:17 292,864 最小二乘法的基本原理...
  • pwm的matlab代码 Learning Map 学习心得地图,欢迎补充。 :laptop: 教程(网页) :notebook: 教程(文件) :television: 教程(视频) ...例程、代码 ...(上位机软件详细代码讲解) ...学习建议掌握一定Linux编程基础
  • 另外如果你开发LINUX就要用ARM-LINUX-TOOLS,不一样,不通用。 5 U-BOOT 大名鼎鼎的BOOTLOADER生成工具,同类的好象还有VIVI(名字很暧昧~~) 生成的BOOTLOADER烧到FLASH里,然后就可以用BOOTLOADER下载 烧写其他了...
  • 通用串口波特率设置方法并没有配置成这个波特率的方法。没有办法,只能查资料。各种搜索引擎尝试了之后,发现也有同僚遇到这种问题,但是基本所有的搜索条目都指向了同一个帖子,或者其复制品。而这个帖子只给出...
  • Java二进制IO类与文件复制操作实例 16个目标文件 内容索引:Java源码,初学实例,二进制,文件复制 Java二进制IO类与文件复制操作实例,好像是一本书的例子,源代码有的是独立运行的,与同目录下的其它代码文件互不联系...
  • 测试程序.rar

    2019-09-21 19:23:58
    嵌入式Linux下一些通用的调试及测试代码串口读写,camera图像V4L2采集及显示,i2C通信,gps串口读写数据及解析
  • stm32实用循环buffer

    千次阅读 2017-03-05 21:21:33
    本人在实际开发中多次用到串口的循环buffer,最开始在网上搜索了相关文章和资料,感觉通用性不是很高。 自己也写过fifo,感觉还是过于臃肿。一直想找个完美的循环buffer。在看linux内核代码时,发现内核里面也 经常...
  • 产品版本: 芯片名称 内核版本 ...2.1 代码路径 采用的是8250通用驱动,类型是16550A 2.2 内核配置 2.3 使能串口设备 2.3.1 使能uart0 2.3.2 驱动设备注册log 2.3.3 串口设备 2.4 DTS节点配置...

空空如也

空空如也

1 2 3 4
收藏数 71
精华内容 28
关键字:

linux串口通用代码

linux 订阅