STM-32:USART串口协议、串口外设—数据发送/数据发送+接收

news/2024/11/7 22:44:41/

目录

  • 一、串口通信
    • 1.1通信接口
    • 1.2串口通信
      • 1.2.1简介
      • 1.2.2硬件电路
      • 1.2.3串口参数及时序
  • 二、STM32的USART外设
    • 2.1USART简介
    • 2.2USART框图
  • 三、数据传输
    • 3.1数据帧
    • 3.2输入数据策略
      • 3.2.1起始位侦测
      • 3.2.2数据采样
    • 3.3波特率发生器
    • 3.4数据模式
  • 四、实际用例
    • 4.1串口发送
      • 4.1.1接线图
      • 4.1.2程序代码
    • 4.2串口发送+接收
      • 4.2.1接线图
      • 4.2.2程序代码

一、串口通信

1.1通信接口

通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统。比如STM32芯片里面集成了很多功能模块,如定时器计数、PWM输出、AD采集等等,这些都是芯片内部的电路,它们的配置寄存器、数据寄存器都在芯片里面,操作简单,直接读写就行。但是有些功能STM32内部没有,如想要蓝牙无线遥控的功能、想要陀螺仪加速度计测量姿态的功能,只能外挂芯片完成,数据都在STM32外面,这就需要通信线路发送或者接收数据完成数据交换。

通信协议:制定通信的规则,通信双方按照协议规则进行数据收发。STM32里的通信协议有如下表:(引脚列出的只是最简单最常用的值得注意的)
在这里插入图片描述
全双工为输入输出可同时进行,半双工为既可以输入也可以输出但不能同时进行,单工指只能输出或输入。

I2C和SPI有时钟线SCL为同步通信,没时钟线的为异步通信,需要双方约定一个采样频率,且需要加一些帧头帧尾等进行采样位置的对齐。

电平特性,单端就是它们引脚的高低电平都是对GND的电压差,所以单端信号通信的双方必须要共地,就是把GND接在一起。CAN和USB为差分数据通信,靠两个差分引脚的电压差来传输信号的,在通信时可以不用GND,不过USB协议里有些地方要单端信号,它还是需要共地的。差分信号有好的抗干扰特性,所以一般它的传输速度和距离都会比较高性能也不错。

USART和USB属于点对点通信,中间三个可挂载多个设备,需要一个寻址过程(设备编号)。

1.2串口通信

1.2.1简介

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。

单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力。主要特点就是用在单片机和电脑之间,其他如I2C、SPI等较复杂的通信协议多用在芯片与芯片之间,陀螺仪加速度计测量姿态芯片与STM32、MPU6050与STM32。
在这里插入图片描述

1.2.2硬件电路

简单双向串口通信有两根通信线(发送端TX和接收端RX)
TX与RX要交叉连接
当只需单向的数据传输时,可以只接一根通信线。
在这里插入图片描述
当电平标准不一致时,需要加电平转换芯片。串口有很多电平标准,像我们这种直接从控制器里面出来的信号一般都是TTL电平,相同的电平才能互相通信,几种点评标准如下介绍。

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:
(1)TTL电平:+3.3V或+5V表示1,0V表示0。低压小型设备,如单片机。
(2)RS232电平:-3到-15V表示1,+3到+15V表示0。一般在大型机器使用,由于环境比较恶劣静电干扰比较大,所以电压较大且允许波动的范围较广。
(3)RS485电平:两线压差+2到+6V表示1,-2到-6V表示0(差分信号)。通信距离可达上千米,上面两种最远几十米。

1.2.3串口参数及时序

串口中,每个字节都装载在一个数据帧(10或11位)里,每个数据帧都由起始位、数据位和停止位加粗样式组成,数据位有8个代表一个字节的8位。右图数据位9位多了一个奇偶校验位,跟在有效载荷(字节)后面。参数如下:

①波特率:串口通信的速率。波特率本来的意思是每秒传输码元的个数,单位是码元/s,或者直接叫波特(Baud),还有个速率叫比特率,每秒传输的比特数,单位是bit/s,或者是bps。在二进制调制下,一个码元就是一个bit,此时波特率等于比特率,单片机的串口通信基本都是二进制调制(高电平表示1,低电平表示0,一位就是1bit),所以串口的波特率经常会和比特率混用。

②起始位:标志一个数据帧的开始,固定为低电平。空闲状态为高电平,起始位产生下降沿,来告诉设备要开始发送数据了

③数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行。

④校验位:用于数据验证,根据数据位计算得来。这里串口用的是奇偶校验的数据验证方法,可以判断数据传输是否出错,如果出错可选择丢弃或者重传。
可选择三种方式,无校验、奇校验、偶校验。
奇校验,包括校验位在内的9个数据位会出现奇数个1,根据8位数据情况奇校验位补0或1,保证1的个数位奇数,接收方接收数据时,会验证数据位和校验位,检出率不高比如有两位同时出错,只校验奇偶特性是检验不出的。
偶校验同理,只能保证一定检出率。如果要更高检出率可以使用CRC校验。

⑤停止位:用于数据帧间隔,固定为高电平。也是为下一个起始位做准备(切换到高电平空闲状态)
在这里插入图片描述
在这里插入图片描述

二、STM32的USART外设

2.1USART简介

USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器。同步模式只是多了个时钟输出,只支持时钟输出不支持时钟输入,一般很少用,所以USART和UART基本相同。同步模式更多的是为了兼容别的协议或者特殊用途而设计的。

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

STM32F103C8T6 USART资源: USART1、 USART2、 USART3。

2.2USART框图

在这里插入图片描述
左上角TX、RX为发送和接收引脚,SW_RX、IRDA_OUT/IN是智能卡和IrDA通信的引脚(暂时不用)。TX由发送移位寄存器发出,RX通向接收移位寄存器。
灰色部分,TDR和RDR就是串口的数据寄存器了,发送和接收的字节存在这里,两个寄存器占用同一个地址(与51单片机串口的SBUF寄存器一样),所以在程序上只表现为一个寄存器DR。发送数据寄存器只写,接收数据寄存器只读。
比如某时刻在TXD里写入0x55,二进制为01010101,此时硬件检测到写入数据了,就会检查当前发送移位寄存器是否有数据在移位,如果没有那么01010101就会全部移动到寄存器准备发送,这时会置一个标志位TXE(TX Empty)发送寄存器空,检查这个标志位如果置1了,就可在TDR写入下一个数据(注意TXE置1时数据其实还没发送出去),然后发送移位寄存器在下面发送器控制的驱动下开始向右移位,一位一位地把数据送到TX引脚(向右移位与串口规定的低位先行是一致的)。

图下半部分主要是控制部分和增强功能。硬件数据流控,也叫硬件流控制,简称流控,之前介绍过,引脚nRTS(request to send)是请求发送,为输出脚,接到外部设备的CTS用于告诉外部设备现在自己是否可以接收;引脚nCTS(clear to send)是清除发送,是输入脚,接到外部设备的RTS用于接收别人nRTS的信号,查看别人是否可以接收(也就是要求对方也要有流控引脚)。”n“的意思是低电平有效。(一般不用,了解一下)
SCLK控制和SCLK这部分用于产生同步的时钟信号,配合发送移位寄存器输出的,发送寄存器每移位一次,同步时钟电平就跳变一个周期,时钟告诉对方我移出一位数据了,只支持输出不支持输入,所以两个USART之间不能实现同步串口通信。它的第一个用途是,兼容别的协议,比如串口加上时钟后跟SPI协议很像,有了时钟输出的串口就可兼容SPI;这个时钟也可做自适应波特率,比如接收设备不确定发送设备给的什么波特率,就可测量下此时钟周期计算得到波特率(一般不用,了解一下)
唤醒单元,作用是实现串口挂载多设备。之前说过串口一般为点对点通信(只支持两个设备,直接发收数据就行),而多设备通信在一条总线可接多个从设备,每个设备分配一个地址,通信要先进行寻址确定通信对象,再进行数据收发。唤醒单元可使其获得这种功能,在USART地址处给串口分配一个地址,当发送指定地址时此设备唤醒开始工作,当发送别的设备地址时别的设备工作,没收到地址的设备不唤醒保持沉默。

中断(输出)控制,申请中断位就是状态寄存器SR的各种标志位,其中TXE发送寄存器空和RXNE接收寄存器非空比较重要,是必要的。中断控制就是配置中断是否可以通向NVIC。
最下面为波特率发生器部分,波特率发生器其实就是分频器,APB时钟进行分频,得到发送和接收移位的时钟。时钟输入是fPCLKx(x=1或2),USART1挂载在APB2上所以为PCLK2时钟,一般72MHz;其他的USART都挂载在APB1,为PCLK1的时钟,一般为36MHz。时钟除以一个USARTDIV分频系数进行分频,USARTDIV为虚线框内为一个数值,分为DIV_Mantissa整数部分和DIV_Fractior小数部分,分频更加精准。
分频完还要再除以16,得到发送器时钟和接收器时钟,通向发送器和接收器控制部分。如果TE(TX Enable)为1,即发送器使能,发送部分的波特率有效,如果RE(RX Enable)为1,就是接收器使能,接收部分的波特率有效。
在这里插入图片描述

三、数据传输

3.1数据帧

在这里插入图片描述
数据帧下的时钟波就是之前说过的同步时钟输出的功能,在每个数据位的中间都有一个时钟上升沿,时钟频率与数据速率一样,接收端可以在时钟上升沿处进行采样,这样可以精准定位每一位数据,时钟的最后一位(虚线)可通过LBCL位控制要不要输出,时钟的极性、相位等也可通过配置寄存器配置。

空闲帧从头到尾都是1,断开帧从头到尾都是0,它们用于局域网协议,串口用不到,可了解下。

8位字长也可以设置有无校验位,一般为了发送完整字节都选择无校验。9位字长常选择有校验。
在这里插入图片描述
以上为不同停止位的波形变化。STM32的串口可配置停止位长度为0.5,1,1.5,2四种,本质是时长不同。一般选择1位。

3.2输入数据策略

串口的输出TX比输入RX简单很多,输出就定时翻转TX引脚高低电平;但是输入要保证采样频率和波特率一致,还要保证每次输入采样的位置,要正好处于每一位正中间,这样读进来的电平最可靠,另外输入要对噪声有一定判断能力,如果是噪声最好能置个标志位提醒一下。

3.2.1起始位侦测

在这里插入图片描述
当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率连续采样一帧数据,同时从起始位开始,采样位置就要对齐到位的正中间,只要第一位对齐后面就肯定对齐了。

首先输入电路对采样时钟进行了细分,会以波特率的16倍频率进行采样,即在1位的时间里进行16次采样。策略是最开始空闲状态高电平,那采样就一直是1,在某个位置突然采到0就说明在两次采样之间出现了下降沿,如果没有任何噪声那么之后应该就是起始位了,在起始位会进行连续16次采样,没有噪声的话16次都是0。
实际上会有噪声,所以即使出现下降沿后续也要再采样几次,以防万一,根据手册接收电路还会在下降沿出现之后的第3次、5次、7次进行一批采样,在第8次、9次、10次再进行一批采样,且这两批采样都要要求每三位里面至少有2个0(如图),才算检测到起始位。如果三位都是0那就无噪声,检测到起始位;如果有两个0,也算检测到起始位但会在状态寄存器里会置一个NE(Noise Error)噪声标志位,用于提醒数据是收到了但是有噪声,用的时候注意下;如果三位只有一个0,就不算检测到起始位,重新捕捉下降沿。
如果通过了起始位侦测,接收状态就由空闲变为接收起始位,同时第8、9、10次采样的位置正好是起始位的正中间,之后接收数据位时就都在第8、9、10次进行采样,保证再正中间。

3.2.2数据采样

在这里插入图片描述
从1到16是一个数据位的时间长度,即一个数据位有16个采样时钟,由于起始位侦测已经对齐了采样时钟,所以这里就直接在第8、9、10次采样数据位。为了保证数据可靠性连续采样3次(8、9、10),如果没有噪声时,3次都为1或0,那么就认为采样的为1或0;如果有噪声造成3次中既有0也有1,按照2:1规则来,按出现2次的,这种情况NE噪声标志位也会置1。

3.3波特率发生器

在这里插入图片描述
在这里插入图片描述
比如要配置USART为9600的波特率,那么BRR寄存器的配置为:代入公式9600=72M/(16xDIV),解得DIV=468.75,二进制数为111010100.11,所以写入DIV整数部分的是111010100(前面未满的位补零),DIV小数部分写入的是11(后面未满的位补零)。不过用库函数就很方便。

3.4数据模式

在这里插入图片描述

四、实际用例

4.1串口发送

4.1.1接线图

在这里插入图片描述

4.1.2程序代码

Serial.c

#include "stm32f10x.h"                  // Device header
//1、开启时钟,打开USART和GPIO时钟都打开
//2、GPIO初始化,把TX配置成复用输出,RX配置为输入(本代码没用到接收功能)
//3、配置USART,使用一个结构体即可配置所有相关参数
//4、如果只需要发送功能,直接开启USART初始化就结束了
//(5、如果需要接收功能,可能需要配置中断,在开启USART之前加上ITConfig和NVIC的代码就行)
//初始化完成后,如果需要发送数据调用一个发送函数就行,接收数据同理,如果要获取发送和接收标志位也是调用一个函数
void Serial_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//USART1为APB2总线上的外设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;//供串口外设TX脚使用GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate= 9600;//Init函数内部会自动计算需要的分频系数写入BRR寄存器USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None;//无流控USART_InitStructure.USART_Mode= USART_Mode_Tx;//输出发送USART_InitStructure.USART_Parity= USART_Parity_No;//Odd奇、Even偶、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);while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);//等待置1//该标志位不需要手动清零,下一次SendData这个标志位会自动清零
}void Serial_SendArray(uint8_t* Array,uint16_t Length){//第一个参数为uint8_t*类型指向数组首地址,传递数组需要用到指针//第二个参数由于数组无法判断是否结束,用Lengthuint16_t i;for(i=0;i<Length;i++){Serial_SendByte(Array[i]);}
}void Serial_SendString(char* String){//uint8_t也可以,由于字符串自带结束标志位0,不用Length了uint8_t i;for(i=0;String[i] != '\0';i++){//可以把字符串当作一个数组Serial_SendByte(String[i]);}}uint32_t Serial_Pow(uint32_t X,uint32_t Y){//次方函数X**Yuint32_t Result = 1;while(Y--){Result *= X;}return Result;
}
void Serial_SendNumer(uint32_t Number,uint8_t Length){//首先要把需要发送的Number的个位、十位、百位、千位等每一位以十进制拆分开//然后转换为字符数字对应的数据,依次发送出去//以十进制拆分:比如一个数字为1234,取千位1:1234/10**3=1.234;1.234%10=1uint8_t i;for(i=0;i<Length;i++){Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+0x30);//之所以是Length-i-1是因为从高位开始取的(如1234依次取千位、百位、十位、个位依次发送)//之所以+0x30是因为要用字符(文本)显示,ASCII码表里字符0对应0x30。其实也可写成+'0'}}

main.c

#include "stm32f10x.h"   // Device header
#include "Delay.h"   
#include "OLED.h"
#include "Serial.h"int main(void){OLED_Init();Serial_Init();Serial_SendByte(0x22);//程序的逻辑是上电后,初始化串口,再利用STM32串口发送一个0x22。//调用Serial_SendByte(0x22)后TX引脚会产生一个0x22对应的波形,这个波形可以发送给其他支持串口的模块//这里使用USB转串口模块发送给电脑端,需要一个串口助手软件查看uint8_t MyArray[]={0x42,0x43,0x44,0x45};Serial_SendArray(MyArray,4);Serial_SendString("Hello world!\n");Serial_SendNumer(1234 , 4);while(1){}
}

串口助手软件里接收模式选择HEX模式,就是以原始数据的形式显示,如发送0x41,显示为41;如果想显示字符串可选择文本模式,以字符形式显示,如发送0x41显示为A。
HEX模式也叫做十六进制模式或二进制模式,以原始数据显示,只能显示一个个十六进制数;
文本模式也叫字符模式,通过原始数据查找字符集编码成一个字符,如ASCII码表是最简单的字符集,如果想显示和存储汉字的话需要汉字字符集,如GB2312、GBK、GB18030等。为了标准化不同国家的字符集,出现了Unicode字符集,Unicode最常用的传输形式是UTF8。
0x41到A叫做译码,A到0x41叫做编码。

4.2串口发送+接收

4.2.1接线图

在这里插入图片描述

4.2.2程序代码

Serial.c

#include "stm32f10x.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;USART_Init(USART1,&USART_InitStructure);//对于串口接收可使用查询和中断两种方法,如果使用查询到此初始化就结束//查询流程:在主函数里不断判断RXNE标志位,置1说明收到数据了,//再调用USART_ReceiveData读取DR(与上面的Serial_SendByte类似)//下面我们程序中实现下中断的方法:USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启RXNE标志位到NVIC的输出NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC分组NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel= USART1_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd= ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority= 1;NVIC_Init(&NVIC_InitStructure);//初始化NVIC的USART通道USART_Cmd(USART1,ENABLE);
}void Serial_SendByte(uint8_t Byte){USART_SendData(USART1,Byte);while (USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);}void Serial_SendArray(uint8_t* Array,uint16_t 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++){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_SendNumer(uint32_t Number,uint8_t Length){uint8_t i;for(i=0;i<Length;i++){Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+0x30);}}//RXNE标志位一但置1,就会向NVIC申请中断,之后就会在中断函数里接收数据
//其实就是在中断里面对数据进行了一次转存,最终还要扫描查询RxFlag来接收数据
//放在这里转运一个字节意义看着不大,但是为下节多字节数据包接收作铺垫
void USART1_IRQHandler(void){//中断函数名是固定的if (USART_GetFlagStatus(USART1,USART_IT_RXNE)==SET){Serial_RxData= USART_ReceiveData(USART1);Serial_RxFlag= 1;USART_ClearITPendingBit(USART1,USART_IT_RXNE);//标志位,如果读取了DR就会自动清零,如果没读取DR,就需要手动清零。这里清零下也不影响}
}uint8_t Serial_GetRxFlag(void){//实现读后自动清除功能if(Serial_RxFlag == 1){Serial_RxFlag=0;return 1;}return 0;
}
uint8_t Serial_GetRxData(void){//封装接收数据函数return Serial_RxData;
}

main.c

//main.c
#include "stm32f10x.h"   
#include "Delay.h"   
#include "OLED.h"
#include "Serial.h"uint8_t RxData;int main(void){OLED_Init();OLED_ShowString(1,1,"RxData:");Serial_Init();while(1){if (Serial_GetRxFlag()==1){RxData =Serial_GetRxData();//接收数据Serial_SendByte(RxData);//把接受到的这一数据回传到电脑OLED_ShowHexNum(1,8,RxData,2);}}
}

http://www.ppmy.cn/news/40596.html

相关文章

在MDK5(Keil537)中同时配置STM32和C51的环境(简单可行)

1.首先安装MDK5&#xff0c;可以看到&#xff0c;安装路径为D盘下的Keil_v4Andv5文件夹&#xff0c;next进行安装 2.安装完成后,这一步非常重要&#xff0c;将TOOLS文件改名&#xff0c;随便改什么都行。否则下载keil4时产生的TOOL文件将会取消下载或者替换掉原文件 3.接下来下…

python通过pil模块获得图片exif信息的方法

python通过pil模块获得图片exif信息的方法 本文实例讲述了python通过pil模块获得图片exif信息的方法。分享给大家供大家参考。具体分析如下&#xff1a; python的pil模块功能超级强大&#xff0c;不但可以用来处理图片也可以用来获取图片的exif数据 from PIL import Image #cod…

MacOS IDEA无法打开报错NSInternalInconsistencyException

文章目录1.现象2.解决办法3.参考资料1.现象 今天要Coding的时候打开IDEA发现报错&#xff0c;无法运行&#xff0c;报如下错误&#xff1a; 忘了怎么看的了&#xff0c;可能是看报告&#xff1f;&#xff1f; 可以看到具体的报错信息&#xff0c;大概就是类似这样 2.解决办…

Pytorch自定义中心损失函数与交叉熵函数进行[手写数据集识别],并进行对比

加上中心损失函数 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms# 设置随机种子&#xff0c;确保实验可重复性 torch.manual_seed(42) torch.backends.cudnn.deter…

【Qt笔记】4.文本编辑的简单使用

文章目录1 概述2 QLineEdit3 QTextEdit4 QPlainTextEdit1 概述 QT中的文本编辑类常用的有三种&#xff0c; 1.QLineEdit&#xff1a;单行普通文本2.QTextEdit&#xff1a;多行富文本3.QPlainTextEdit&#xff1a;多行普通文本 富文本指的是多文本格式。可以编辑图片和媒体相…

蚁群算法 | 运筹优化

1、原理 蚁群算法是由自然界中蚂蚁觅食的行为而启发的。在自然界中&#xff0c;蚂蚁觅食过程中&#xff0c;蚁群总能够寻找到一条从蚁巢到食物源的最优路径。信息素是蚂蚁之间交流的工具之一&#xff0c;当有蚂蚁走过时&#xff0c;它将会在它行进的路上释放出信息素&#xff…

第十三届蓝桥杯大赛软件赛省赛 C/C++ 大学 B 组思考+总结

第十三届蓝桥杯大赛软件赛省赛 C/C 大学 B 组思考总结 公告 第十三届蓝桥杯大赛软件赛省赛 C/C 大学 B 组 C题 刷题统计 D题 修剪灌木 E题 X进制减法 F题 统计子矩阵 G题 积木画 H题 扫雷 I题 李白打酒加强版 J题 砍竹子 注意事项&#xff1a;1. 对于编程题目&#xff…

微软将 Exchange Online CAR 的弃用推迟到 2024 年

微软在 4 月 8 日宣布&#xff0c;Exchange Online 中的客户端访问规则 (CAR) 弃用将推迟一年&#xff0c;直到 2024 年 9 月。 Microsoft 365 管理员可以利用包含优先级值、异常、操作和条件的 CAR&#xff0c;使用各种因素过滤客户端对 Exchange Online 的访问。 这些因素包…