【江协STM32】9-1/2/3 USART串口协议、USART外设、串口发送串口发送+接收

news2025/1/9 11:02:43

1. 通信接口

  • 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
  • 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发
  • 全双工:指通信双方能够同时进行双向通信。发送线路和接收线路互不影响,一般有两根数据传输线
  • 半双工:一般只有一根数据传输线
  • 单工:指数据只能从一个设备到另一个设备,不能反过来 

2. 串口通信

  • 串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
  • 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力

           

2.1 硬件电路 

  • 简单双向串口通信有两根通信线(发送端TX和接收端RX)
  • TX和RX是单端信号,它们的高低电平都是相对于GND的
  • TX与RX要交叉连接
  • 当只需单向的数据传输时,可以只接一根通信线
  • 当电平标准不一致时,需要加电平转换芯片

2.2 电平标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

  • TTL电平:+3.3V或+5V表示1,0V表示0
  • RS232电平:-3~-15V表示1,+3~+15V表示0
  • RS485电平:两线压差+2~+6V表示1,-2~-6V表示0(差分信号)

如果需要其他的电平,加电平转换芯片即可,在软件层面都属于串口,不影响程序。

2.3 串口参数及时序 

  • 波特率:串口通信的速率,决定了每隔多久发送一位数据
  • 起始位:标志一个数据帧的开始,固定为低电平。串口的空闲状态是高电平,需要传输时,必须先发送一个低电平的起始位。
  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行。串口中,每个字节都装载在一个数据帧内,每个数据帧都由起始位、数据位和停止位组成。数据位有8位,在数据位的最后还可以加一个奇偶校验位,这样数据位就是9位。 
  • 校验位:用于数据验证,根据数据位计算得来,计算1的个数。校验可以选择3种方式,无校验、奇校验和偶校验。如果使用奇校验,那么包括校验位在内的9位数据会出现奇数个1,比如传输0000 1111,那么校验位为1,如果数据是0000 1110,那么校验位为0。
  • 停止位:用于数据帧间隔,固定为高电平。可以选择1位、1.5位、2位等

     

实测波形(探头负极接GND,探头正极接发送设备的TX引脚)

3. USART外设

  • USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
  • USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
  • 自带波特率发生器,最高达4.5Mbits/s。波特率常用9600或115200
  • 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
  • 可选校验位(无校验/奇校验/偶校验)
  • 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
  • STM32F103C8T6 USART资源: USART1(APB2总线,f_PCLK2=72MHz)、 USART2(APB1总线,f_PCLK1=36MHz)、 USART3(APB1总线) 
USART框图
USART基本结构

3.1 数据帧 

数据帧(字长设置)
数据帧(配置停止位)

当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率连续采样一帧数据。同时,从起始位开始,采样位置要对齐到位的正中间。首先以波特率的16位频率进行采样,也就是在一位的时间里可以进行16次采样。最开始,空闲状态为高电平,采样值为1,某个位置采到0,开始在起始位进行16次采样,如果没有噪声,这16次采样均为0。由于实际电路存在噪声,接收电路会在下降沿后的第3、5、7次进行一批采样,在第8、9、10次再进行一批采样,且这两批采样均要求每3位至少有两个0,如果3位里面有两个0一个1,也算检测到了起始位,但会在状态寄存器内置噪声标志位NE(Noise Error)为1。如果3位里面只有1个0,就不算检测到起始位,重新检测。

如果通过了起始位侦测,那么接收状态就由空闲变为接收起始位。同时,第8、9、10次采样的位置正好是起始位的正中间。之后接收数据位时,就均在第8、9、10次进行采样,这样就可以保证采样位置在位的正中间了。

起始位侦测

从1到16是一个数据位的时间长度,在一个数据位有16个采样时钟,由于起始位侦测已经对齐了采样时钟,所以直接在第8、9、10次采样数据位。没有噪声的理想情况下,三次采样全为0或全为1。如果有噪声,2次为1就认为收到了1,2次为0就认为收到了0,并且噪声标志位NE置1。

数据采样

3.2 波特率发生器

  • 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定
  • 计算公式:波特率 = f_PCLK2/1 / (16 * DIV)
  • 比如要配置USART1为9600的波特率,9600 = 72M / (16 * DIV),解得DIV=468.75

4. 数据模式 

  • HEX模式/十六进制模式/二进制模式:以原始数据的形式显示
  • 文本模式/字符模式:以原始数据编码后的形式显示

5. 串口发送

5.1 接线图

根据引脚定义表,USART1_TX是PA9,USART1_RX是PA10。PA9是STM32的TX发送,所以接到串口模块的RXD接收。PA10是STM32的RX接收,所以接到串口模块的TXD发送。两个设备之间要把负极接在一起,进行共地。最后,串口模块和STLINK都要插在电脑上,使两者均有独立供电。

5.2 代码

USART初始化流程:

  1. 开启时钟,USART(APB2)和GPIO
  2. 配置GPIO,TX配置为复用输出,RX配置为输入
  3. 配置USART
  4. 开启USART

配置参数:9600波特率、8位字长、无校验、1位停止位、无流控、只有发送模式 

Serial.c

#include "stm32f10x.h"                  // Device header

void Serial_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;//    波特率
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//   不使用流控
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_InitStructure.USART_Parity = USART_Parity_No;//   校验位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//    停止位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//   字长。不需要校验,所以选8位即可
    USART_Init(USART1, &USART_InitStructure);
    
    USART_Cmd(USART1, ENABLE);
}

//  发送字节
void Serial_SendByte(uint8_t Byte)
{
    USART_SendData(USART1, Byte);// 调用此库函数,Byte变量就写入TDR了,写完后需要等待TDR数据转移至移位寄存器。如果数据在TDR内再写入数据,会产生数据覆盖
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//  发送数据寄存器(TDR)空标志位
    //  这里标志位置1后不需要手动清零,当下一次再SendData时,此标志位会自动清零
}

//  发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)// 指向待发送数组的首地址。由于数组无法判断是否结束,所以需要传递一个Length进来
{
    uint16_t i;
    for(i = 0; i < Length; i++)
    {
        Serial_SendByte(Array[i]);
    }
}

//  发送字符串
void Serial_SendString(char *String)//  由于字符串自带一个结束标志位,所以不需要传递长度参数
{
    uint8_t i;
    for(i = 0; String[i] != 0; i++)//   循环条件用结束标志位来判断。这里数字0对应空字符,是字符串的结束标志位。如果不等于0,就是还没结束,进行循环
                                   //   这里数据0也可以写成字符形式,就是'\0',这就是空字符的转义字符表示形式for(i = 0; String[i] != '\0'; i++),和直接写0最终效果是一样的
    {
        Serial_SendByte(String[i]);
    }
}

//  计算次方函数
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
    uint32_t Result = 1;
    while(Y--)
    {
        Result *= X;
    }
    return Result;
}

//  发送数字
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
    //  需要把Number的个位、十位、百位等以十进制拆分开,然后转换成字符数字对应的数据,依次发送
    uint8_t i;
    for(i = 0; i < Length; i++)
    {
        Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');//    最终要以字符的形式发送,所以最后要加上字符的偏移,根据ASCII码表,字符0对应的数据是0x30,也可以以字符的形式写'0'
    }
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
uint32_t Serial_Pow(uint32_t X, uint32_t Y);
void Serial_SendNumber(uint32_t Number, uint8_t Length);

#endif

main.c

#include "stm32f10x.h"                  // Device 
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

int main(void)
{
	OLED_Init();
    Serial_Init();
    
    Serial_SendByte(0x41);//    调用此函数后,TX引脚就会产生一个0x41对应的波形,此波形可以发送给其他支持串口的模块,也可以通过USB转串口模块发送到电脑端
    
    uint8_t MyArray[] = {0x42, 0x43, 0x44, 0x45};
    Serial_SendArray(MyArray, 4);
    
    Serial_SendString("HelloWorld!\r\n");// 写完字符串后,编译器会自动补上结束标志位,所以字符串的存储空间,会比字符的个数大1
                                     // 如果需要换行,使用转义字符\r \n
    Serial_SendNumber(12345, 5);
    
	while(1)
	{
	}
}

其他引用的头文件和c代码可在此处查阅:OLED.h(【江协STM32】4 OLED调试工具,第5节)、  Delay.h(【江协STM32】3-2 LED闪烁&LED流水灯&蜂鸣器,第1.3节)

5.3 printf函数的移植 

使用printf前,需要打开工程选项,勾选“Use MicroLIB”。MicroLIB是Keil为嵌入式平台优化的一个精简库。

然后还需要对printf进行重定向,将printf函数打印的内容输出到串口。首先,在串口模块的最开始加上“#include <stdio.h>”(c文件和h文件都需要加)。之后,在串口模块内重写fputc函数即可,代码如下。因为fputc是printf函数的底层,printf函数在打印的时候,就是不断调用fputc函数一个个打印的,所以将fputc函数重定向到了串口,printf函数自然就输出到串口了。

int fputc(int ch, FILE *f)
{
    Serial_SendByte(ch);
    return ch;
}

此时便可以直接在主函数中使用printf函数,例如:printf("Num = %d\r\n", 666); 这种方式是最常用的方法。

上述printf函数的移植方法,printf重定向到串口1后,串口2就没有了。如果想要在多个串口上使用printf,可以使用下面的方法。

sprintf可以把格式化字符输出到一个字符串里,所以可以先定义一个字符串,然后sprintf中第一个参数是打印输出的位置,之后参数就和printf一样了,最后再把字符串通过串口发送出去,代码如下。

    char String[100];// 首先定义一个字符串,长度给够
    sprintf(String, "Num = %d\r\n", 666);// 将格式化字符串保存到String中
    Serial_SendString(String);//    将字符串通过串口发送出去,可以更改此函数的串口

可以将上述三行代码封装一下,简化使用过程。封装过程:首先在串口模块中添加头文件“#include <stdarg.h>” ,然后在最后对sprintf函数进行封装,代码如下

void Serial_Printf(char *format, ...)
{
    char String[100];
    va_list arg;//    定义一个参数列表变量
    va_start(arg, format);//    从format位置开始接收参数表,放在arg内
    vsprintf(String, format, arg);
    va_end(arg);//    释放参数表
    Serial_SendString(String);//    发送String
}

此时便可以直接在主函数中使用“Serial_Printf("Num = %d\r\n", 666); ”进行打印。

5.4 汉字显示乱码 

文档汉字编码格式UTF8。最终发送到串口,汉字会以UTF8的方式编码,所以在串口助手也要选择UTF8才能解码正确。

直接在printf函数中写汉字,编译器有时会报错,解决方法是打开工程选项,C/C++,杂项控制栏内写上“--no-multibyte-chars” 

另外,如果要切换文档编码格式。切换文档编码格式后,需要把汉字删掉,再把文件关掉,重新打开,编码格式才算改过来。

文档使用GB2312编码格式时 ,串口助手的解码要选择GBK。

6. 串口发送+接收

6.1 接线图

6.2 代码

在上一节代码的基础上添加接收部分的代码。

首先,GPIO要使用RX的引脚(PA10),引脚模式可以选择浮空输入或上拉输入。USART初始化中,模式部分增加串口模式RX。对于串口接收来说,可以使用查询和中断两种方法。

6.2.1 查询方法

查询的流程是,在主函数中不断判断RXNE标志位,如果置1就说明收到了数据,再读取DR寄存器即可。 

Serial.c(更改后的初始化函数)

void Serial_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//	上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;//    波特率
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//   不使用流控
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//   同时开启发送和接收
    USART_InitStructure.USART_Parity = USART_Parity_No;//   校验位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//    停止位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//   字长。不需要校验,所以选8位即可
    USART_Init(USART1, &USART_InitStructure);
    
    USART_Cmd(USART1, ENABLE);
}

main.c

#include "stm32f10x.h"                  // Device 
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

uint8_t RxData;

int main(void)
{
	OLED_Init();
    Serial_Init();
	while(1)
	{
		if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
        {
            RxData = USART_ReceiveData(USART1);//   将接收到的一个字节的数据保存在RxData中
            //  读DR时会自动清空RXNE标志位,所以这里不需要再清除标志位了
            OLED_ShowHexNum(1, 1, RxData, 2);
        }
	}
}

适用于程序比较简单的情况。 

6.2.2 中断方法 

首先,在初始化中需要加上开启中断的代码,开启RXNE标志位到NVIC的输出。之后配置NVIC,写中断函数。

Serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//	上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;//    波特率
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//   不使用流控
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//   同时开启发送和接收
    USART_InitStructure.USART_Parity = USART_Parity_No;//   校验位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//    停止位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//   字长。不需要校验,所以选8位即可
    USART_Init(USART1, &USART_InitStructure);
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//    开启RXNE标志位到NVIC的输出
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitTypeDef NVIC_InitSturcture;
    NVIC_InitSturcture.NVIC_IRQChannel = USART1_IRQn;//    中断通道
    NVIC_InitSturcture.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitSturcture.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitSturcture.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitSturcture);
    
    USART_Cmd(USART1, ENABLE);
}

//  发送字节
void Serial_SendByte(uint8_t Byte)
{
    USART_SendData(USART1, Byte);// 调用此库函数,Byte变量就写入TDR了,写完后需要等待TDR数据转移至移位寄存器。如果数据在TDR内再写入数据,会产生数据覆盖
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);//  发送数据寄存器(TDR)空标志位
    //  这里标志位置1后不需要手动清零,当下一次再SendData时,此标志位会自动清零
}

//  发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)// 指向待发送数组的首地址。由于数组无法判断是否结束,所以需要传递一个Length进来
{
    uint16_t i;
    for(i = 0; i < Length; i++)
    {
        Serial_SendByte(Array[i]);
    }
}

//  发送字符串
void Serial_SendString(char *String)//  由于字符串自带一个结束标志位,所以不需要传递长度参数
{
    uint8_t i;
    for(i = 0; String[i] != 0; i++)//   循环条件用结束标志位来判断。这里数字0对应空字符,是字符串的结束标志位。如果不等于0,就是还没结束,进行循环
                                   //   这里数据0也可以写成字符形式,就是'\0',这就是空字符的转义字符表示形式for(i = 0; String[i] != '\0'; i++),和直接写0最终效果是一样的
    {
        Serial_SendByte(String[i]);
    }
}

//  计算次方函数
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
    uint32_t Result = 1;
    while(Y--)
    {
        Result *= X;
    }
    return Result;
}

//  发送数字
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
    //  需要把Number的个位、十位、百位等以十进制拆分开,然后转换成字符数字对应的数据,依次发送
    uint8_t i;
    for(i = 0; i < Length; i++)
    {
        Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');//    最终要以字符的形式发送,所以最后要加上字符的偏移,根据ASCII码表,字符0对应的数据是0x30,也可以以字符的形式写'0'
    }
}

int fputc(int ch, FILE *f)
{
    Serial_SendByte(ch);
    return ch;
}

//  实现Serial_RxFlag标志位读后自动清除
uint8_t Serial_GetRxFlag(void)
{
    if(Serial_RxFlag == 1)
    {
        Serial_RxFlag = 0;
        return 1;
    }
    return 0;
}

uint8_t Serial_GetRxData(void)
{
    return Serial_RxData;
}

//  中断函数
void USART1_IRQHandler(void)
{
    if(USART_GetFlagStatus(USART1, USART_IT_RXNE) == SET)
    {
        Serial_RxData = USART_ReceiveData(USART1);
        Serial_RxFlag = 1;//    置标志位为1
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);//   清除标志位
    }
}

Serial.h

#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>
void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
uint32_t Serial_Pow(uint32_t X, uint32_t Y);
void Serial_SendNumber(uint32_t Number, uint8_t Length);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
    
#endif

main.c

#include "stm32f10x.h"                  // Device 
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

uint8_t RxData;

int main(void)
{
	OLED_Init();
    Serial_Init();
    OLED_ShowString(1, 1, "RxData:");
	while(1)
	{
		if(Serial_GetRxFlag() == 1)
        {
            RxData = Serial_GetRxData();//   将接收到的一个字节的数据保存在RxData中
            //  读DR时会自动清空RXNE标志位,所以这里不需要再清除标志位了
            Serial_SendByte(RxData);//  串口接收回传
            OLED_ShowHexNum(1, 8, RxData, 2);
        }
	}
}

目前这里只支持1个字节的接收,对于大量数据需要用数据包的形式进行传输。

其他引用的头文件和c代码可在此处查阅:OLED.h(【江协STM32】4 OLED调试工具,第5节)、  Delay.h(【江协STM32】3-2 LED闪烁&LED流水灯&蜂鸣器,第1.3节)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2273748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小程序租赁系统开发的优势与应用前景分析

内容概要 小程序租赁系统是一种新兴的数字化解决方案&#xff0c;旨在为用户提供更加便捷与高效的租赁服务。它通常包括一系列功能&#xff0c;如在线浏览、即时预定、支付功能以及用户反馈机制。这些系统在使用上极为友好&#xff0c;让用户能够轻松选择所需的商品或服务&…

25/1/8 算法笔记<强化学习> GYM环境

前几天花了好多时间在装各个仿真环境上&#xff0c;有V-rep,Pybullet,unity的Ml-agent,很多一大堆&#xff0c;好多好多问题差点逼疯我&#xff0c;可能就pybullet能玩一点&#xff0c;到之后学了机器人我再来玩它&#xff0c;最后的最后&#xff0c;我发现还得是我的gym&#…

学习随记:word2vec中归一化处理的作用

答案来自ai&#xff0c;直接复用为参考&#xff1a; 向量归一化的好处 将向量进行归一化&#xff0c;使其模长为 1&#xff08;即投射到单位圆/单位球上&#xff09;&#xff0c;在许多情况下具有实际意义和计算优势。以下是归一化的主要好处和原因&#xff1a; 1. 提高数值稳…

【C++】B2108 图像模糊处理

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;题目描述题目内容输入格式输出格式示例输入&#xff1a;输出&#xff1a; &#x1f4af;题目分析问题拆解 &#x1f4af;我的做法代码实现代码分析 &#x1f4af;老师的做法…

selenium+pyqt5自动化工具总结

说明&#xff1a;本工具是&#xff0c;操作外部google浏览器、selenium是无法操作qt界面中嵌套的浏览器的&#xff0c; 工具在后面 1. 代码结构 pycharm打开的文件下&#xff0c;再写一个子文件&#xff0c;文件导入的时候把子文件名带上 这样就可以在 外层使用命令 pyinst…

经典多模态模型CLIP - 直观且详尽的解释

对比语言-图像预训练&#xff08;CLIP&#xff09;&#xff0c;这是一种创新的多模态建模策略&#xff0c;能够创建视觉和语言的联合表示。CLIP 的效果非常出色&#xff0c;可以用于构建高度特定且性能卓越的分类器&#xff0c;而无需任何训练数据。本文将深入探讨其理论基础&a…

新时期下k8s 网络插件calico 安装

1、k8s master节点初始化完毕以后一直处于notreadey状态&#xff0c;一直怀疑是安装有问题或者是初始化有问题&#xff08;当然&#xff0c;如果真有问题要先解决这些问题&#xff09;&#xff0c;经过不断探索才发现是网络插件没有安装导致的&#xff0c;根据建议安装calico插…

【图像加密解密】Logistic混沌映射的彩色图像加密算法复现(含相关性检验)【Matlab完整源码 1期】

1、说明 本文给出详细完整代码、完整的实验报告和PPT。 环境&#xff1a;MATLAB2019a 复现文献&#xff1a;[1]黄硕.基于改进的Logistic混沌映射彩色图像加密算法[J].河南工程学院学报(自然科学版),2015,27(02):63-67. 主要目的是为了快速了解何为混沌序列、混沌序列产生、…

[AUTOSAR 基础入门] - RTE虚拟总线详解

文章目录 一、什么是RTE二、RTE的作用三、RTE对Runnables的运行支撑四、RTE与通信4.1. RTE – ECU之间通信4.2. RTE - Sender/Receiver 通信4.2.1 不使用队列&#xff08;直接访问&#xff09;4.2.2 不使用队列&#xff08;缓存访问&#xff09;4.2.3 使用队列 4.3 RTE - Clien…

Linux下文件操作相关接口

文章目录 一 文件是什么普通数据文件 二 文件是谁打开的进程用户 三 进程打开文件的相关的接口c语言标准库相关文件接口1. fopen 函数2. fread 函数3. fwrite 函数4. fclose 函数5. fseek 函数 linux系统调用接口1. open 系统调用2. creat 系统调用3. read 系统调用4. write 系…

用Python实现简单的任务自动化

目录 1. 自动发送邮件提醒 2. 自动备份文件 3. 自动下载网页内容 总结 在现代工作和生活中,任务自动化可以极大地提高效率和准确性。Python,作为一种功能强大且易于学习的编程语言,是实现任务自动化的理想选择。本文将通过几个简单而实用的案例,展示如何用Python实现任…

小程序开发-页面事件之上拉触底实战案例

&#x1f3a5; 作者简介&#xff1a; CSDN\阿里云\腾讯云\华为云开发社区优质创作者&#xff0c;专注分享大数据、Python、数据库、人工智能等领域的优质内容 &#x1f338;个人主页&#xff1a; 长风清留杨的博客 &#x1f343;形式准则&#xff1a; 无论成就大小&#xff0c;…

Docker 服务、镜像、容器之命令(Docker Services, Images, and Container Commands)

Docker 服务、镜像、容器之命令 Docker是一个强大的容器化平台&#xff0c;能够帮助开发者高效地构建、部署和管理应用程序。本文将详细介绍Docker的服务命令、镜像命令和容器命令&#xff0c;帮助你快速上手Docker。 一、Docker的服务相关命令 在使用Docker之前&#xff0c…

STM32内置Flash

一、原理 利用flash存储用户数据需要注意查看&#xff0c;用户数据是否会覆盖芯片运行程序。 IAP&#xff08;在程序中编程&#xff09;利用程序修改程序本身&#xff0c;和OTA是一个原理。IAP在程序中编程支持任意一种通信下载。 ICP&#xff08;在电路中编程&#xff0c;通…

两种方式实现Kepware与PLC之间的心跳检测

两种方式实现Kepware与PLC之间的心跳检测 实现Kepware与PLC之间的心跳检测1.OPCUA 外挂程序2.Kepware Advanced Tag 实现Kepware与PLC之间的心跳检测 1.OPCUA 外挂程序 这是通过上位程序来触发心跳的一种机制&#xff0c;在C#中&#xff0c;可以利用OPC UAOPCAutodll的方式…

英伟达Project Digits赋能医疗大模型:创新应用与未来展望

英伟达Project Digits赋能医疗大模型&#xff1a;创新应用与未来展望 一、引言 1.1 研究背景与意义 在当今数字化时代&#xff0c;医疗行业作为关乎国计民生的关键领域&#xff0c;正面临着前所未有的挑战与机遇。一方面&#xff0c;传统医疗模式在应对海量医疗数据的处理、复…

中国省级产业结构高级化及合理化数据测算(2000-2023年)

一、数据介绍 数据名称&#xff1a;中国省级产业结构高级化、泰尔指数 数据年份&#xff1a;2000-2023年 数据范围&#xff1a;31个省份 数据来源&#xff1a;中国统计年鉴、国家统计局 数据整理&#xff1a;内含原始版本、线性插值版本、ARIMA填补版本 数据说明&#xf…

关于Mac使用VSCode连接虚拟机

1. 下载插件 输入Remote - SSH下载下图两个插件。 2. 配置虚拟机信息 按图示步骤点击完成后&#xff0c;进入到虚拟主机的配置页面。 其中Host可以自定义主机名&#xff0c;HostName是虚拟机ip&#xff0c;可以通过ifconfig eth0查看ip&#xff0c;User是虚拟机的用户名。…

GOGOGO 接口

低高耦合?【程序中追求低耦合,所以接口广用】 低耦合:关联依赖性弱(你走了我还在) 高耦合:关联依赖性强(牵一发而动全身) 接口 概念:多个抽象方法的集合,只有结构无具体实现,并交给实现类完成功能操作【接口写功能,实现类写具体实现】 语法结构: 定义接口的关…

nginx反向代理+缓存

1、nginx-LB配置页面缓存 [rootOldboy conf]# vi nginx.conf http {include mime.types;default_type application/octet-stream;sendfile on;keepalive_timeout 65;include proxy.conf; …