串口驱动程序设计详解---串口打开、发送、接收(下)

串口设备驱动 uart_port 中最主要的就是第 235 行的 ops,ops 包含了串口的具体驱动函数,UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,是实实在在的和 UART 寄存器打交道的。串口全称叫做串行接口,通常也叫做 COM 接口,串行接口指的是数据一个一个的顺序传输,通信线路简单。这一套接口并不是针对串口开发的,而是针对所有的终端设备,串口是一种终端设备,计算机系统本地连接的鼠标、键盘也是终端设备,通过 ssh 远程登录连接的伪终端也是终端设备。 阅读详情

上一篇博客分析了串口驱动初始化部分,下面逐步分析串口驱动中的打开串口,数据发送和接收!

初始化主要工作流程:


先来分析串口打开操作流程,还是先上图:



这里分析还是离不开上篇博客中的两张重要的图:



串口操作重要的数据结构:


由上一篇串口驱动分析可知在samsung.c中模块初始化中有一项工作是注册一个串口驱动,


跳到这个函数中uart_register_driver可以看到有一个函数:

retval = tty_register_driver(normal);

跳到这个函数中,这里贴上源码:

int tty_register_driver(struct tty_driver *driver)
{
	int error;
	int i;
	dev_t dev;
	void **p = NULL;
	struct device *d;

	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);
	mutex_unlock(&tty_mutex);

	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
		for (i = 0; i < driver->num; i++) {
			d = tty_register_device(driver, i, NULL);
			if (IS_ERR(d)) {
				error = PTR_ERR(d);
				goto err;
			}
		}
	}
	proc_tty_register_driver(driver);
	driver->flags |= TTY_DRIVER_INSTALLED;
	return 0;

err:
	for (i--; i >= 0; i--)
		tty_unregister_device(driver, i);

	mutex_lock(&tty_mutex);
	list_del(&driver->tty_drivers);
	mutex_unlock(&tty_mutex);

	unregister_chrdev_region(dev, driver->num);
	driver->ttys = NULL;
	driver->termios = NULL;
	kfree(p);
	return error;
}
可以看到这个函数内部实现其实就是注册一个字符设备!
看看这一行:cdev_init(&driver->cdev, &tty_fops);

从这个tty_fops找到串口open函数的接口:


可以看到open操作对应的是tty_open(这里的tty_fops就是字符设备的file_operations)

跳到这个函数中可以看到箭头所指向的一行:


这个ops实际上是struct   tty_operations  类型的:


这里总结一下:应用程序空间的打开串口open操作调用了tty_ops中的tty_open,然后tty_open又对应的调用了uart_ops中的uart_open这个函数,这个函数还是tty层次里面的还不涉及驱动层!

下面跳到uart_open这个函数里面:

static int uart_open(struct tty_struct *tty, struct file *filp)
{
	struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
	struct uart_state *state;
	struct tty_port *port;
	int retval, line = tty->index;

	BUG_ON(!tty_locked());
	pr_debug("uart_open(%d) called\n", line);

	state = uart_get(drv, line);
	if (IS_ERR(state)) {
		retval = PTR_ERR(state);
		goto fail;
	}
	port = &state->port;

	tty->driver_data = state;
	state->uart_port->state = state;
	tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
	tty->alt_speed = 0;
	tty_port_tty_set(port, tty);

	/*
	 * If the port is in the middle of closing, bail out now.
	 */
	if (tty_hung_up_p(filp)) {
		retval = -EAGAIN;
		port->count--;
		mutex_unlock(&port->mutex);
		goto fail;
	}

	/*
	 * Make sure the device is in D0 state.
	 */
	if (port->count == 1)
		uart_change_pm(state, 0);

	/*
	 * Start up the serial port.
	 */
	retval = uart_startup(tty, state, 0);

	/*
	 * If we succeeded, wait until the port is ready.
	 */
	mutex_unlock(&port->mutex);
	if (retval == 0)
		retval = tty_port_block_til_ready(port, tty, filp);

fail:
	return retval;

可以看到这个函数调用一个uart_startup函数,这个函数任然是tty里面的还不涉及串口驱动层!


这个函数比较长,截取重要的部分,上篇文章中也有提到过,看第二个红色箭头所指部分:uport的ops的startup

uport类型可以从第一个箭头所指部分看到是struct uart_port类型的,一个uart_port对应的是一个串口,在这个数据结构中是针对这个串口的函数操作集,而这些函数操作集就是由串口驱动来实现的!

所以现在就是要找出串口操作集里面的start_up,而这个就要从驱动里面去找了!

又串口初始化分析可以找到串口初始化中的port是从probe这个函数获取的:


而这个数组结构定义如下:每个port代表一个串口


下面再来看看这个红色箭头所指向的串口驱动操作集里面的内容:



上面截图中信息量比较多,左边和右边的可以对比着看,一个是函数指针,一个是函数指针对应的函数名字!

至此总结一下串口open操作的函数调用关系:

open ---> tty_open(tty_ops里面的) ---> uart_open(uart_ops里面的) ---> uart_start  --->   上图中红色箭头所指部分(这个就是相当于驱动层里面的open)

下面跳转到这个函数中:


代码量不多对照着代码分析总结如下图:


以上就是整个串口打开操作的实现!


下面再来分析串口驱动的发送操作,还是先上图:


整体分析流程和open函数一样!

write---> tty_write ---> n_tty_write(线路规程里面) ---> uart_write ---> uart_start ---> 向上看第四张图,也就是驱动层对应的write操作


这里直接跳到s3c24xx_serial_start_tx这个函数:


从上面的源码中可以看到这里没有操作寄存器的发送部分!这里有个小窍门!关键之处在enable_irq(ourport->tx_irq)这个地方!

当发送中断时会有中断处理程序来处理发送!这里的只是起一个激活中断发送处理程序!


在这个函数中可以看到注册了一个发送中断处理程序!跳到这个函数里面看看


上面的代码中可以看到寄存器操作部分!总体简要总结:应用层的write串口操作最终会调用上面的s3c24xx_serial_start_tx函数,而这个函数仅仅是起一个激活发送中断的功能,具体数据发送过程又是在注册发送中断来实现的!

下面这张图就是根据上面的这个函数实现的总结:




分析完了发送,下面来分析接收read函数对应的操作:


函数整个调用流程对应的和write一样!

有了上面的基础,下面可以来思考下面的两个问题:

1. tty子系统是如何响应用户的读数据请求?

2. 串口驱动又是如何来接收处理的?


其实是同write操作一样!下面还是简要的分析一下:

做为响应用户空间的read函数的第一个结点还是struct  file_operations结构中的tty_read:


下面跳到这个函数里面来看看源码:


红色箭头部分可以看到这一行其实是调用了线路规程里面的read,ops的数据类型:


再来看看read所在的结构体类型:


其实这个被调用的read函数对应的是线路规程里面的read.

下面再来看看线路规程struct tty_ldisc_ops tty_ldisc_N_TTY这个结构:


可以看到这里tty_read又由线路规程里面的n_tty_read来响应!

n_tty_read这个函数代码比较多!这里暂不截全图!只分析其中比较重要的三个部分


箭头所指部分是设置应用程序这个进程为阻塞状态!(这行代码还不会立即阻塞)

然后箭头下面的第二个if语句里面有个判断,input_available_p判断是否有数据读!


当没有数据可读的时候,将会阻塞,不会被CPU调度占用CPU。结合上面的就是如果没数据就让其阻塞生效

如果有数据将会从read_buf中读走数据


看看这个函数内部实现:


其实这个read_buf和驱动是紧密相关的,当驱动里面有数据的时候,驱动就将数据往read_buf里面送!下面再来看驱动是怎么收到数据的!

还是和write函数一样在驱动模块初始化里面的有个注册发送中断函数,然后跳到s3c24xx_serial_startup函数

在这个函数里面有个request_irq函数,这个函数里面其中一个函数指针参数就是s3c24xx_serial_rx_chars函数



分析完了下面来着手撸驱动代码了!!!这个只是在原有代码的基础上根据上面的分析流程来自己实现串口驱动的重要部分:

这里来实现两个最核心的功能:

1. 串口驱动发送中断处理程序

2. 串口驱动接收中断处理程序


打开samsung.c,找到static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)发送中断函数,将里面的代码删除,然后根据上面的分析流程实现!


发送中断处理程序代码:


第一步:1. 判断x_char是否为0,如果不为0,则发送x_char


x_char成员在port结构中,为xon或者xoff(这个是和流控相关的)

<span style="font-size:18px;">static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
	struct s3c24xx_uart_port *ourport = id;
	struct uart_port *port = &ourport->port;
	struct circ_buf *xmit = &port->state->xmit;//循环缓冲
	int count = 256;
	
	//1. 判断x_char是否为0,如果不为0,则发送x_char
	if(port->x_char)
	{
		wr_regb(port, S3C2410_UTXH,  port->x_char);//发送一个字符实际上就是将数据写到UTXH寄存器里面
		goto out;
	}
	
	//2. 判断发送缓冲是否为空或者驱动被设置为停止发送的状态 则取消发送
	if( (uart_circ_empty(xmit)) || (uart_tx_stopped(port)) )
	{
		s3c24xx_serial_stop_tx(port);
		goto out;
	}
	
	//3. 循环发送,循环条件:发送缓冲不为空
	while( (!uart_circ_empty(xmit)) || (count--) > 0 )
	{
		//3.1 发送fifo如果满,退出发送
		if( rd_regl(port, S3C2410_UFSTAT) & (1 << 14) )//这里要查datasheet UFSTAT寄存器14位
			break;
				
		//3.2 将要发送的字符写入发送寄存器
		wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);//从尾巴里面取出数据
		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);//循环,如果到最后一位又从第一位开始发送
		
		//3.3 修改循环缓冲的尾部位置
		port->icount.tx++;//更新发送的统计量
	}
	
	//4. 如果发送缓冲中的剩余数据量uart_circ_chars_pending<256
	//则唤醒之前阻塞的发送进程uart_write_wakeup
	if (uart_circ_chars_pending(xmit) < 256)
		uart_write_wakeup(port);

	
	//5. 如果发送缓冲为空,则关闭发送使能
	if (uart_circ_empty(xmit))
		s3c24xx_serial_stop_tx(port);
		
out:
	
	return IRQ_HANDLED;//函数出口,表示中断已经处理
}</span>
上面的代码除了参考之前的分析流程图还有之前的源码,还有datasheet,这里就不一一截图祥举了!

然后make uImage ARCH=arm COMPELE_CROSS=arm-linux-编译内核源码!将uImage下载到开发板启动内核!会发现这里有个小问题!不过对照内核源码看看可以解决!

串口驱动接收中断处理程序:



s3c24xx_serial_rx_chars1111(int irq, void *dev_id)
{
	struct s3c24xx_uart_port *ourport = dev_id;
	struct uart_port *port = &ourport->port;
	struct tty_struct *tty = port->state->port.tty;
	unsigned int ufcon, ch, flag, ufstat, uerstat;
	int max_count = 64;//一次最多接收的字符数

	while (max_count-- > 0) {
		ufcon = rd_regl(port, S3C2410_UFCON);//1. 读取UPCON寄存器
		ufstat = rd_regl(port, S3C2410_UFSTAT);//2. 读取UPSTAT寄存器
		
		//3. 如果接收fifo里的数据量为0,则退出
		if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
			break;

		uerstat = rd_regl(port, S3C2410_UERSTAT);//4. 读取UERSTAT寄存器
		ch = rd_regb(port, S3C2410_URXH);//取出字符

		if (port->flags & UPF_CONS_FLOW) {//6. 流控制处理
			int txe = s3c24xx_serial_txempty_nofifo(port);

			if (rx_enabled(port)) {
				if (!txe) {
					rx_enabled(port) = 0;
					continue;
				}
			} else {
				if (txe) {
					ufcon |= S3C2410_UFCON_RESETRX;
					wr_regl(port, S3C2410_UFCON, ufcon);
					rx_enabled(port) = 1;
					goto out;
				}
				continue;
			}
		}

		/* insert the character into the buffer */

		flag = TTY_NORMAL;
		port->icount.rx++;

		if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
			dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
			    ch, uerstat);

			/* check for break */
			if (uerstat & S3C2410_UERSTAT_BREAK) {
				dbg("break!\n");
				port->icount.brk++;
				if (uart_handle_break(port))
				    goto ignore_char;
			}

			if (uerstat & S3C2410_UERSTAT_FRAME)
				port->icount.frame++;
			if (uerstat & S3C2410_UERSTAT_OVERRUN)
				port->icount.overrun++;

			uerstat &= port->read_status_mask;

			if (uerstat & S3C2410_UERSTAT_BREAK)
				flag = TTY_BREAK;
			else if (uerstat & S3C2410_UERSTAT_PARITY)
				flag = TTY_PARITY;
			else if (uerstat & (S3C2410_UERSTAT_FRAME |
					    S3C2410_UERSTAT_OVERRUN))
				flag = TTY_FRAME;
		}

		if (uart_handle_sysrq_char(port, ch))
			goto ignore_char;
		
		//9. 将接收到的字符发送到串口驱动的buf
		uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,
				 ch, flag);

 ignore_char:
		continue;
	}
	//10. 把串口驱动收到的数据发送到线路规程的read_buf
	tty_flip_buffer_push(tty);

 out:
	return IRQ_HANDLED;
}
还是表示鸭梨山大!任重道远!


Linux tty驱动学习 - UART驱动的write操作流程 从tty核心层到最后把数据写入到硬件,整个操作流程如下:tty_write() -> do_tty_write() -> n_tty_write() -> uart_write() -> serial8250_start_tx() -> serial_out()。也就是从tty核心层到线路规程,然后到tty驱动层,再到UART驱动层,最后到UART端口的输出寄存器中。 首先看tty核心的写操作t 阅读详情

相关推荐

Arduino串口发送接收和串口中断事件

Arduino MEGA开发板。包含4个串口,串口0(即TX0、RX0)出厂默认和USB连接使用。串口1、串口2、串口3用户可以实际使用。串口0的类是Serial;串口1的类是Serial1;串口1的类是Serial2;串口1的类是Serial3。当串口接收缓冲区有数据时自动触发该事件。串口0中断事件serialEvent()。Arduino MEGA开发板:串口1中断事件serialEvent1()。串口2中断事件serialEvent2()。串口3中断事件serialEvent3()。

panjinliang066333的博客 6866

嵌入式UART串口驱动开发实战:从协议底层到Linux tty与DMA避坑指南

串行通信是嵌入式系统中最基础的异步数据传输方式,其核心原理在于收发双方依靠约定波特率实现无时钟线的位同步。由于缺乏时钟线,UART对时序精度、起始位检测和电平标准尤为敏感,这也决定了它在成本、接线与可靠性之间的工程取舍。在裸机与Linux驱动开发中,合理运用环形缓冲区、DMA与IDLE中断,能显著提升数据吞吐能力并降低丢帧风险。从TTL、RS232到RS485差分传输,UART广泛覆盖工业控制、多机通信与跨平台调试等场景。围绕STM32 HAL库首帧丢失、Linux tty溢出排查等高频问题,本文深入拆解U

weixin_29185167的博客 274

ESP32 (UART 接收发送)-串口之接收发送通讯(4)

提示:本博客作为学习笔记,有错误的地方希望指正 文章目录一、ESP32串口介绍二、硬件设计三、实现代码四、串口实验演示结果五、ESP32串口函数API5.1、uart_types.h文件中的内容的API5.2、在uart.h文件中的内容的API 一、ESP32串口介绍   UART 是一种以字符为导向的通用数据链,可以实现设备间的通信。异步传输的意思是不需要在发送数据上添加时钟信息。这也要求发送端和接收端的速率、停止位、奇偶校验位等都要相同,通信才能成功。   一个典型的 UART 帧开始于一个起始位,紧接

believe666的博客 1万+

开发一个RISC-V上的操作系统(三)—— 串口驱动程序(UART)

在写串口的驱动程序之前,我们首先要知道如何与开发板上的串口进行交互,所以我们要先看看我们riscv cpu项目是怎么实现串口模块的。

欢迎一起交流问题! 2736

Linux串口驱动程序(3)-打开设备

先来分析一下串口打开的过程: 1、用户调用open函数打开串口设备文件; 2、在内核中通过tty子系统,把open操作层层传递到串口驱动程序中; 3、在串口驱动程序中的xx_open最终实现这个操作。 这里主要有2个点需要我们重点分析,一个是open函数的传递过程,而是串口驱动程序XXX_open函数的实现。 1.open函数传递 打开uart_register_driver函数,里面就...

小虾米编程 4838

USB串口设备驱动程序整体流程

通过以上几个步骤,USB串口设备驱动程序完成了初始化、数据传输、中断处理和资源释放等关键操作,实现了单片机与计算机之间的串口通信功能。请注意,以上代码示例仅为演示目的,实际的驱动程序代码可能会根据具体的硬件和操作系统进行调整和修改。USB串口设备驱动程序用于实现单片机与计算机之间的串口通信,使得单片机可以通过USB接口与计算机进行数据交换。USB串口设备驱动程序是用于连接单片机与计算机之间的通信的关键组件。本文将详细介绍USB串口设备驱动程序的整体流程,并提供相应的源代码示例。USB串口设备驱动程序概述。

weGuru的博客 827

Linux下tty串口驱动数据的发送、接收过程源码实例详解

简介 本文主要时讲解tty驱动文件的打开、数据的发送、数据的接收相关的源码解析,从用户层到硬件层的源码追踪过程。 具体的读写操作可先大致看一下流程图,下面的源码分析也依然是围绕该流程进行函数追踪。 一、tty数据接收流程分析: 对于tty设备的打开操作,即用户调用read函数来读取设备的文件的数据,首先经过vfs层、字符设备驱动层,到达tty_open()函数,经过tty_core层、serial_core等层后,主要完成一下工作: 1、首先通过dev_t,再tty_driver链表中查找对应的驱动,并返

Luckiers的博客 9876

C# SerialPort串口通信发送接收,处理接收数据完整

本文介绍C#串口通讯,并通过实例讲解使用方法,解决了数据接收不完整问题

qq_30725967的博客 3万+

stm32学习笔记---USART串口外设(代码部分)串口发送/串口发送+接收

第一步,开启时钟,把需要用的USART和GPIO的时钟打开。 第二步,GPIO初始化,把TX配置成复用输出,RX配置成输入。 第三步,配置USART,直接使用一个结构体,就可以把这里所有的参数都配置好了。 第四步,开关控制,如果只需要发送的功能,就直接开启USART初始化就结束了。如果需要接收的功能,可能还需要配置中断。那就在开启USART之前,再加上ITConfig和NVIC的代码就行了。 初始化完成之后,如果要发送数据,调用一个发送函数就行了。如果要接收数据,就调用接收的函数。如果要获取发送和接收的状态

xiaobaivera的博客 7236

USB-232 串口驱动程序安装与应用——以PL2303芯片为例

本文还有配套的精品资源,点击获取 简介:本简介主要讲述PL2303芯片作为USB到UART桥接器,在USB-232转串口设备中的应用。PL2303广泛用于USB到串口适配器,使得电脑可以通过USB接口与使用串口的设备进行通信。特定驱动程序"PL-2303 Driver Installer.exe"专为Windows XP操作系统设计,兼容帝特(Ditek)或力特(Ritek...

weixin_35266799的博客 5741

Zynq裸机调试RTL8211FS网卡踩坑记:从ping不通到完美兼容的完整代码修改

本文详细记录了在Zynq平台上使用Xilinx SDK(XSDK)调试RTL8211FS网卡的完整过程,从最初的ping不通问题到最终实现完美兼容的解决方案。通过分析PHY芯片的寄存器配置差异,提供了关键代码修改和调试技巧,帮助开发者快速解决类似问题。

weixin_42550597的博客 418

USART串口协议——串口发送接收以及串口收发数据包

Receiver/Transmitter)通用同步/异步收发器(1)USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里(2)自带波特率发生器,最高达4.5Mbits/s(3)可配置数据位长度(8/9)、停止位长度(4)可选校验位(无校验/奇校验/偶校验)(5)支持同步模式硬件流控制DMA智能卡IrDA(用于红外通信的)LIN(局域网的通信协议)

qq_45419343的博客 4669

[0352]基于JAVA的停车场收费智慧管理系统的设计与实现

其次,从经营管理层面考虑,本系统涵盖了丰富的月卡管理和财务结算功能,如月卡用户管理、缴费、注销、挂失、解挂、补办等全流程服务,以及临保和月保的结算汇总与欠费查询等功能,这不仅有助于规范停车场的日常运营管理,更能有效提高财务管理效率,降低运营成本,实现精细化、数字化的财务管控。综上所述,本课题计划研发的基于Java的停车场收费智慧管理系统将在现有研究成果的基础上,重点优化月卡用户全生命周期管理和财务统计分析功能,以满足现代停车场高效、精准、安全的管理需求,紧跟行业发展趋势,为推动智慧城市的建设贡献一份力量。

muyu2980的专栏 589

使用串口实现接收和发送功能

使用串口实现接收和发送功能 一、前言 本文以串口-USART1为例,初步实现接收和发送功能,使用的是stm32f1板子,基于火哥教学做的自我小结。电脑设备需要安装串口调试助手和USB转串口CH340G的驱动。 二、正文 1.硬件设计: 将 CH340G 的 TXD 引脚与 USART1 的 RX 引脚连接, CH340G 的 RXD 引脚与USART1 的 TX 引脚连接,此处已在开发板上配置好了,我们只需进行编程。 ————————————————————————————————— USART1的引脚重

zyxnb的博客 5997

ADS学习笔记 1. 功率放大器设计

基于ADS2023 update2的学习笔记1.功率放大器设计

u014087103的博客 1万+

PCB 传输线教程(上):基于 Polar Si9000 与嘉立创工艺的传输线设计

理论线宽是W1,但在实际制作时,铜线顶部会有部分被刻蚀掉,表面被刻蚀后的宽度表示为W2,一般取经验值W2=W1-1,单位为 mil,Si9000 里面默认也是采用 W1,W2 相差 1 mil 的方式进行计算。表面阻抗结构就是简单的在一个平面上走信号线,以另外一个平面为地平面,信号线所在平面不进行铺铜(或铺铜间距大到可以忽略不计),这是最简单的一种结构,只需要知道介质层的厚度和介电常数、信号线厚度就可以进行阻抗计算。这个也是经验值,一般保持默认,或者可以填入 C1 相同的值(即忽略堆积)。

Surrea1的博客 1万+

FPGA串口通信下——串口接收+发送

FPGA实现串口发送和接收

m0_51497090的博客 2520

基于多物理参数数据融合和先进人工智能算法的锂电池热失控监测传感器

基于多物理参数数据融合和先进人工智能算法的锂电池热失控监测传感器是多种方案中的优选项!是一种快速、准确、可靠、应用广泛的传感方案!可有效监测锂离子电池热失控风险,保障化学能储能电站安全!

chicheng_danxin的博客 2411

USART串口(发送和接收)

单片机的串口可以使单片机与单片机,单片机与电脑,单片机与各种各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。其中,单片机和电脑通信,是串口的一大优势,可以接电脑屏幕,非常适合调试程序,打印信息。串口的硬件电路,需要其中一个设备的发送端TX连接另外一个设备的接收端RX,接收端RX连接另一个设备的发送端TX。当只需单向的数据传输时,可以只接一根通信线。TX和RX都是单端信号,它们的高低电平都是相对于GND的。所以串口通信的TX,RX,GND都是必须要连接的。

2303_78660417的博客 2991

杰理的蓝牙芯片的key是什么?以及该如何添加key?杰理key文件原理

很多的工程师,在开发杰理蓝牙芯片的第一步就会有一个很深的疑惑,关于key的 1、什么是key文件? 2、Key文件是如何产生的? 3、为什么需要他key文件? 4、如何添加key文件? 5、以及实际开发中的一些报错该如何处理?

qyvhome的博客 4094

关于Win11安装Ubuntu子系统报错:WslRegisterDistribution failed with error 解决办法

Win11安装Ubuntu子系统报错:WslRegisterDistribution failed with error

qq_51908382的博客 3万+
上一篇: select、poll、epoll之间的区别总结(转)
下一篇: 应用程序运行***buffer overflow detected*** terminated Aborted异常退出问题
coding__madman
博客等级 码龄11年 188粉丝 76原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值