精华内容
下载资源
问答
  • 控制台:console, 一种特殊的终端,能够显示系统信息,一般只有一个。 1.2 终端驱动框架 tty核心:对整个tty设备的抽象,对用户提供统一的接口 tty线路规程:对传输数据的格式化 tty驱动:面向tty设备的硬件驱动 ...

    1 框架

    1.1 概念

    终端:只有输入、显示部件并能连接到计算机的设备,早期设备,现在没有了。
    虚拟终端:由硬件终端演化出来的软件概念,用计算机的软件来模拟以前硬件的方式。
    控制台:console, 一种特殊的终端,能够显示系统信息,一般只有一个。

    1.2 终端驱动框架

    在这里插入图片描述
    tty核心:对整个tty设备的抽象,对用户提供统一的接口
    tty线路规程:对传输数据的格式化
    tty驱动:面向tty设备的硬件驱动
    当从用户空间进行读写时,流程如下:
    在这里插入图片描述

    2 源码分析

    由上述框架,关注tty核心、tty线路规程和tty驱动,关注整体流程。

    2.1 设备

    在内核启动时,调用init/main.c中的start_kernel。

    start_kernel --->
    	setup_arch --->
    		paging_init --->
    			devicemaps_init --->
    				mdesc->map_io
    

    以s3c2440为例,mdesc是根据mdesc = setup_machine(machine_arch_type)得到的,在mach-s3c2440.c中

    MACHINE_START(S3C2440, "SMDK2440")
    	/* Maintainer: Ben Dooks <ben@fluff.org> */
    	.phys_io	= S3C2410_PA_UART,
    	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    	.boot_params	= S3C2410_SDRAM_PA + 0x100,
    
    	.init_irq	= s3c24xx_init_irq,
    	.map_io		= smdk2440_map_io,
    	.init_machine	= smdk2440_machine_init,
    	.timer		= &s3c24xx_timer,
    MACHINE_END
    

    所以mdesc->map_io会调用smdk2440_map_io,继续看流程

    smdk2440_map_io --->
    	s3c24xx_init_uarts --->
    		(cpu->init_uarts)(cfg, no)
    

    cpu_ids[]在arch/arm/plat-s3c24xx/cpu.c中,其中2440的init_uarts对应的是s3c244x_init_uarts

    void __init s3c244x_init_uarts(struct s3c2410_uartcfg *cfg, int no)
    {
    	s3c24xx_init_uartdevs("s3c2440-uart", s3c2410_uart_resources, cfg, no);
    }
    
    void __init s3c24xx_init_uartdevs(char *name,
    				  struct s3c24xx_uart_resources *res,
    				  struct s3c2410_uartcfg *cfg, int no)
    {
    	struct platform_device *platdev;
    	struct s3c2410_uartcfg *cfgptr = uart_cfgs;//全局数组,保存串口的配置信息
    	struct s3c24xx_uart_resources *resp;
    	int uart;
    
    	memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
    
    	for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
    		//为每个uart的platform_device设置好相关值
    		platdev = s3c24xx_uart_src[cfgptr->hwport];
    
    		resp = res + cfgptr->hwport;
    
    		s3c24xx_uart_devs[uart] = platdev;
    
    		platdev->name = name; /*platform 设备名称初始化为 "s3c2440-uart" */
    		platdev->resource = resp->resources;
    		platdev->num_resources = resp->nr_resources;
    
    		platdev->dev.platform_data = cfgptr;
    	}
    
    	nr_uarts = no;
    }
    

    另外在系统初始化过程中,会调用存放在arch_initcall段中的函数。

    static int __init s3c_arch_init(void)
    {
    	...
    	ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
    	return ret;
    }
    
    arch_initcall(s3c_arch_init);
    

    在这里添加了串口设备。

    2.2 终端

    start_kernel -->console_init

    void __init console_init(void)
    {
    	initcall_t *call;
    
    	/* 设置默认的线路规程 */
    	tty_ldisc_begin();
    
    	call = __con_initcall_start;
    	while (call < __con_initcall_end) {
    		(*call)();
    		call++;
    	}
    }
    

    默认的线路规程为N_TTY

    void tty_ldisc_begin(void)
    {
    	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
    }
    
    int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
    {
    	...
    	tty_ldiscs[disc] = new_ldisc;
    	...
    }
    

    在drivers/serial/samsung.h中

    #define s3c24xx_console_init(__drv, __inf)			\
    static int __init s3c_serial_console_init(void)			\
    {								\
    	return s3c24xx_serial_initconsole(__drv, __inf);	\
    }								\
    								\
    console_initcall(s3c_serial_console_init)
    

    在s3c2440.c中

    s3c24xx_console_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
    

    所以上述调用call,会调用s3c24xx_serial_initconsole

    static struct console s3c24xx_serial_console = {
    	.name		= S3C24XX_SERIAL_NAME,
    	.device		= uart_console_device,
    	.flags		= CON_PRINTBUFFER,
    	.index		= -1,
    	.write		= s3c24xx_serial_console_write,
    	.setup		= s3c24xx_serial_console_setup
    };
    
    int s3c24xx_serial_initconsole(struct platform_driver *drv,
    			       struct s3c24xx_uart_info *info)
    
    {
    	...
    	s3c24xx_serial_init_ports(info);  //初始化串口端口
    	register_console(&s3c24xx_serial_console);
    	return 0;
    }
    

    继续

    register_console --->
    	newcon->setup ---> //即s3c24xx_serial_console_setup
    		s3c24xx_serial_console_setup --->
    			uart_set_options --->
    				port->ops->set_termios --->//即s3c24xx_serial_ports->ops->set_termios,s3c24xx_serial_set_termios,初始化termios
    

    2.3 驱动

    (1)s3c24xx_serial_modinit
    在/drivers/serial/samsung.c中,s3c24xx_serial_modinit —>uart_register_driver(&s3c24xx_uart_drv)

    int uart_register_driver(struct uart_driver *drv)
    {
    	struct tty_driver *normal = NULL;
    	int i, retval;
    
    	BUG_ON(drv->state);
    
    	drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
    	retval = -ENOMEM;
    	if (!drv->state)
    		goto out;
    
    	normal  = alloc_tty_driver(drv->nr); //分配tty结构体
    	if (!normal)
    		goto out;
    
    	drv->tty_driver = normal;
        //设置tty结构体内容
    	normal->owner		= drv->owner;
    	normal->driver_name	= drv->driver_name;
    	normal->name		= drv->dev_name;
    	normal->major		= drv->major;
    	normal->minor_start	= drv->minor;
    	normal->type		= TTY_DRIVER_TYPE_SERIAL;
    	normal->subtype		= SERIAL_TYPE_NORMAL;
    	normal->init_termios	= tty_std_termios;
    	normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    	normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
    	normal->flags		= TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
    	normal->driver_state    = drv;
    	tty_set_operations(normal, &uart_ops);//设置tty_driver的ops
    
    	for (i = 0; i < drv->nr; i++) {
    		struct uart_state *state = drv->state + i;
    		struct tty_port *port = &state->port;
    
    		tty_port_init(port);
    		port->close_delay     = 500;	/* .5 seconds */
    		port->closing_wait    = 30000;	/* 30 seconds */
    		tasklet_init(&state->tlet, uart_tasklet_action,
    			     (unsigned long)state);//定义了tasklet中断处理
    	}
    
    	retval = tty_register_driver(normal);//注册tty驱动
     out:
    	if (retval < 0) {
    		put_tty_driver(normal);
    		kfree(drv->state);
    	}
    	return retval;
    }	
    

    注册tty_driver

    int tty_register_driver(struct tty_driver *driver)
    {
    	int error;
    	int i;
    	dev_t dev;
    	void **p = NULL;
    
    	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
    		p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
    		if (!p)
    			return -ENOMEM;
    	}
    
    	if (!driver->major) {
    		error = alloc_chrdev_region(&dev, driver->minor_start,
    						driver->num, driver->name);
    		if (!error) {
    			driver->major = MAJOR(dev);
    			driver->minor_start = MINOR(dev);
    		}
    	} else {
    		dev = MKDEV(driver->major, driver->minor_start);
    		error = register_chrdev_region(dev, driver->num, driver->name);
    	}
    	if (error < 0) {
    		kfree(p);
    		return error;
    	}
    
    	if (p) {
    		driver->ttys = (struct tty_struct **)p;
    		driver->termios = (struct ktermios **)(p + driver->num);
    	} else {
    		driver->ttys = NULL;
    		driver->termios = NULL;
    	}
    
    	cdev_init(&driver->cdev, &tty_fops);
    	driver->cdev.owner = driver->owner;
    	error = cdev_add(&driver->cdev, dev, driver->num);
    	if (error) {
    		unregister_chrdev_region(dev, driver->num);
    		driver->ttys = NULL;
    		driver->termios = NULL;
    		kfree(p);
    		return error;
    	}
    
    	mutex_lock(&tty_mutex);
    	list_add(&driver->tty_drivers, &tty_drivers); //添加到tty_drivers
    	mutex_unlock(&tty_mutex);
    
    	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
    		for (i = 0; i < driver->num; i++)
    		    tty_register_device(driver, i, NULL);
    	}
    	proc_tty_register_driver(driver);
    	driver->flags |= TTY_DRIVER_INSTALLED;
    	return 0;
    }
    

    (2)s3c2440_serial_init
    在drivers/serial/s3c2440.c中,s3c2440_serial_init–>s3c24xx_serial_init–>platform_driver_register,添加了驱动后,调用probe。
    s3c2440_serial_probe–>s3c24xx_serial_probe

    int s3c24xx_serial_probe(struct platform_device *dev,
    			 struct s3c24xx_uart_info *info)
    {
    	struct s3c24xx_uart_port *ourport;
    	int ret;
    
    	dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
    
    	ourport = &s3c24xx_serial_ports[probe_index];
    	probe_index++;
    
    	dbg("%s: initialising port %p...\n", __func__, ourport);
    
    	ret = s3c24xx_serial_init_port(ourport, info, dev);
    	if (ret < 0)
    		goto probe_err;
    
    	dbg("%s: adding port\n", __func__);
    	uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); //
    	platform_set_drvdata(dev, &ourport->port);
    
    	ret = device_create_file(&dev->dev, &dev_attr_clock_source);
    	if (ret < 0)
    		printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
    
    	ret = s3c24xx_serial_cpufreq_register(ourport);
    	if (ret < 0)
    		dev_err(&dev->dev, "failed to add cpufreq notifier\n");
    
    	return 0;
    
     probe_err:
    	return ret;
    }
    

    2.4 open

    当应用层调用open,会调用到tty_fops.open,tty_open–>__tty_open

    static int __tty_open(struct inode *inode, struct file *filp)
    {
    	...
    	driver = get_tty_driver(device, &index);
    	...
    
    	if (tty) {
    		retval = tty_reopen(tty);
    		if (retval)
    			tty = ERR_PTR(retval);
    	} else
    		tty = tty_init_dev(driver, index, 0);
    
    	...
    	if (!retval) {
    		if (tty->ops->open)
    			retval = tty->ops->open(tty, filp); //调用uart_ops中的uart_open函数
    		else
    			retval = -ENODEV;
    	}
    	...
    }
    

    (1)get_tty_driver

    static struct tty_driver *get_tty_driver(dev_t device, int *index)
    {
    	struct tty_driver *p;
    
    	list_for_each_entry(p, &tty_drivers, tty_drivers) {
    		dev_t base = MKDEV(p->major, p->minor_start);
    		if (device < base || device >= base + p->num)
    			continue;
    		*index = device - base;
    		return tty_driver_kref_get(p);
    	}
    	return NULL;
    }
    

    通过遍历tty_drivers,得到索引号,返回tty_driver。
    (2)tty_init_dev

    struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
    								int first_ok)
    {
    	...
    
    	tty = alloc_tty_struct();
    	if (!tty)
    		goto fail_no_mem;
    	initialize_tty_struct(tty, driver, idx);
    
    	retval = tty_driver_install_tty(driver, tty);
    	if (retval < 0) {
    		free_tty_struct(tty);
    		module_put(driver->owner);
    		return ERR_PTR(retval);
    	}
    
    	retval = tty_ldisc_setup(tty, tty->link);
    	if (retval)
    		goto release_mem_out;
    	return tty;
    	...
    }
    
    void initialize_tty_struct(struct tty_struct *tty,
    		struct tty_driver *driver, int idx)
    {
    	memset(tty, 0, sizeof(struct tty_struct));
    	kref_init(&tty->kref);
    	tty->magic = TTY_MAGIC;
    	tty_ldisc_init(tty); //初始化线路规程
    	...
    
    	tty->driver = driver;
    	tty->ops = driver->ops;
    	tty->index = idx;  //
    	tty_line_name(driver, idx, tty->name);
    }
    

    在tty_ldisc.c中

    static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
    {
    	tty->ldisc = ld;
    }
    
    void tty_ldisc_init(struct tty_struct *tty)
    {
    	struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
    	if (IS_ERR(ld))
    		panic("n_tty: init_tty");
    	tty_ldisc_assign(tty, ld);
    }
    

    获取N_TTY的线路规程,并填充到tty_driver中。
    (3)tty->ops->open

    uart_open --->
    	uart_startup --->
    		uport->ops->startup  ---> //s3c24xx_serial_startup
    
    static int s3c24xx_serial_startup(struct uart_port *port)
    {
    	struct s3c24xx_uart_port *ourport = to_ourport(port);
    	int ret;
    
    	dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
    	    port->mapbase, port->membase);
    
    	rx_enabled(port) = 1; //使能串口接收功能
    
    	ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
    			  s3c24xx_serial_portname(port), ourport);//注册接收中断处理程序
    
    	if (ret != 0) {
    		printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
    		return ret;
    	}
    
    	ourport->rx_claimed = 1;
    
    	dbg("requesting tx irq...\n");
    
    	tx_enabled(port) = 1;
    
    	ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,
    			  s3c24xx_serial_portname(port), ourport);//注册发送中断处理程序
    
    	if (ret) {
    		printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
    		goto err;
    	}
    
    	ourport->tx_claimed = 1; //使能串口发送功能
    
    	dbg("s3c24xx_serial_startup ok\n");
    
    	return ret;
    
     err:
    	s3c24xx_serial_shutdown(port);
    	return ret;
    }
    

    2.5 write

    当应用层调用write,调用tty_write

    tty_write --->
    	do_tty_write --->
    		write(tty, file, tty->write_buf, size) --->//即n_tty_write 
    			tty->ops->write ---> //uart_ops.write
    				uart_start --->
    					__uart_start --->
    						port->ops->start_tx ---> //s3c24xx_serial_start_tx
    							enable_irq(ourport->tx_irq); //使能中断					
    

    触发中断后,调用s3c24xx_serial_tx_chars,发送数据

    2.6 read

    当应用层调用read,调用tty_read

    tty_read --->
    	ld->ops->read --->
    		n_tty_read --->		
    
    static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
    			 unsigned char __user *buf, size_t nr)
    {
    	...
    	add_wait_queue(&tty->read_wait, &wait);
    	while (nr) {
    		...
    		set_current_state(TASK_INTERRUPTIBLE); //设置当前进程为可中断的阻塞态
    
    		if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
    		    ((minimum - (b - buf)) >= 1))
    			tty->minimum_to_wake = (minimum - (b - buf));
    
    		if (!input_available_p(tty, 0)) {  //如果没有可用数据可供应用程序读
    			...
    			timeout = schedule_timeout(timeout);//使阻塞生效
    			continue;
    		}
    		//如果有可用数据可读
    		__set_current_state(TASK_RUNNING);
    
    		...
    		if (tty->icanon) {
    			...
    		} else {
    			int uncopied;
    
    			uncopied = copy_from_read_buf(tty, &b, &nr);
    			uncopied += copy_from_read_buf(tty, &b, &nr);//从read_buf中读走数据
    			if (uncopied) {
    				retval = -EFAULT;
    				break;
    			}
    		}
    		...
    	}
    	...
    }
    

    当中断发生时,调用s3c24xx_serial_rx_chars,读取数据,放入缓冲区。

    展开全文
  • 现在手里有一块s3c2440友善之臂的开发板,这个开发板有三个串口,其中串口0和串口1...我想问的问题就是,怎么修改(u-boot 内核或者驱动或者别的地方),可以像程序1一样读写串口1(就是程序片段2能够正确执行)?
  • SJE300系列磁卡读写器可联接任何具有RS232串口的电脑或终端,用于读写磁卡或存折本上的磁条信息。该系列磁卡读写器操作编程简单,读写均一次刷卡完成,具有读、写双重校验功能,性能稳定可靠,并且兼容性好(能自动...
  • github:... 第一步:获取所有的已插入的串口驱动 1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 2 List<UsbSerialDriver> availableDrivers...

     

    github:https://github.com/mik3y/usb-serial-for-android

    第一步:获取所有的已插入的串口驱动

    1 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
    2 List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
    3 if (availableDrivers.isEmpty()) {
    4   return;
    5 }

     

    然后,我们选择第一个dirver ,连接设备

    1 UsbSerialDriver driver = availableDrivers.get(0);
    2 UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
    3 if (connection == null) {
    4   // You probably need to call UsbManager.requestPermission(driver.getDevice(), ..)
    5   return;
    6 }

     

    接下来就可以读数据了

     

     1 // Read some data! Most have just one port (port 0).
     2 UsbSerialPort port = driver.getPorts().get(0);
     3 try {
     4   port.open(connection);
     5 //设置串口的波特率、数据位,停止位,校验位
     6   port.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
     7 
     8   byte buffer[] = new byte[16];
     9   int numBytesRead = port.read(buffer, 1000);
    10   Log.d(TAG, "Read " + numBytesRead + " bytes.");
    11 } catch (IOException e) {
    12   // Deal with error.
    13 } finally {
    14   port.close();
    15 }

     

     

    当然,我们可以给串口添加个监听

     

     1  1 private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
     2  2 private SerialInputOutputManager mSerialIoManager;
     3  3 
     4  4 private final SerialInputOutputManager.Listener mListener =
     5  5             new SerialInputOutputManager.Listener() {
     6  6                 @Override
     7  7                 public void onRunError(Exception e) {
     8  8                     Log.d(TAG, "Runner stopped.");
     9  9                 }
    10 10 
    11 11                 @Override
    12 12                 public void onNewData(final byte[] data) {
    13 13                    //TODO 新的数据
    14 14                 }
    15 15             };
    16 16 
    17 17 mSerialIoManager = new SerialInputOutputManager(sPort, mListener);//添加监听
    18 //在新的线程中监听串口的数据变化
    19 18 mExecutor.submit(mSerialIoManager);

     

    如果需要接受比较大的数据,有可能会遇到一个问题:数据缓存和接收时间不够,导致数据被覆盖或者丢失,我们就需要修改串口读取缓存了

    把 SerialInputOutputManager 中的 READ_WAIT_MILLIS 和 BUFSIZ 改成合适的大小就可以了

    写数据的操作就是调用port的方法

    port.write(bytes, 1000);
    

    其实这个开源项目已经为我们封装了很多驱动类,都在driver包下,我们直接拿来用就可以了

     

    零 USB背景知识

    USB是一种数据通信方式,也是一种数据总线,而且是最复杂的总线之一。
    硬件上,它是用插头连接。一边是公头(plug),一边是母头(receptacle)。例如,PC上的插座就是母头,USB设备使用公头与PC连接。
    目前USB硬件接口分三种,普通PC上使用的叫Type;原来诺基亚功能机时代的接口为Mini USB;目前Android手机使用的Micro USB。

    Host
    USB是由Host端控制整个总线的数据传输的。单个USB总线上,只能有一个Host。
    OTG
    On The Go,这是在USB2.0引入的一种mode,提出了一个新的概念叫主机协商协议(Host Negotiation Protocol),允许两个设备间商量谁去当Host。

     

    一、Android中的USB

    Android对Usb的支持是从3.1开始的,显然是加强Android平板的对外扩展能力。而对Usb使用更多的,是Android在工业中的使用。Android工业板子一般都会提供多个U口和多个串口,它们是连接外设的手段与桥梁。下面就来介绍一下Android Usb使用模式之一的USB Host。

    android.hardware.usb包下提供了USB开发的相关类。
    我们需要了解UsbManager、UsbDevice、UsbInterface、UsbEndpoint、UsbDeviceConnection、UsbRequest、UsbConstants。
    1、UsbManager:获得Usb的状态,与连接的Usb设备通信。
    2、UsbDevice:Usb设备的抽象,它包含一个或多个UsbInterface,而每个UsbInterface包含多个UsbEndpoint。Host与其通信,先打开UsbDeviceConnection,使用UsbRequest在一个端点(endpoint)发送和接收数据。
    3、UsbInterface:定义了设备的功能集,一个UsbDevice包含多个UsbInterface,每个Interface都是独立的。
    4、UsbEndpoint:endpoint是interface的通信通道。
    5、UsbDeviceConnection:host与device建立的连接,并在endpoint传输数据。
    6、UsbRequest:usb 请求包。可以在UsbDeviceConnection上同步异步传输数据。
    7、UsbConstants:usb常量的定义,对应linux/usb/ch9.h

    二、USB插入事件

    Usb的插入和拔出是以系统广播的形式发送的,只要我们注册这个广播即可。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    @Override

        protected void onResume() {

            super.onResume();

            IntentFilter usbFilter = new IntentFilter();

            usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);

            usbFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);

            registerReceiver(mUsbReceiver, usbFilter);

        }

     

        @Override

        protected void onPause() {

            super.onPause();

            unregisterReceiver(mUsbReceiver);

        }

     

        private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

            public void onReceive(Context context, Intent intent) {

                String action = intent.getAction();

                tvInfo.append("BroadcastReceiver in\n");

     

               if(UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {

                    tvInfo.append("ACTION_USB_DEVICE_ATTACHED\n");

                } else if(UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {

                    tvInfo.append("ACTION_USB_DEVICE_DETACHED\n");

                }

            }

        };

    三、Usb插入时启动程序

    有些应用场景是,Usb插入后启动特定程序处理特定问题。
    我们的做法就是在Manifest中某个Activity加入Usb插入的action。

    1

    2

    3

    4

    <intent-filter>

        <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED">

    </action></intent-filter>

      <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usbfilter"></meta-data>

    在usbfilter中加入厂商id和产品id的过滤,如下:

    1

    2

    3

    4

     

    <resources>

        <usb-device vendor-id="1234" product-id="5678">

    </usb-device></resources>

    结果就是,当此型号设备通过Usb连接到系统时,对应的Activity就会启动。

    四、UsbManager的初始化

    1

    mUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);

    五、列出Usb设备

     

    1

    2

    3

    4

    5

    6

    7

    8

       HashMap<string,usbdevice> deviceHashMap = mUsbManager.getDeviceList();

            Iterator<usbdevice> iterator = deviceHashMap.values().iterator();

            while (iterator.hasNext()) {

                UsbDevice device = iterator.next();

                tvInfo.append("\ndevice name: "+device.getDeviceName()+"\ndevice product name:"

                        +device.getProductName()+"\nvendor id:"+device.getVendorId()+

                        "\ndevice serial: "+device.getSerialNumber());

            }</usbdevice></string,usbdevice>

    六、USB使用权限

    安卓系统对USB口的使用需要得到相应的权限,而这个权限要用户亲自给才行。
    首先我们会确认一下上一节中的device是否已经获得权限,如果没有就要主动申请权限:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

                //先判断是否为自己的设备

                //注意:支持十进制和十六进制

                //比如:device.getProductId() == 0x04D2

                if(device.getProductId() == 1234 && device.getVendorId() == 5678) {

                    if(mUsbManager.hasPermission(device)) {

                        //do your work

                    } else {

                        mUsbManager.requestPermission(device,mPermissionIntent);

                    }

                }

    我们仍然使用广播来获得权限赋予情况。

    1public static final String ACTION_DEVICE_PERMISSION = "com.linc.USB_PERMISSION";

    注册广播

    1

    2

    3

       mPermissionIntent = PendingIntent.getBroadcast(this,0,new Intent(ACTION_DEVICE_PERMISSION),0);

            IntentFilter permissionFilter = new IntentFilter(ACTION_DEVICE_PERMISSION);

            registerReceiver(mUsbReceiver,permissionFilter);

    接收器的代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

            public void onReceive(Context context, Intent intent) {

                String action = intent.getAction();

                tvInfo.append("BroadcastReceiver in\n");

                if (ACTION_DEVICE_PERMISSION.equals(action)) {

                    synchronized (this) {

                        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {

                            if (device != null) {

                                tvInfo.append("usb EXTRA_PERMISSION_GRANTED");

                            }

                        } else {

                            tvInfo.append("usb EXTRA_PERMISSION_GRANTED null!!!");

                        }

                    }

                }

            }

        };

    七、通信

    UsbDevice有了权限,下面就可以进行通信了。
    这里要用到:UsbInterface、UsbEndpoint(一进一出两个endpoint,双向通信)、UsbDeviceConnection。
    注意:通信的过程不能在UI线程中进行。
    得到授权后,将做一些通信前的准备工作,如下:

    ?

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    private void initCommunication(UsbDevice device) {

            tvInfo.append("initCommunication in\n");

            if(1234 == device.getVendorId() && 5678 == device.getProductId()) {

                tvInfo.append("initCommunication in right device\n");

                int interfaceCount = device.getInterfaceCount();

                for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {

                    UsbInterface usbInterface = device.getInterface(interfaceIndex);

                    if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())

                            && (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())) {

                        continue;

                    }

     

                    for (int i = 0; i < usbInterface.getEndpointCount(); i++) {

                        UsbEndpoint ep = usbInterface.getEndpoint(i);

                        if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {

                            if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {

                                mUsbEndpointIn = ep;

                            } else {

                                mUsbEndpointOut = ep;

                            }

                        }

                    }

     

                    if ((null == mUsbEndpointIn) || (null == mUsbEndpointOut)) {

                        tvInfo.append("endpoint is null\n");

                        mUsbEndpointIn = null;

                        mUsbEndpointOut = null;

                        mUsbInterface = null;

                    } else {

                        tvInfo.append("\nendpoint out: " + mUsbEndpointOut + ",endpoint in: " +

                                mUsbEndpointIn.getAddress()+"\n");

                        mUsbInterface = usbInterface;

                        mUsbDeviceConnection = mUsbManager.openDevice(device);

                        break;

                    }

                }

            }

        }

    发送数据如下:

    ?

    1

    result = mUsbDeviceConnection.bulkTransfer(mUsbEndpointOut, mData, (int)buffSize, 1500);//需要在另一个线程中进行

     

    展开全文
  • VB串口通信源码210个

    千次下载 热门讨论 2013-10-15 11:32:27
    072、VB实现串口调试LED信息显示屏设备主要代码 073、VB实现串口调试工具的完整源码 074、vb实现串口通信 文件传送系统,用vb以及mscomm控件实现 075、VB实现串口通信,发送命令从而接收相应数据 076、VB使用mscom...
  • 6410 IIC读写问题

    2010-10-31 17:30:00
    后来终于发现,可以调整驱动串口打印驱动运行的信息,经过对比两个程序打印的信息,发现我的程序根本没有进入iocontrol的case里面。对比驱动打印的信息,发现我的控制码有错误。 我的驱动中控制码是通过CTL_CODE...

    最近一直在做IIC读写测试,网上下载的驱动测试程序可以正确读写一个字节,但是整个读写就会导致SCL或者SDA一直为低。但是我自己的程序用示波器跟踪SCL和SDA信号一直都是高电平。由于是第一次接触这方面,不知道问题出在哪儿。

    后来终于发现,可以调整驱动从串口打印驱动运行的信息,经过对比两个程序打印的信息,发现我的程序根本没有进入iocontrol的case里面。对比驱动打印的信息,发现我的控制码有错误。  我的驱动中控制码是通过CTL_CODE(FILE_DEVICE_IIC, 0, METHOD_BUFFERED, FILE_ANY_ACCESS)函数得到的,并不是单纯的0,1,2。。。更改控制码以后,发现我自己的程序可以正确读写一个字节了。但是数据量大了还是会出现data abort的问题,由于csdn上有人问过这个问题,我照着他的方法修改了驱动以后,还是不对。但是如果修改时钟或者增加一点时延的话,可以连续读写的数据量会有适当的增加。我觉得这个跟iic的时序有关系。但是现在不知道应该怎么修改。哎。。还需要好好研究研究,做这方面的朋友我们可以一起讨论。(*^__^*) 嘻嘻……

     

    补充:控制码应该是这样:

    #define FILE_DEVICE_IIC 0x00000004

    #define METHOD_BUFFERED (0)

    #define FILE_ANY_ACCESS (0)

    #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))

    #define IOCTL_IIC_SET_CLOCK CTL_CODE(FILE_DEVICE_IIC, 2, METHOD_BUFFERED, FILE_ANY_ACCESS)

    #define IOCTL_IIC_SET_DELAY CTL_CODE(FILE_DEVICE_IIC, 8, METHOD_BUFFERED, FILE_ANY_ACCESS)

    #define IOCTL_IIC_WRITE CTL_CODE(FILE_DEVICE_IIC, 1, METHOD_BUFFERED, FILE_ANY_ACCESS)

    #define IOCTL_IIC_READ CTL_CODE(FILE_DEVICE_IIC, 0, METHOD_BUFFERED, FILE_ANY_ACCESS)

    #define IOCTL_IIC_SET_MODE CTL_CODE(FILE_DEVICE_IIC, 4, METHOD_BUFFERED, FILE_ANY_ACCESS)

    #define IOCTL_IIC_SET_FILTER CTL_CODE(FILE_DEVICE_IIC, 6, METHOD_BUFFERED, FILE_ANY_ACCESS)

    展开全文
  • visual C++_Turbo C串口通信编程实践

    热门讨论 2010-07-30 09:14:24
    3.2.1使用VARIANT 和SAFEARRAY 数据类型从串口读写数据 3.2.2 MSComm控件能离开对话框独立存在吗? 3.2.3 如何发送接收ASCII值为0和大于128的字符? 3.2.4 在同一程序中用MSComm控件控制多个串口的具体操作方法 ...
  • Linux驱动基础

    2021-03-17 09:57:48
    ​ 在使用printf()函数输出信息的时候,应用软件工程师并不需要知道底层是如何把相应的信息输出到屏幕或者串口上。对他来说,硬件好像是不存在的。实现硬件隐形的工作,就是驱动工程师的工作。 ​ 对设备驱动最通俗...

    Linux驱动基础

    1. Linux设备驱动概述

    1.1. 设备驱动的作用

    ​ 在使用printf()函数输出信息的时候,应用软件工程师并不需要知道底层是如何把相应的信息输出到屏幕或者串口上。对他来说,硬件好像是不存在的。实现硬件隐形的工作,就是驱动工程师的工作。

    ​ 对设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA 通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面。

    ​ 设备驱动充当了硬件和应用软件之间的纽带,应用软件时只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作。在系统没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口,如对串口定义SerialSend()、SerialRecv()。而在有操作系统的情况下,驱动的架构则由相应的操作系统定义,驱动工程师必须按照相应的架构设计驱动,这样,驱动才能良好地整合入操作系统的内核中。

    1.2. 无操作系统时的设备驱动

    ​ 一个无限循环中夹杂着对设备中断的检测或者对设备的轮询是这种系统中软件的典型架构

    #include <stdio.h>
    int main()
    {
        while(1)
        {
            if(serialInt == 1) //有串口中断
            {
                ProcessSerialInt(); //处理串口中断
                serialInt = 0; //中断标志变量清0
            }
        }
    }
    

    ​ 在这样系统中,虽然不存在操作系统,但是设备驱动依然存在。每一种驱动存在以下两种文件:

    .h : 定义该驱动的数据结构并声明外部变量和函数

    .c : 驱动的具体实现

    ​ 其他模块想要使用该设备时,只需要包含设备驱动的头文件,如serial.h,然后调用其中的外部接口函数。

    #include<serial.h> //包含头文件
    SerialSend("HELLO WORLD",11); //调用接口
    

    ​ 由此,在没有操作系统的情况下,硬件,设备驱动与应用软件的关系如下所示:

    1614838436777
    1.3. 有操作系统时的设备驱动

    ​ 首先,硬件操作的工作仍然是必不可少的,没有这部分,驱动不可能和设备打交道
    ​ 其次,我们还需要将驱动融入内核。为了实现这种融合,必须在所有设备的驱动中设计面向操作系统内核的接口,这样的接口由操作系统规定,对一类设备而言结构一致,独立于具体的设备。

    ​ 由此可见,当系统中存在操作系统的时候,驱动变成了连接硬件和内核的桥梁。如图所示,操作系统的存在势必要求设备驱动附加更多的代码和功能,把单一的“驱使硬件设备行动”变成了操作系统内与硬件交互的模块,它对外呈现为操作系统的API,不再给应用软件工程师直
    接提供接口。

    1614838978807

    使用操作系统有以下好处

    1.能处理多个并发任务

    2.提供内存管理机制

    3.上层更方便调用,对于Linux等操作系统而言,当应用程序通过write()、read() 等函数读写文件就可访问各种字符设备和块设备,而不论设备的具体类型和工作方式。

    1.4. Linux设备驱动
    驱动针对的对象是存储器和外设(包括CPU 内部集成的存储器和外设),而不是针对CPU 内核。Linux 将存储器和外设分为3 个基础大类。
    

    1.字符设备:以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。
    2.块设备:按任意顺序进行访问,以块为单位进行操作,如硬盘、eMMC 等。Linux 的块设备有两种访问方法:一种是类似dd 命令对应的原始块设备,如“ /dev/sdb1”等;另外一种方法是在块设备上建立FAT、EXT4、BTRFS 等文件系统,然后以文件路径如“ /home/barry/hello.txt”的形式进行访问。
    3.网络设备。面向数据包的接收和发送而设计,它并不倾向于对应于文件系统的节点。

    注:除网络设备外,字符设备与块设备都被映射到Linux 文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close() 等即可访问字符设备和块设备。

    2. 驱动设计的硬件基础

    2.1 处理器
    1614842257501
    2.2 存储器
    1614842472875
    2.3. 接口和总线
    2.3.1. 串口

    ​ RS-232、RS-422 与RS-485 都是串行数据接口标准,最初都是由电子工业协会(EIA)制订并发布的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ow03qKr5-1615946217867)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614843227061.png)]

    2.3.2. IIC

    ​ I2C(内置集成电路)总线是由Philips 公司开发的两线式串行总线,I2C 总线简单而有效,设计成本低。I2C 总线支持多主控(Multi-Mastering)模式,任何能够进行发送和接收的设备都可以成为主设备。主控能够控制数据的传输和时钟频率,在任意时刻只能有一个主控。
    ​ 组成I2C 总线的两个信号为数据线SDA 和时钟SCL。为了避免总线信号的混乱,要求各设备连接到总线的输出端必须是开漏输出或集电极开路输出的结构。总线空闲时,上拉电阻使SDA 和SCL 线都保持高电平。根据开漏输出或集电极开路输出信号的“线与”逻辑,I2C总线上任意器件输出低电平都会使相应总线上的信号线变低。

    • 传输逻辑

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ib987Etz-1615946217870)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614908321204.png)]

    ​ 起始和结束信号:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。

    数据传输:主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从(MSB)高位到低位的顺序传输完后,紧接着**从设备将拉低SDA线**,回传给主设备一个**应答位**, 此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。
    

    ​ 一个包的组成:I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。向指定设备发送数据的格式如图所示:(每一最小包数据由9bit组成,8bit内容+1bit ACK, 如果是地址数据,则8bit包含1bit方向)

    • 总线操作

      主设备往从设备中写数据:

    img

    ​ 主设备从从设备中读数据:

    img

    ​ 主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下: img

    • IIC数据传输速率:

      在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s

    • IIC总线需要接上拉电阻:

      1.根据IIC总线规范,总线空闲时两根线都必须为高。

      2.起保护端口的作用。

      3.对总线而言,上拉电阻越大,信号的上升时间就越长,通信速率就越低,反之亦然。但电阻也并不是越小越好,阻值过小的话,总线低电平时电阻上的大电流会增加电路的功耗。此外,电容也会影响信号的上升时间,于是就有了 I2C 总线总电容 400 pf 的限制。

    2.3.3. SPI
    • **简介:**SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性。

    • **通信原理:**SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是

      SDO     – 主设备数据输出,从设备数据输入 对应MOSI master output slave input
      SDI      – 主设备数据输入,从设备数据输出  对应MISO master input slave output
      SCLK   – 时钟信号,由主设备产生
      CS        – 从设备使能信号,由主设备控制
      

      CS: 其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。

      SDI/SDO/SCLK: 通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。

      ​ 要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。 SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据,也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。

      ​ 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些,以及没有指定的流控制,没有应答机制确认是否接收到数据。

    • **数据交换和内部框图:**SPI数据传输一般由数据交换构成,在主控制器向从设备发送数据时,从设备也向主控制器发送数据。因此主控制器的内部移位寄存器和从设备都采用环形设置。在数据交换之前,主控制器和从设备会将存储器数据加载至它们的内部移位寄存器,收到时钟信号后,主控制器先通过 MOSI 线路时钟输出其移位寄存器的 MSB。同时从设备会读取位于 SIMO 的主控器第一位元,将其存储在存储器中,然后通过 SOMI 时钟输出其 MSB。主控制器可读取位于 MISO 的从设备第一位元,并将其存储在存储器中,以便后续处理。整个过程将一直持续到所有位元完成交换,而主控器则可让时钟空闲并通过 /SS 禁用从设备。SPI 数据速率一般在 1 到 70MHz 的范围内,字长为从 8 位及 12 位到这两个值的倍数。
      在这里插入图片描述

    2.3.4. USB

    ​ USB(通用串行总线)是Intel、Microsoft 等厂商为解决计算机外设种类的日益增加与有限的主板插槽和端口之间的矛盾而于1995 年提出的,它具有数据传输率高、易扩展、支持即插即用和热插拔的优点,目前已得到广泛应用。

    ​ USB 1.1 包含全速和低速两种模式,低速方式的速率为1.5Mbit/s,支持一些不需要很大数据吞吐量和很高实时性的设备,如鼠标等。全速模式为12Mbit/s,可以外接速率更高的外设。在USB 2.0 中,增加了一种高速方式,数据传输率达到480Mbit/s,半双工,可以满足更高速外设的需要。而USB 3.0(也被认为是Super Speed USB)的最大传输带宽高达5.0Gbit/s(即640MB/s),全双工。

    ​ USB 2.0 总线的机械连接非常简单,采用4 芯的屏蔽线,一对差分线(D+、D-)传送信号,另一对(VBUS、电源地)传送+5V 的直流电。USB 3.0 线缆则设计了8 条内部线路,除VBUS、电源地之外,其余3 对均为数据传输线路。其中保留了D+ 与D- 这两条兼容USB2.0 的线路,新增了SSRX 与SSTX 专为USB 3.0 所设的线路。

    2.3.5. 以太网接口

    ​ 以太网接口由MAC(以太网媒体接入控制器)和PHY(物理接口收发器)组成。以太网MAC 由IEEE 802.3 以太网标准定义,实现了数据链路层。常用的MAC 支持10Mbit/s 或100Mbit/s 两种速率。吉比特以太网(也称为千兆位以太网)是快速以太网的下一代技术,将网速提高到了1000 Mbit/s。千兆位以太网以 IEEE 802.3z 和802.3ab 发布,作为IEEE 802.3标准的补充。
    ​ MAC 和PHY 之间采用MII(媒体独立接口)连接,它是IEEE-802.3 定义的以太网行业标准,包括1 个数据接口与MAC 和PHY 之间的1 个管理接口。数据接口包括分别用于发送和接收的两条独立信道,每条信道都有自己的数据、时钟和控制信号,MII 数据接口总共需要16 个信号。MII 管理接口包含两个信号,一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。
    ​ 一个以太网接口的硬件电路原理如图2.15 所示,从CPU 到最终接口依次为CPU、MAC、PHY、以太网隔离变压器、RJ45 插座。以太网隔离变压器是以太网收发芯片与连接器之间的磁性组件,在其两者之间起着信号传输、阻抗匹配、波形修复、信号杂波抑制和高电压隔离作用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wLcTLExU-1615946217877)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614911669689.png)]

    2.3.6. PCI和PCI-E

    ​ PCI(外围部件互连)是由Intel 于1991 年推出的一种局部总线,作为一种通用的总线接口标准,它在目前的计算机系统中得到了非常广泛应用。

    2.3.7. SD和SDIO

    ​ SD(Secure Digital)是一种关于Flash 存储卡的标准,也就是一般常见的SD 记忆卡,在设计上MMC(Multi-Media Card)保持了兼容。SDHC(SD High Capacity)是大容量SD 卡,支持的最大容量为32GB。2009 年发布的SDXC(SD eXtended Capacity)则支持最大2TB 大小的容量。
    ​ SDIO(Secure Digital Input and Output Card,安全数字输入输出卡)在SD 标准的基础上,定义了除存储卡以外的外设接口。SDIO 主要有两类应用——可移动和不可移动。不可移动设备遵循相同的电气标准,但不要求符合物理标准。现在已经有非常多的手机或者手持装置都支持SDIO 的功能,以连接WiFi、蓝牙、GPS 等模块。

    2.3.8. CPLD和FPGA

    ​ CPLD(复杂可编程逻辑器件)由完全可编程的与或门阵列以及宏单元构成。

    ​ 与CPLD 不同,FPGA(现场可编程门阵列)基于LUT(查找表)工艺。查找表本质上是一片RAM,当用户通过原理图或HDL(硬件描述语言)描述了一个逻辑电路以后,FPGA 开发软件会自动计算逻辑电路所有可能的结果,并把结果事先写入RAM。这样,输入一组信号进行逻辑运算就等于输入一个地址进行查表以输出对应地址的内容。# Linux驱动基础

    1. Linux设备驱动概述

    1.1. 设备驱动的作用

    ​ 在使用printf()函数输出信息的时候,应用软件工程师并不需要知道底层是如何把相应的信息输出到屏幕或者串口上。对他来说,硬件好像是不存在的。实现硬件隐形的工作,就是驱动工程师的工作。

    ​ 对设备驱动最通俗的解释就是“驱使硬件设备行动”。驱动与底层硬件直接打交道,按照硬件设备的具体工作方式,读写设备的寄存器,完成设备的轮询、中断处理、DMA 通信,进行物理内存向虚拟内存的映射等,最终让通信设备能收发数据,让显示设备能显示文字和画面。

    ​ 设备驱动充当了硬件和应用软件之间的纽带,应用软件时只需要调用系统软件的应用编程接口(API)就可让硬件去完成要求的工作。在系统没有操作系统的情况下,工程师可以根据硬件设备的特点自行定义接口,如对串口定义SerialSend()、SerialRecv()。而在有操作系统的情况下,驱动的架构则由相应的操作系统定义,驱动工程师必须按照相应的架构设计驱动,这样,驱动才能良好地整合入操作系统的内核中。

    1.2. 无操作系统时的设备驱动

    ​ 一个无限循环中夹杂着对设备中断的检测或者对设备的轮询是这种系统中软件的典型架构

    #include <stdio.h>
    int main()
    {
        while(1)
        {
            if(serialInt == 1) //有串口中断
            {
                ProcessSerialInt(); //处理串口中断
                serialInt = 0; //中断标志变量清0
            }
        }
    }
    

    ​ 在这样系统中,虽然不存在操作系统,但是设备驱动依然存在。每一种驱动存在以下两种文件:

    .h : 定义该驱动的数据结构并声明外部变量和函数

    .c : 驱动的具体实现

    ​ 其他模块想要使用该设备时,只需要包含设备驱动的头文件,如serial.h,然后调用其中的外部接口函数。

    #include<serial.h> //包含头文件
    SerialSend("HELLO WORLD",11); //调用接口
    

    ​ 由此,在没有操作系统的情况下,硬件,设备驱动与应用软件的关系如下所示:

    1614838436777
    1.3. 有操作系统时的设备驱动

    ​ 首先,硬件操作的工作仍然是必不可少的,没有这部分,驱动不可能和设备打交道
    ​ 其次,我们还需要将驱动融入内核。为了实现这种融合,必须在所有设备的驱动中设计面向操作系统内核的接口,这样的接口由操作系统规定,对一类设备而言结构一致,独立于具体的设备。

    ​ 由此可见,当系统中存在操作系统的时候,驱动变成了连接硬件和内核的桥梁。如图所示,操作系统的存在势必要求设备驱动附加更多的代码和功能,把单一的“驱使硬件设备行动”变成了操作系统内与硬件交互的模块,它对外呈现为操作系统的API,不再给应用软件工程师直
    接提供接口。

    1614838978807

    使用操作系统有以下好处

    1.能处理多个并发任务

    2.提供内存管理机制

    3.上层更方便调用,对于Linux等操作系统而言,当应用程序通过write()、read() 等函数读写文件就可访问各种字符设备和块设备,而不论设备的具体类型和工作方式。

    1.4. Linux设备驱动
    驱动针对的对象是存储器和外设(包括CPU 内部集成的存储器和外设),而不是针对CPU 内核。Linux 将存储器和外设分为3 个基础大类。
    

    1.字符设备:以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。
    2.块设备:按任意顺序进行访问,以块为单位进行操作,如硬盘、eMMC 等。Linux 的块设备有两种访问方法:一种是类似dd 命令对应的原始块设备,如“ /dev/sdb1”等;另外一种方法是在块设备上建立FAT、EXT4、BTRFS 等文件系统,然后以文件路径如“ /home/barry/hello.txt”的形式进行访问。
    3.网络设备。面向数据包的接收和发送而设计,它并不倾向于对应于文件系统的节点。

    注:除网络设备外,字符设备与块设备都被映射到Linux 文件系统的文件和目录,通过文件系统的系统调用接口open()、write()、read()、close() 等即可访问字符设备和块设备。

    2. 驱动设计的硬件基础

    2.1 处理器
    1614842257501
    2.2 存储器
    1614842472875
    2.3. 接口和总线
    2.3.1. 串口

    ​ RS-232、RS-422 与RS-485 都是串行数据接口标准,最初都是由电子工业协会(EIA)制订并发布的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-877oneB2-1615946218961)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614843227061.png)]

    2.3.2. IIC

    ​ I2C(内置集成电路)总线是由Philips 公司开发的两线式串行总线,I2C 总线简单而有效,设计成本低。I2C 总线支持多主控(Multi-Mastering)模式,任何能够进行发送和接收的设备都可以成为主设备。主控能够控制数据的传输和时钟频率,在任意时刻只能有一个主控。
    ​ 组成I2C 总线的两个信号为数据线SDA 和时钟SCL。为了避免总线信号的混乱,要求各设备连接到总线的输出端必须是开漏输出或集电极开路输出的结构。总线空闲时,上拉电阻使SDA 和SCL 线都保持高电平。根据开漏输出或集电极开路输出信号的“线与”逻辑,I2C总线上任意器件输出低电平都会使相应总线上的信号线变低。

    • 传输逻辑

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MbW8V3GP-1615946218961)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614908321204.png)]

    ​ 起始和结束信号:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。

    数据传输:主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从(MSB)高位到低位的顺序传输完后,紧接着**从设备将拉低SDA线**,回传给主设备一个**应答位**, 此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。
    

    ​ 一个包的组成:I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。向指定设备发送数据的格式如图所示:(每一最小包数据由9bit组成,8bit内容+1bit ACK, 如果是地址数据,则8bit包含1bit方向)

    • 总线操作

      主设备往从设备中写数据:

    img

    ​ 主设备从从设备中读数据:

    img

    ​ 主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下: img

    • IIC数据传输速率:

      在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s

    • IIC总线需要接上拉电阻:

      1.根据IIC总线规范,总线空闲时两根线都必须为高。

      2.起保护端口的作用。

      3.对总线而言,上拉电阻越大,信号的上升时间就越长,通信速率就越低,反之亦然。但电阻也并不是越小越好,阻值过小的话,总线低电平时电阻上的大电流会增加电路的功耗。此外,电容也会影响信号的上升时间,于是就有了 I2C 总线总电容 400 pf 的限制。

    2.3.3. SPI
    • **简介:**SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性。

    • **通信原理:**SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是

      SDO     – 主设备数据输出,从设备数据输入 对应MOSI master output slave input
      SDI      – 主设备数据输入,从设备数据输出  对应MISO master input slave output
      SCLK   – 时钟信号,由主设备产生
      CS        – 从设备使能信号,由主设备控制
      

      CS: 其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。

      SDI/SDO/SCLK: 通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,SDI,SDO则基于此脉冲完成数据传输。数据输出通过 SDO线,数据在时钟上升沿或下降沿时改变,在紧接着的下降沿或上升沿被读取。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。

      ​ 要注意的是,SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。 SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据,也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。

      ​ 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些,以及没有指定的流控制,没有应答机制确认是否接收到数据。

    • **数据交换和内部框图:**SPI数据传输一般由数据交换构成,在主控制器向从设备发送数据时,从设备也向主控制器发送数据。因此主控制器的内部移位寄存器和从设备都采用环形设置。在数据交换之前,主控制器和从设备会将存储器数据加载至它们的内部移位寄存器,收到时钟信号后,主控制器先通过 MOSI 线路时钟输出其移位寄存器的 MSB。同时从设备会读取位于 SIMO 的主控器第一位元,将其存储在存储器中,然后通过 SOMI 时钟输出其 MSB。主控制器可读取位于 MISO 的从设备第一位元,并将其存储在存储器中,以便后续处理。整个过程将一直持续到所有位元完成交换,而主控器则可让时钟空闲并通过 /SS 禁用从设备。SPI 数据速率一般在 1 到 70MHz 的范围内,字长为从 8 位及 12 位到这两个值的倍数。
      在这里插入图片描述

    2.3.4. USB

    ​ USB(通用串行总线)是Intel、Microsoft 等厂商为解决计算机外设种类的日益增加与有限的主板插槽和端口之间的矛盾而于1995 年提出的,它具有数据传输率高、易扩展、支持即插即用和热插拔的优点,目前已得到广泛应用。

    ​ USB 1.1 包含全速和低速两种模式,低速方式的速率为1.5Mbit/s,支持一些不需要很大数据吞吐量和很高实时性的设备,如鼠标等。全速模式为12Mbit/s,可以外接速率更高的外设。在USB 2.0 中,增加了一种高速方式,数据传输率达到480Mbit/s,半双工,可以满足更高速外设的需要。而USB 3.0(也被认为是Super Speed USB)的最大传输带宽高达5.0Gbit/s(即640MB/s),全双工。

    ​ USB 2.0 总线的机械连接非常简单,采用4 芯的屏蔽线,一对差分线(D+、D-)传送信号,另一对(VBUS、电源地)传送+5V 的直流电。USB 3.0 线缆则设计了8 条内部线路,除VBUS、电源地之外,其余3 对均为数据传输线路。其中保留了D+ 与D- 这两条兼容USB2.0 的线路,新增了SSRX 与SSTX 专为USB 3.0 所设的线路。

    2.3.5. 以太网接口

    ​ 以太网接口由MAC(以太网媒体接入控制器)和PHY(物理接口收发器)组成。以太网MAC 由IEEE 802.3 以太网标准定义,实现了数据链路层。常用的MAC 支持10Mbit/s 或100Mbit/s 两种速率。吉比特以太网(也称为千兆位以太网)是快速以太网的下一代技术,将网速提高到了1000 Mbit/s。千兆位以太网以 IEEE 802.3z 和802.3ab 发布,作为IEEE 802.3标准的补充。
    ​ MAC 和PHY 之间采用MII(媒体独立接口)连接,它是IEEE-802.3 定义的以太网行业标准,包括1 个数据接口与MAC 和PHY 之间的1 个管理接口。数据接口包括分别用于发送和接收的两条独立信道,每条信道都有自己的数据、时钟和控制信号,MII 数据接口总共需要16 个信号。MII 管理接口包含两个信号,一个是时钟信号,另一个是数据信号。通过管理接口,上层能监视和控制PHY。
    ​ 一个以太网接口的硬件电路原理如图2.15 所示,从CPU 到最终接口依次为CPU、MAC、PHY、以太网隔离变压器、RJ45 插座。以太网隔离变压器是以太网收发芯片与连接器之间的磁性组件,在其两者之间起着信号传输、阻抗匹配、波形修复、信号杂波抑制和高电压隔离作用。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-boE28mAn-1615946218968)(C:\Users\guoyiyu\AppData\Roaming\Typora\typora-user-images\1614911669689.png)]

    2.3.6. PCI和PCI-E

    ​ PCI(外围部件互连)是由Intel 于1991 年推出的一种局部总线,作为一种通用的总线接口标准,它在目前的计算机系统中得到了非常广泛应用。

    2.3.7. SD和SDIO

    ​ SD(Secure Digital)是一种关于Flash 存储卡的标准,也就是一般常见的SD 记忆卡,在设计上MMC(Multi-Media Card)保持了兼容。SDHC(SD High Capacity)是大容量SD 卡,支持的最大容量为32GB。2009 年发布的SDXC(SD eXtended Capacity)则支持最大2TB 大小的容量。
    ​ SDIO(Secure Digital Input and Output Card,安全数字输入输出卡)在SD 标准的基础上,定义了除存储卡以外的外设接口。SDIO 主要有两类应用——可移动和不可移动。不可移动设备遵循相同的电气标准,但不要求符合物理标准。现在已经有非常多的手机或者手持装置都支持SDIO 的功能,以连接WiFi、蓝牙、GPS 等模块。

    2.3.8. CPLD和FPGA

    ​ CPLD(复杂可编程逻辑器件)由完全可编程的与或门阵列以及宏单元构成。

    ​ 与CPLD 不同,FPGA(现场可编程门阵列)基于LUT(查找表)工艺。查找表本质上是一片RAM,当用户通过原理图或HDL(硬件描述语言)描述了一个逻辑电路以后,FPGA 开发软件会自动计算逻辑电路所有可能的结果,并把结果事先写入RAM。这样,输入一组信号进行逻辑运算就等于输入一个地址进行查表以输出对应地址的内容。

    展开全文
  • 3.2.1 使用variant 和safearray 数据类型从串口读写数据 56 .3.2.2 mscomm控件能离开对话框独立存在吗 59 3.2.3 如何发送接收ascii值为0和大于128的字符 60 3.2.4 在同一程序中用mscomm控件控制多个串口的具体...
  • 3.2.1 使用variant 和safearray 数据类型从串口读写数据 56 .3.2.2 mscomm控件能离开对话框独立存在吗 59 3.2.3 如何发送接收ascii值为0和大于128的字符 60 3.2.4 在同一程序中用mscomm控件控制多个串口的具体...
  • 3.2.1 使用variant 和safearray 数据类型从串口读写数据 56 .3.2.2 mscomm控件能离开对话框独立存在吗 59 3.2.3 如何发送接收ascii值为0和大于128的字符 60 3.2.4 在同一程序中用mscomm控件控制多个串口的具体...
  • 6.9.2 pc与电机驱动系统串口通信程序设计用软、硬件 258 6.9.3 pc与电机驱动系统串口通信程序硬件线路图 258 6.9.4 设计任务 259 6.9.5 任务实现 259 6.10 pc与常用测试仪器串口通信程序设计 262 ...
  • 6.9.2 pc与电机驱动系统串口通信程序设计用软、硬件 258 6.9.3 pc与电机驱动系统串口通信程序硬件线路图 258 6.9.4 设计任务 259 6.9.5 任务实现 259 6.10 pc与常用测试仪器串口通信程序设计 262 ...
  • 书中精选来自工程实践的应用范例,主要涵盖串口通信的理论基础、Visual c++集成开发环境简介、MSComm控件串口编程、Windows API串口编程、TAPI通信编程、串口实现双机互连、串口调试精灵、串口控制Modem设备、串口...
  • 用ubuntu系统来做硬件控制的时候,常使用USB串口与传感器或者驱动器进行信息通信。ubuntu系统在开机的时候会为每一个插入的USB设备自动升序命名,如ttyUSB0、ttyUSB1......(有些也会命名为ttyACM0、ttyACM1......)...
  • 3.2.1 使用VARIANT 和SAFEARRAY 数据类型从串口读写数据 56 3.2.2 MSComm控件能离开对话框独立存在吗 59 3.2.3 如何发送接收ASCII值为0和大于128的字符 60 3.2.4 在同一程序中用MSComm控件控制多个串口的具体操作...
  • 3.4 采用事件驱动方式的编程方法 61 3.4.1 定义全局变量 61 3.4.2 打开串口及开启事件线程 62 3.4.3 发送数据 64 3.4.4 自定义消息函数读取数据 65 3.4.5 关闭串口及关闭事件线程 67 3.5 编程实例 67 3.5.1 程序功能...
  • 用ubuntu系统来做硬件控制的时候,常使用USB串口与传感器或者驱动器进行信息通信。ubuntu系统在开机的时候会为每一个插入的USB设备自动升序命名,如ttyUSB0、ttyUSB1......(有些也会命名为ttyACM0、ttyACM1......)...
  • 3.2.1 使用VARIANT 和SAFEARRAY 数据类型从串口读写数据 56 3.2.2 MSComm控件能离开对话框独立存在吗 59 3.2.3 如何发送接收ASCII值为0和大于128的字符 60 3.2.4 在同一程序中用MSComm控件控制多个串口的具体操作...
  • Linux终端设备驱动

    2010-12-17 20:08:00
    在Linux系统中,终端设备非常重要,没有终端设备,系统将无法向用户反馈信息,Linux中包含控制台、串口和伪终端3类终端设备。14.1节阐述了终端设备的概念及分类,14.2节给出了Linux终端设备驱动的框架结构,重点描述...
  • 、虚拟串口驱动程序、摄像头驱动程序、SDIO驱动程序进行了详细的介绍,本书最大的特色在于每一节 的例子都是经过精挑细选的,具有很强的针对性。力求让读者通过亲自动手实验,掌握各类Windows驱动 程序的开发技巧...
  • SigmaTel、Matrox等)的主板、显卡、声卡、网卡、调制解调器、摄像头、无线、打印机、扫描仪、读卡器、阵列卡、蓝牙、手写板、读写器、USB、1394、Bluetooth、Display、Image、MEDIA、Modem、Net、PCMCIA、SCSI ...
  • 同时,还针对流行的PCI驱动程序、USB驱动程序、虚拟串口驱动程序、摄像头驱动程序、SDIO驱动程序进行了详细的介绍,本书最大的特色在于每一节的例子都是经过精挑细选的,具有很强的针对性。力求让读者通过亲自动手...
  • 1 、随便调用了一个串口读写,运行程序就开始报错,Error 1073807202 occur at property Node in VISA configure serial port (Instr).vi-0829 信息传输B.vi,如图1所示。 图1:报错截图 原因:LabVIEW里使用VISA...
  • 本章知识点 在 Linux 系统中,终端设备非常重要,没有终端设备,系统将无法向用户反馈信息,Linux 中包含控制台、串口和伪终端 3 类终端设备。 1、终端设备的概念及分类, Linux 终端设备驱动的框架架构,重点...
  • 大部分驱动需要 -- 除了读写设备的能力 -- 还要通过设备驱动进行各种硬件控制的能力. 大部分设备可进行超出简单的数据传输之外的操作; 用户空间必须常常能够请求, 例如, 设备锁上它的门,弹出它的介质, 报告...
  • 程序功能说明:1、通过I2C硬件查询方式驱动AM2311。2、获取AM2311测得的温度和湿度并串口打印输出。3、使用I2C1。程序注意事项:1、AM2311在测试完数据后会进入一个休眠模式,程序需要向AM2311发送一个任意的数据将...
  • 最近在调试Camera驱动时候,发现通过IIC读写Camera设备时,总是出现问题。跟踪调试发现,对Camera设备的写操作基本不会出现问题,但是读操作有时候正常,有时候不正常。从串口输入调试信息发现,读操作总是出现“ACK...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 153
精华内容 61
关键字:

驱动读写串口信息