当前位置: 首页 > news >正文

【RTT-Studio】详细使用教程十二:UART的分析和使用

文章目录

    • 一、简介
      • 1.串口发送模式
      • 2.串口接收模式
    • 二、串口配置
    • 三、串口发送
    • 四、串口接收

一、简介

  本文主要阐述STM32串口的几种工作中使用的工作模式和编程思路。串口通常情况下使用的是:1个起始位,8个数据位,无奇偶校验,1位停止位,每传输1个字节的数据相当于传输10bit,在波特率:9600的情况下,1字节需要1.04ms,如果传输波特率:115200传输1字节数据需要0.087ms。

1.串口发送模式

串口发送方式主要分为3种:轮询、中断、DMA发送。下面简述一下各个模式优缺点:

模式优点缺点
轮询代码最简单会导致代码阻塞,会被任何中断,在带os的系统中还容易被高优先级任务打断
中断将发送优先级提高到该串口中断级别,不会被任务打断,只会被更高优先级中断打断占用cpu和中断
DMA不需要cpu参与数据发送,发送也不会被打断代码复杂

(1)轮询发送
  轮询发送是将需要发送的数据写到串口的数据(DR)寄存器中,然后通过循环等待发送完成TC标致位置位判断数据是否发送结束。一般串口在115200波特率下发送1字节数据时间大概0.087ms,在没有os的情况下,程序需要阻塞89ms,只会被中断打断。在有os的情况下还需要考虑到任务切换和高优先级任务的使用,串口发送的一包数据就会是断断续续的,时间更长。

(2)中断发送
  中断发送是对轮询发送的一种改进,其目的是连续发送一包数据,不被低优先级中断和任务打断。其思路是开启串口发送中断,在应用层调用驱动层发送函数时,将中断发送标志位置1/或直接发送第一字节,触发串口中断,在中断中发送剩余数据。期间每次传输完成时进一次中断,其余时间cpu可以运行别的任务,而且减少了查询发送完成标志位的时间。对于没有os的系统来说做到了时间片处理非阻塞发送。

(3)DMA发送
  该模式需要根据手册提前配置DMA通道模式等信息,通过HAL库提供的发送函数,DMA会自动将内存中的参数搬运到串口外设中,CPU执行完发送命令就可以执行别的任务了。在dma发送结束时会产生DMA中断告诉用户数据是否发送结束。

2.串口接收模式

串口接收也有轮询模式,主要是中断和DMA的方式。下面简述一下各个模式优缺点:

模式优点缺点
中断代码简单需要多次进入中断
DMA不需要cpu参与每字节接收,仅产生少量的中断需要配合空闲中断处理

(1)中断接收
  中断接收是一种常见的接收方式,适合波特率较低(9600)或任务不复杂的情况下使用。9600传输数据时,使用串口中断接收数据大约1.04ms进一次中断,在要求不高的情况下使用中断接收也能满足设计要求,尤其对裸机基于时间片的代码框架来说影响不大,代码也简洁。但是随着波特率的提高此方法则不太适用,当波特率很高(115200以上),若使用串口中断接收意味着每87us会进入一次中断,那么对主程序的时间片影响将会很大,故推荐使用DMA。

(2)DMA接收+空闲中断接收
  需要提前配置DMA通道,DMA模式,使用DMA搬运数据,串口空闲中断判断驱动层的帧结束。当空闲中断产生时将DMA映射的数据拷贝到应用层的接收缓存区。与中断接收区别是不用每个字节进入一次中断,拷贝数据也是将一帧数据拷贝到缓存区。发送1K数据则会在89ms内进1000次中断,若再提高波特率,对裸机时间片的系统来说影响就很大了。


二、串口配置

  UART(Universal Asynchronous Receiver/Transmitter)通用异步收发传输器,UART 作为异步串口通信协议的一种,工作原理是将传输数据的每个字符一位接一位地传输,是在应用程序开发过程中使用频率最高的数据总线。

RT-Thread串口配置步骤:
在这里插入图片描述
(1)通过串口名字找到串口句柄

#define SAMPLE_UART_NAME       "uart2"static rt_device_t serial;
serial = rt_device_find(SAMPLE_UART_NAME);

(2)配置串口参数
使用rt_device_control()函数修改串口配置参数

/* 初始化配置参数 */
struct serial_configure uart2_config = RT_SERIAL_CONFIG_DEFAULT;  /* step2:修改串口配置参数 */
uart2_config.baud_rate = BAUD_RATE_115200;      //修改波特率为 9600
uart2_config.data_bits = DATA_BITS_8;           //数据位 8
uart2_config.stop_bits = STOP_BITS_1;           //停止位 1
uart2_config.bufsz     = 128;                   //修改缓冲区 buff size 为 128
uart2_config.parity    = PARITY_NONE;           //无奇偶校验位rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &uart2_config);

(3)设置串口接收回调函数
当发生中断(接收中断或者空闲中断)时,会调用注册的回调函数。用户可以在回调函数中写一些无阻塞的功能。虽然回调函数是在中断中执行,所以不能有阻塞和延时。

/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */rt_sem_release(&rx_sem);cnt++;return RT_EOK;
}/* 设置接收回调函数 */
rt_device_set_rx_indicate(serial, uart_input);

(4)打开串口设备
打开设备时可以对串口的模式进行配置,rt-thread提供三种模式:中断模式、轮询模式、DMA 模式。发送和接收只能从中选一个,发送和接收可以配置的不一致。默认使用中断接收和轮询发送。

/* 以中断接收及轮询发送模式打开串口设备 */
rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag)

oflag支持模式参数为下列几个:

#define RT_DEVICE_FLAG_INT_RX           0x100           /**< INT mode on Rx 中断接收 */
#define RT_DEVICE_FLAG_DMA_RX           0x200           /**< DMA mode on Rx DMA接收 */
#define RT_DEVICE_FLAG_INT_TX           0x400           /**< INT mode on Tx 中断发送 */
#define RT_DEVICE_FLAG_DMA_TX           0x800           /**< DMA mode on Tx DMA接收 */

rt_device_open函数中初始化DMA模式会进行参数检查,需要确认使用串口DMA宏被定义。如:BSP_UART2_RX_USING_DMA,否则会rt_serial_open()返回错误。当对应串口DMA宏在rtconfig.h中被定义后,通过宏定义设置到对应串口对象的uart_dma_flag参数中,在串口初始化阶段通过rt_hw_usart_init()中的rt_hw_serial_register()函数会把对应uart_dma_flag设置到串口dev->flag中,后续串口open时只是检查一下参数。

完整的串口配置例程代码如下:

/** 程序清单:这是一个 串口 设备使用例程* 例程导出了 uart_sample 命令到控制终端* 命令调用格式:uart_sample uart2* 命令解释:命令第二个参数是要使用的串口设备名称,为空则使用默认的串口设备* 程序功能:通过串口输出字符串"hello RT-Thread!",然后错位输出输入的字符
*/
#include <rtthread.h>
#include "module_uart.h"
#include "drv_usart.h"#define SAMPLE_UART_NAME       "uart2"/* 初始化配置参数 */
struct serial_configure uart2_config = RT_SERIAL_CONFIG_DEFAULT; /* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
uint16_t cnt = 0;/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */rt_sem_release(&rx_sem);cnt++;return RT_EOK;
}static void serial_thread_entry(void *parameter)
{char ch;while (1){/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */while (rt_device_read(serial, -1, &ch, 1) != 1){/* 阻塞等待接收信号量,等到信号量后再次读取数据 */rt_sem_take(&rx_sem, RT_WAITING_FOREVER);}/* 读取到的数据通过串口输出 */rt_device_write(serial, 0, &ch, 1);}
}int uart2_init(void)
{rt_err_t ret;/* 查找系统中的串口设备 */serial = rt_device_find(SAMPLE_UART_NAME);if (!serial){rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);return RT_ERROR;}/* step2:修改串口配置参数 */uart2_config.baud_rate = BAUD_RATE_115200;        //修改波特率为 9600uart2_config.data_bits = DATA_BITS_8;           //数据位 8uart2_config.stop_bits = STOP_BITS_1;           //停止位 1uart2_config.bufsz     = 128;                   //修改缓冲区 buff size 为 128uart2_config.parity    = PARITY_NONE;           //无奇偶校验位rt_device_control(serial, RT_DEVICE_CTRL_CONFIG, &uart2_config);/* 初始化信号量 */rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);/* 以中断接收及轮询发送模式打开串口设备 */rt_device_open(serial, RT_DEVICE_FLAG_INT_RX);/* 设置接收回调函数 */rt_device_set_rx_indicate(serial, uart_input);/* 创建 serial 线程 */rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);/* 创建成功则启动线程 */if (thread != RT_NULL){rt_thread_startup(thread);}else{ret = RT_ERROR;}return ret;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(uart_sample, uart device sample);

三、串口发送

  串口的三种发送:轮询、中断、DMA发送。下面是发送程序对应用层的接口:

rt_size_t rt_device_write(rt_device_t dev,rt_off_t    pos,const void *buffer,rt_size_t   size)

里面本质上会调用rt_serial_write()函数往串口设备写数据,里面又细分了三个函数:_serial_int_tx()(中断发送) _serial_dma_tx()(dma发送)_serial_poll_tx()轮询发送。

1.轮询发送
轮询发送是最简单的发送模式,rt-thread提供的是阻塞的发送函数。程序会将需要发送的数据一字节一字节的写到串口的DR寄存器,然后查询发送完成标志,直到数据完全发送结束。优点:程序简单易懂,缺点:浪费cpu资源,容易被高优先级任务打断,一帧数据发送断断续续的。
_serial_poll_tx()的具体实现:每次发送1字节数据循环length长度

rt_inline int _serial_poll_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{int size;RT_ASSERT(serial != RT_NULL);size = length;while (length){serial->ops->putc(serial, *data);++ data;-- length;}return size - length;
}

putc函数的实现:根据单片机型号将数据写入DR或者TDR寄存器中,然后等待发送完成标志。按照这种方式,代码需要在这阻塞到一帧数据完全发送结束才会执行后面的代码。再此期间代码会被高优先级任务和中断打断。

static int stm32_putc(struct rt_serial_device *serial, char c)
{struct stm32_uart *uart;RT_ASSERT(serial != RT_NULL);uart = rt_container_of(serial, struct stm32_uart, serial);UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);
#if defined(SOC_SERIES_STM32L4) || defined(SOC_SERIES_STM32WL) || defined(SOC_SERIES_STM32F7) || defined(SOC_SERIES_STM32F0) \|| defined(SOC_SERIES_STM32L0) || defined(SOC_SERIES_STM32G0) || defined(SOC_SERIES_STM32H7) || defined(SOC_SERIES_STM32L5)\|| defined(SOC_SERIES_STM32G4) || defined(SOC_SERIES_STM32MP1) || defined(SOC_SERIES_STM32WB) ||defined(SOC_SERIES_STM32F3)\|| defined(SOC_SERIES_STM32U5)uart->handle.Instance->TDR = c;
#elseif(uart->config->is485){HAL_GPIO_WritePin(uart->config->de_port,uart->config->de_pin, GPIO_PIN_RESET);}uart->handle.Instance->DR = c;
#endifwhile (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) == RESET);if(uart->config->is485){HAL_GPIO_WritePin(uart->config->de_port,uart->config->de_pin, GPIO_PIN_SET);}return 1;
}

2.中断发送
与轮询发送区别是多了一个等待完成信号量的内容。bug是里面字符发送函数与轮询发送函数一样,永远返回1,所以等待完成函数不会被触发。

rt_inline int _serial_int_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{int size;struct rt_serial_tx_fifo *tx;RT_ASSERT(serial != RT_NULL);size = length;tx = (struct rt_serial_tx_fifo*) serial->serial_tx;RT_ASSERT(tx != RT_NULL);while (length){if (serial->ops->putc(serial, *(char*)data) == -1){rt_completion_wait(&(tx->completion), RT_WAITING_FOREVER);continue;}data ++; length --;}return size - length;
}

3.DMA发送
rt_data_queue_push():将数据放入dma数据队列中,这里需要特别注意一下直接是应用层写的数据,没有发送缓存区,若是局部变量,在dma发送是可能已经释放掉,无法发送正确数据。其次在发送过程中修改应用层数据,dma会直接发送修改后的数据,这点也需要特别注意。
serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX):在函数指针背后调用的是对hal库分装一层的dma传输函数。
HAL_UART_Transmit_DMA():是个无阻塞函数,也就是执行完这一行时,数据开始发送,但是不会等到数据完全发送结束。

rt_inline int _serial_dma_tx(struct rt_serial_device *serial, const rt_uint8_t *data, int length)
{rt_base_t level;rt_err_t result;struct rt_serial_tx_dma *tx_dma;tx_dma = (struct rt_serial_tx_dma*)(serial->serial_tx);result = rt_data_queue_push(&(tx_dma->data_queue), data, length, RT_WAITING_FOREVER);if (result == RT_EOK){level = rt_hw_interrupt_disable();if (tx_dma->activated != RT_TRUE){tx_dma->activated = RT_TRUE;rt_hw_interrupt_enable(level);/* make a DMA transfer */serial->ops->dma_transmit(serial, (rt_uint8_t *)data, length, RT_SERIAL_DMA_TX);}else{rt_hw_interrupt_enable(level);}return length;}else{rt_set_errno(result);return 0;}
}static rt_size_t stm32_dma_transmit(struct rt_serial_device *serial, rt_uint8_t *buf, rt_size_t size, int direction)
{if (RT_SERIAL_DMA_TX == direction){if (HAL_UART_Transmit_DMA(&uart->handle, buf, size) == HAL_OK){return size;}else{return 0;}}return 0;
}void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)
{/* Transfer Complete Interrupt management ***********************************/else if (((flag_it & (DMA_FLAG_TC1 << hdma->ChannelIndex)) != RESET) && ((source_it & DMA_IT_TC) != RESET)){if((hdma->Instance->CCR & DMA_CCR_CIRC) == 0U){/* Disable the transfer complete and error interrupt */__HAL_DMA_DISABLE_IT(hdma, DMA_IT_TE | DMA_IT_TC);  /* Change the DMA state */hdma->State = HAL_DMA_STATE_READY;}/* Clear the transfer complete flag */__HAL_DMA_CLEAR_FLAG(hdma, __HAL_DMA_GET_TC_FLAG_INDEX(hdma));/* Process Unlocked */__HAL_UNLOCK(hdma);if(hdma->XferCpltCallback != NULL){/* Transfer complete callback */hdma->XferCpltCallback(hdma);}}
}

四、串口接收

  串口接收常见的分中断接收和DMA接收。在RTT串口配置中设置了一个ringbuf,其作用是用来接收数据的。无论使用中断接收还是DMA接收数据都会存入ringbuf中。

1.中断接收
在RTT中,中断接收的流程如下:
在这里插入图片描述
中断接收无论如何都会触发单片机中断向量表中的中断函数,只需要去查找中断函数中具体实现即可。如果是接收中断则进入rt_hw_serial_isr函数否则则去清除一些错误标志位。

void USART2_IRQHandler(void)
{/* enter interrupt */rt_interrupt_enter();uart_isr(&(uart_obj[UART2_INDEX].serial));/* leave interrupt */rt_interrupt_leave();
}static void uart_isr(struct rt_serial_device *serial)
{.../* UART in mode Receiver -------------------------------------------------*/if ((__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET) &&(__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_RXNE) != RESET)){rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);}
...else{if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_ORE) != RESET){__HAL_UART_CLEAR_OREFLAG(&uart->handle);}if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_NE) != RESET){__HAL_UART_CLEAR_NEFLAG(&uart->handle);}if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_FE) != RESET){__HAL_UART_CLEAR_FEFLAG(&uart->handle);}if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_PE) != RESET){__HAL_UART_CLEAR_PEFLAG(&uart->handle);}}
}

rt_hw_serial_isr()可以分成两部分读取数据和回调函数调用部分。读取数据部分是负责把串口接收数据读取到ringbuf中。

while (1)
{ch = serial->ops->getc(serial);if (ch == -1) break;/* disable interrupt */level = rt_hw_interrupt_disable();rx_fifo->buffer[rx_fifo->put_index] = ch;rx_fifo->put_index += 1;if (rx_fifo->put_index >= serial->config.bufsz) rx_fifo->put_index = 0;/* if the next position is read index, discard this 'read char' */if (rx_fifo->put_index == rx_fifo->get_index){rx_fifo->get_index += 1;rx_fifo->is_full = RT_TRUE;if (rx_fifo->get_index >= serial->config.bufsz) rx_fifo->get_index = 0;_serial_check_buffer_size();}/* enable interrupt */rt_hw_interrupt_enable(level);
}

上述使用serial->ops->getc(serial)函数,看这个名字就能猜到是获取串口接收的一个字节数据。对于STM32来说就是将串口的DR寄存器通过这个函数返回。将DR寄存器中的值存入ringbuf,还对ringbuf满的情况做了一下异常处理,ringbuf is_full标志位会置一,数据会覆盖最早的数据导致数据丢失,所以需要保证应用层及时从ringbuf中取出数据。

static int stm32_getc(struct rt_serial_device *serial)
{int ch;struct stm32_uart *uart;RT_ASSERT(serial != RT_NULL);uart = rt_container_of(serial, struct stm32_uart, serial);ch = -1;if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_RXNE) != RESET){...ch = uart->handle.Instance->DR & stm32_uart_get_mask(uart->handle.Init.WordLength, uart->handle.Init.Parity);...}return ch;
}

再看看回调函数部分,若回调函数不为空,且这次就收到数据则调用一次接收回调函数。通常回调函数中释放一个信号量通知应用层程序从ringbuf中读取数据。其实这也看出了中断方式的一个缺陷,每接收一字节放一次ringbuf,调用一次回调函数,应用层也只能一字节一字节接收。为了减CPU的使用率还提供了DMA的方式。

/* 调用回调函数部分 */
/* invoke callback */
if (serial->parent.rx_indicate != RT_NULL)
{rt_size_t rx_length;/* get rx length */level = rt_hw_interrupt_disable();rx_length = (rx_fifo->put_index >= rx_fifo->get_index)? (rx_fifo->put_index - rx_fifo->get_index):(serial->config.bufsz - (rx_fifo->get_index - rx_fifo->put_index));rt_hw_interrupt_enable(level);if (rx_length){serial->parent.rx_indicate(&serial->parent, rx_length);}
}
break;

2.DMA接收
在这里插入图片描述
这里触发串口中断的判断变了,变成了DMA半满、满中断和空闲中断触发串口中断,三者是或的关系。接收中断中如果开启DMA则会执行以下代码:

  else if ((uart->uart_dma_flag) && (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_IDLE) != RESET)&& (__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_IDLE) != RESET)){level = rt_hw_interrupt_disable();recv_total_index = serial->config.bufsz - __HAL_DMA_GET_COUNTER(&(uart->dma_rx.handle));recv_len = recv_total_index - uart->dma_rx.last_index;uart->dma_rx.last_index = recv_total_index;rt_hw_interrupt_enable(level);if (recv_len){rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_DMADONE | (recv_len << 8));}__HAL_UART_CLEAR_IDLEFLAG(&uart->handle);}else if (__HAL_UART_GET_FLAG(&(uart->handle), UART_FLAG_TC) &&(__HAL_UART_GET_IT_SOURCE(&(uart->handle), UART_IT_TC) != RESET)){if ((serial->parent.open_flag & RT_DEVICE_FLAG_DMA_TX) != 0){HAL_UART_IRQHandler(&(uart->handle));}UART_INSTANCE_CLEAR_FUNCTION(&(uart->handle), UART_FLAG_TC);}

DMA在处理的过程中也是调用rt_hw_serial_isr与中断一致,接收长度需要根据DMA接收长度增加个小计算得出。后续处理与中断接收也基本一致,将数据更新进ringbuf再调用回调函数。最大的区别是DMA一次会接收一帧或者半帧数据才进一次中断。回调函数处理也会有所不一样,官方例程也不在释放信号量了,通过消息队列传出串口接收数据大小,交给应用层处理。


相关文章:

【RTT-Studio】详细使用教程十二:UART的分析和使用

文章目录 一、简介1.串口发送模式2.串口接收模式 二、串口配置三、串口发送四、串口接收 一、简介 本文主要阐述STM32串口的几种工作中使用的工作模式和编程思路。串口通常情况下使用的是&#xff1a;1个起始位&#xff0c;8个数据位&#xff0c;无奇偶校验&#xff0c;1位停止…...

【AI绘画】Midjourney前置指令/settings设置详解

文章目录 &#x1f4af;Midjourney前置指令/settings设置详解&#x1f4af;Use the default model&#xff08;AI绘画所使用的大模型&#xff09;Midjourney Model&#xff08;Midjourney 模型&#xff09;Niji Model&#xff08;Niji模型&#xff09; &#x1f4af;Midjourney…...

【NI国产替代】PXIe‑4330国产替代24位,8通道PXI应变/桥输入模块

25 kS/s&#xff0c;24位&#xff0c;8通道PXI应变/桥输入模块 PXIe‑4330是一款同步输入模块&#xff0c;为基于桥接的传感器提供集成数据采集和信号调理。 PXIe‑4330具有更高的准确性、高数据吞吐量和同步特性&#xff0c;使其成为高密度测量系统的理想选择。\n\n为了消除噪…...

哪里可以免费上传招生简章

随着招生季的临近&#xff0c;各高校和培训机构纷纷摩拳擦掌&#xff0c;准备迎接新一代学子们的到来。在这个信息化的时代&#xff0c;如何让招生简章发挥最大的效用&#xff0c;成为吸引优质生源的关键。 那么如何制作招生简章&#xff1f; 1. 注册账号&#xff1a;访问FLBO…...

Midjourney中文版教程:参数详解

1.长宽比 可以设置图片的纵横比。按照需求可以选择不同的尺寸&#xff0c;也可以自定义。 注意&#xff1a;--ar必须使用整数。使用139&#xff1a;100代替1.39&#xff1a;1。 长宽比会影响生成图像的形状和构图。 在放大时&#xff0c;某些长宽比可能会稍微改变。 较旧的…...

误闯机器学习(第一关-概念和流程)

以下内容&#xff0c;皆为原创&#xff0c;实属不易&#xff0c;请各位帅锅&#xff0c;镁铝点点赞赞和关注吧&#xff01; 好戏开场了。 一.什么是机器学习 机器学习就是从数据中自动分析获取模型&#xff08;总结出的数据&#xff09;&#xff0c;并训练模型&#xff0c;去预…...

Tensorflow 2.16.0+在PyCharm中找不到keras的报错解决

在PyCharm(2024.2版本)中&#xff0c;直接使用from tensorflow import keras会提示“Cannot find reference ‘keras’ in ‘init.py’ ”&#xff0c;找不到keras&#xff0c;如下图所示。 查阅相关资料&#xff0c;可以发现在tf2.16之后&#xff0c;默认的keras后端升级为了…...

【Python】高效的Web自动化测试利器—Python+Playwright快速上手自动化实战指南(限时开放)

文章目录 前言一.playwright是什么二.python引入playwright1.安装2.playwright命令行参数3.playwright codegen自动生成代码4.Chrome和Chromium有什么关系?三.基本概念1. 无头浏览器(Headless Browser)2.同步和异步模式操作playwright2.1.同步(Sync)模式同步方式代码模板2…...

CentOS上安装和配置Docker与Docker Compose的详细指南

引言 大家好&#xff0c;我是小阳&#xff0c;在这篇文章中&#xff0c;我将带大家一步步完成在CentOS系统上安装和配置Docker与Docker Compose的过程。通过这篇详细的指南&#xff0c;你将能够轻松配置Docker环境&#xff0c;并在日常开发和部署中享受其带来的便利。 原文阅…...

Vim多文件操作

Vim多文件编辑的实际意义在于它极大地提高了开发者在处理多个相关文件时的效率和便利性。在软件开发、文本编辑、代码审查、配置管理等场景中&#xff0c;经常需要同时打开和操作多个文件。Vim的多文件编辑功能使得这些任务变得更加直观和高效。 提高编码效率&#xff1a;在开发…...

【ARM+Codesys 客户案例 】 基于RK3568/A40i/STM32+CODESYS在智能制造中的应用案例:全自动切片机器人

蔬菜是人们日常生活必不可缺的食品&#xff0c;并且食用方法多种多样。自步入小康社会以来&#xff0c;人们的生活节奏越来越快&#xff0c;很多传统服务已不能满足人们的物质需求和生活节奏。日常生活中通过手工快速切菜严重地威胁着人身安全&#xff0c;切菜时间过长或切菜不…...

NSI程序打包脚本文件编写教程

引言 NSIS (Nullsoft Scriptable Install System) 是一个专业开源的制作 windows 安装程序的工具。我们通过HM NSIEDIT编写好脚本、编译即可生成exe安装包。安装过程中可以配置其安装包图标、名称、出版人、网站等。此外&#xff0c;还可以设置程序开机自启动、管理员权限运行…...

[LitCTF 2023]1zjs

很有意思的一道题&#xff0c;打开题目环境之后F12可以看到 点击那个蓝色下划线的就能看到&#xff1a; 然后访问&#xff1a; /fk3f1ag.php就可以看到&#xff1a; 然后将这些复制到控制台然后回车就能得到flag。...

MCU复位RAM会保持吗,如何实现复位时变量数据保持

在使用MCU时&#xff0c;通常大家默认MCU复位时RAM会被复位清零&#xff0c;那实际MCU复位时RAM是什么状态&#xff1f;如何让mcu复位时RAM保持不变呢&#xff1f; MCU复位有电源复位、Standby复位、内核复位、看门狗复位、引脚复位等。 其中内部会有掉电动作的复位有电源复位…...

解决window 端口的占用问题

netstat -nao | findstr "5554" taskkill -pid 5076 -f 本文资料来自 https://cloud.tencent.com/developer/article/1703982...

【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署

【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署 什么是RAG&#xff1a; 我能把这个过程理解为Kimi.ai每次都能列出的一大堆网页参考资料吗&#xff1f;Kimi学了这些资料以后&#xff0c;根据这里面的信息综…...

PyTorch 基础学习(10)- Transformer

系列文章&#xff1a; 《PyTorch 基础学习》文章索引 介绍 Transformer模型是近年来在自然语言处理&#xff08;NLP&#xff09;领域中非常流行的一种模型架构&#xff0c;尤其是在机器翻译任务中表现出了优异的性能。与传统的循环神经网络&#xff08;RNN&#xff09;不同&a…...

mybatis-plus使用

目录 1. 快速开始 1. 创建user表 2. 插入几条数据 3. 创建一个新的springboot项目 4. 导入mybatis-plus依赖 5. 在配置文件中进行配置 6. 编写实体类 7. 编写Mapper 接口类 8. 添加 MapperScan 注解 9. 测试 ​编辑2. CRUD 1. 插入一条语句 2. 根据主键id删除一条记录 3. 根据…...

ant-design-vue快速上手指南及排坑攻略

前言 ant-design-vue是Ant Design的Vue实现&#xff0c;旨在为Vue用户提供一套企业级的UI设计语言。本文将带你快速上手ant-design-vue&#xff0c;并在实践中分享一些常见的坑及解决方法。遵循本文档&#xff0c;让你轻松搭建优雅的Vue应用。 一、环境准备 在开始之前&…...

【GitLab】使用 Docker 安装 3:gitlab-ce:17.3.0-ce.0 配置

参考阿里云的教程docker的重启 sudo systemctl daemon-reload sudo systemctl restart docker配置 –publish 8443:443 --publish 8084:80 --publish 22:22 sudo docker ps -a 當容器狀態為healthy時,說明GitLab容器已經正常啟動。 root@k8s-master-pfsrv:~...

多线程(4)——单例模式、阻塞队列、线程池、定时器

1. 多线程案例 1.1 单例模式 单例模式能保证某个类在程序中只存在唯一一份实例&#xff0c;不会创建出多个实例&#xff08;这一点在很多场景上都需要&#xff0c;比如 JDBC 中的 DataSource 实例就只需要一个 tip&#xff1a;设计模式就是编写代码过程中的 “软性约束”&am…...

告别电量焦虑,高性能65W PD快充芯片HUSB380A打造梦中情【头】

市面上的充电器越来越卷&#xff0c;让人眼花缭乱。压力同样也给到了快充芯片行业&#xff0c;要在激烈的市场竞争中脱颖而出&#xff0c;快充芯片必须集高功率、高性价比与广泛的兼容性等于一身。 基于此&#xff0c;慧能泰推出了新一代高性能PD Source产品——HUSB380A。 图…...

vulnhub靶场 — NARAK

下载地址:https://download.vulnhub.com/ha/narak.ova Description:Narak is the Hindu equivalent of Hell. You are in the pit with the Lord of Hell himself. Can you use your hacking skills to get out of the Narak? Burning walls and demons are around every cor…...

RabbitMQ如何保证消息不丢失

RabbitMQ消息丢失的三种情况 第一种&#xff1a;生产者弄丢了数据。生产者将数据发送到 RabbitMQ 的时候&#xff0c;可能数据就在半路给搞丢了&#xff0c;因为网络问题啥的&#xff0c;都有可能。 第二种&#xff1a;RabbitMQ 弄丢了数据。MQ还没有持久化自己挂了。 第三种…...

(亲测有效)SpringBoot项目集成腾讯云COS对象存储(1)

目录 一、腾讯云对象存储使用 1、创建Bucket 2、使用web控制台上传和浏览文件 3、创建API秘钥 二、代码对接腾讯云COS&#xff08;以Java为例&#xff09; 1、初始化客户端 2、填写配置文件 3、通用能力类 文件上传 测试 一、腾讯云对象存储使用 1、创建Bucket &am…...

无人机之故障排除篇

一、识别故障 掌握基本的无人机系统知识&#xff0c;遵循“先易后难、先外后内、先软件后硬件”的原则进行故障识别。一旦发现故障&#xff0c;立即停止飞行&#xff0c;避免进一步损坏。 二、机械部件维修 对于机身裂痕、螺旋桨损坏等情况&#xff0c;根据损坏程度更换相应部…...

深入理解Python常见数据类型处理

目录 概述数字类型 整数&#xff08;int&#xff09;浮点数&#xff08;float&#xff09;复数&#xff08;complex&#xff09; 字符串&#xff08;str&#xff09; 字符串基本操作字符串方法 列表&#xff08;list&#xff09; 列表基本操作列表方法列表推导式 元组&#xf…...

最佳实践:CI/CD交付模式下的运维展望丨IDCF

李洪锋 启迪万众数字技术(广州)有限公司 &#xff0c;产品研发中心-系统运维部、研发效能&#xff08;DevOps&#xff09;工程师&#xff08;中级&#xff09;课程学员 一、DevOps现状 据云计算产业联盟《中国DevOps现状调查报告2023》显示&#xff0c;国内DevOps 落地成熟度…...

Flat Ads:开发者如何应对全球手游市场的洗牌与转型

2023年下半年至2024年上半年,中国手游的海外市场表现经历了显著变化,开发者要如何应对全球手游市场的洗牌与转型?本篇文章我们将结合相关行业白皮书的最新数据对中国手游出海表现进行分析与洞察。 一、中国手游海外市场表现 根据Sensor Tower《2024年海外手游市场洞察》最新…...

ai取名软件上哪找?一文揭秘5大ai取名生成器

在这个世界上&#xff0c;每一个新生命的到来都是一份奇迹&#xff0c;无论是一个新生儿的第一声啼哭&#xff0c;还是一只宠物的第一次摇尾巴&#xff0c;都充满了无限的希望和喜悦。 然而&#xff0c;给这个小生命起一个响亮、独特且富有意义的名字&#xff0c;往往让人煞费…...

建设职业技术学院网站/seo学校

电脑在我们的生活中被使用的是越来越广泛了。当我们使用电脑的时候&#xff0c;都是需要连接网络的。连上了网络之后&#xff0c;我们才可以获取到更多的消息。在联网的时候&#xff0c;都是需要一个ip的。Ip就是互联网相连的协议。这个ip除了可以自动生成之外&#xff0c;我们…...

阿里妈妈通过审核 又拒绝 网站建设不完整/信息流推广的竞价机制是

win7/win10怎么在任务栏添加计算机快捷方式&#xff1f;win7 和win10系统默认关闭了快速启动栏&#xff0c;而引入新的功能“库”。把计算机锁定到任务栏里面&#xff0c;点击的仍然还是“库”&#xff0c;不是“计算机”。这是什么情况&#xff1f;怎么处理这一故障&#xff1…...

网站备案网站要有内容吗/网站搜索优化找哪家

2019&#xff0c;5G的春风吹的正劲&#xff0c;吹到了紫禁城中&#xff0c;将故宫博物院吹成一座集数字化、信息化、智慧化于一身的“5G智慧故宫”。紫禁城的朱墙&#xff0c;矗立了600年后&#xff0c;被5G技术洞穿。通信技术的壁垒&#xff0c;发展了40多年&#xff0c;被来自…...

保定网站建设价格/网络营销做得好的公司

五、C语言实现双向循环链表 1、逻辑图 2、初始化 3、关于插入位置的处理 4、插入操作 插入操作&#xff1a;无需特殊考虑是头插还是尾插&#xff0c;别忘了head->length; 5、删除操作 同理&#xff0c;删除操作也无需考虑头、尾删除操作,也别忘了head->length- -; 6…...

做网站编辑需要经验吗/抖音seo优化软件

2019独角兽企业重金招聘Python工程师标准>>> 多级缓存支撑海量读服务设计方案 博客分类&#xff1a; java 缓存 多级缓存支撑海量读服务设计方案 Cache1&#xff1a;一级本地缓存(map,ehcache,guava) Cache2: 二级分布式缓存(redis,memcached,hazelcast,iginte) 更新…...

wordpress启用memcached/百度营稍

本文演示如何用 ADO.NET 中 OleDbConnection 对象的 GetOleDbSchemaTable 方法检索数据库架构信息。数据源中的架构信息包括数据库或可通过数据库中的数据源、表和视图得到的目录以及所存在的约束等。表中的架构信息包括主键、列和自动编号字段。注意&#xff0c;在使用 SqlCli…...