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

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节)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第一 二章 小车硬件介绍-(全网最详细)基于STM32智能小车-蓝牙遥控、避障、循迹、跟随、PID速度控制、视觉循迹、openmv与STM32通信、openmv图像处理、smt32f103c8t6

第一篇-STM32智能小车硬件介绍 后续章节也放这里 持续更新中&#xff0c;视频发布在小B站 里面。这边也会更新。 B站视频合集: STM32智能小车V3-STM32入门教程-openmv与STM32循迹小车-stm32f103c8t6-电赛 嵌入式学习 PID控制算法 编码器电机 跟随 小B站链接:https://www.bilib…

【网络】电路交换(Circuit Switching)、报文交换(Message Switching)和分组交换(Packet Switching)

电路交换&#xff08;Circuit Switching&#xff09;&#xff1a;一条专用的通信线路&#xff08;或电路&#xff09;&#xff08; 电话专用线路&#xff0c;好处&#xff1a;专用稳定&#xff0c;有没有数据都被占用&#xff0c;坏处&#xff1a;容易浪费&#xff09; 报文交换…

Pixel 6a手机提示无法连接移动网络,打电话失败!

1、开启VoLTE 2、如果没有&#xff0c;下载shizuku和PixelIMS应用。 shizuke Releases RikkaApps/Shizuku GitHub PixellMS Release v1.2.8 kyujin-cho/pixel-volte-patch GitHub 3、安装shizuke启动&#xff0c;开通root可以直接点击下面的启动&#xff0c;如果没有就…

游戏关卡设计的常用模式

游戏关卡分为很多种&#xff0c;但常用的有固定套路&#xff0c;分为若干种类型。 关卡是主角与怪物、敌方战斗的场所&#xff0c;包括装饰物、通道。 单人游戏的关卡较小&#xff0c;偏线性&#xff1b; 联机/MMO的关卡较大&#xff0c;通道多&#xff0c;自由度高&#xf…

DC/AC并网逆变器模型与仿真MATLAB

DC/AC并网逆变器是一种将直流电&#xff08;DC&#xff09;转化为交流电&#xff08;AC&#xff09;&#xff0c;并将其与电网并联的设备。它的核心功能是实现直流电源&#xff08;如光伏电池板或储能电池&#xff09;与电网的有效连接&#xff0c;同时保证输出电能质量满足电网…

作业:IO:day2

题目一 第一步&#xff1a;创建一个 struct Student 类型的数组 arr[3],初始化该数组中3个学生的属性 第二步&#xff1a;编写一个叫做save的函数&#xff0c;功能为 将数组arr中的3个学生的所有信息&#xff0c;保存到文件中去&#xff0c;使用fread实现fwrite 第三步&#xf…

环动科技平均售价波动下滑:大客户依赖明显,应收账款周转率骤降

《港湾商业观察》施子夫 2024年12月18日&#xff0c;浙江环动机器人关节科技股份有限公司&#xff08;以下简称&#xff0c;环动科技&#xff09;的上市审核状态变更为“已问询”&#xff0c;公司在11月25日科创板IPO获上交所受理&#xff0c;独家保荐机构为广发证券。 此次环…

【数据可视化-11】全国大学数据可视化分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

SAP 02-AMDP Functions for CDS Table Functions

1. 创建一个Core Data Service Table Functions 新建 Core Data Service Table Function 定义CDS Table Functions EndUserText.label: a simple AMDP for CDS Table Functions ClientDependent: true //打开 Open SQL 的自动客户端处理 defin…

Ungoogled Chromium127 编译指南 MacOS篇(八)- 开始编译

1. 引言 完成了所有依赖包的安装后&#xff0c;我们终于来到了最关键的编译阶段。在开始编译之前&#xff0c;有一些重要的配置信息需要了解。本文将指导您完成整个编译过程。 2. 签名相关说明 虽然在我们的测试编译中不需要进行签名操作&#xff0c;但了解官方的签名要求仍…

SpringBootWeb案例-1(day10)

准备工作 需求 & 环境搭建 需求说明 环境搭建 步骤&#xff1a; 准备数据库表(dept、emp)创建 springboot 工程&#xff0c;引入对应的起步依赖&#xff08;web、mybatis、mysql 驱动、lombok&#xff09;配置文件 application.properties 中引入 mybatis 的配置信息&…

动手学深度学习-卷积神经网络-1从全连接层到卷积

目录 不变性 多层感知机的限制 平移不变性 局部性 卷积 “沃尔多在哪里”回顾 通道 小结 我们之前讨论的多层感知机十分适合处理表格数据&#xff0c;其中行对应样本&#xff0c;列对应特征。 对于表格数据&#xff0c;我们寻找的模式可能涉及特征之间的交互&#xff0…

【HTML+CSS+JS+VUE】web前端教程-4-标签之段落、换行、水平线

标签之段落 段落是通过<p>标签定义的换行 如果您希望在不生产一个新段落的情况下进行换行&#xff08;新行&#xff09;&#xff0c;请使用<br><br />元素是一个空的HTML元素 <p>这个<br>段落<br>演示了分行的效果</p>水平线 <…

vulnhub靶场【DC系列】之7

前言 靶机&#xff1a;DC-7&#xff0c;IP地址为192.168.10.13 攻击&#xff1a;kali&#xff0c;IP地址为192.168.10.2 都采用VMWare&#xff0c;网卡为桥接模式 对于文章中涉及到的靶场以及工具&#xff0c;我放置在网盘中&#xff0c;链接&#xff1a;https://pan.quark…

腾讯云AI代码助手编程挑战赛-图片转换工具

作品简介&#xff1a; 解决了人们学习生活中的图片格式转换问题&#xff0c; 制作该脚本&#xff0c;省去了打开在线编辑器操作的时间&#xff0c; 免费为用户提供图片格式的转换的实用小工具 技术架构 python语言的tk库来完成的GUI页面设计&#xff0c; 引用PIL包转换图…

Linux 文件的特殊权限—ACL项目练习

本文为Ubuntu Linux操作系统- 第二十一期~~ 上期回顾: 【ACL权限控制详解】 更多Linux 相关内容请点击&#x1f449;【Linux专栏】~ 主页&#xff1a;【练小杰的CSDN】 文章目录 项目项目要求具体的设置命令如下问题2问题3第一步&#xff1a;设置默认ACL前&#xff0c;在projec…

系统日志优化---自定义springboot-starter日志组件供各个服务使用

在优化项目时发现各个微服务都有各自的接口调用日志逻辑&#xff0c;比如每个服务都定义一个aop类拦截&#xff0c;十分冗余&#xff0c;其实是可以做成starter被各个服务引用使用&#xff0c;前提要先了解一下springboot自动装配原理 创建springboot工程&#xff0c;如果是jdk…

Matlab贝叶斯估计MCMC分析药物对不同种群生物生理指标数据评估可视化

全文链接&#xff1a;https://tecdat.cn/?p38756 摘要&#xff1a;本文着重探讨了如何利用Matlab实现贝叶斯估计。阐述了具体的实现流程&#xff0c;涵盖数据加载、先验常数设定、马尔可夫链蒙特卡洛&#xff08;MCMC&#xff09;属性指定、模型构建、运行链条以及结果查看等环…

js:日期对象和dom节点

日期对象 事件对象在前端开发里经常用来表示日期&#xff1a; 可以获取当前系统的时间 实例化 使用new关键字来实例化一个对象&#xff1a; const date new Date()console.log(date); 获取当前时间 const date new Date(2008-8-8)console.log(date); 获取指定时间 写得…

Ⅱ.INTRODUCTION TO CUDA C (CUDA C 入门)

前言 上一节环境配置好了&#xff0c;我们开始吧&#xff01; 一、A First Program 1. Hello, World! 我们先写一个C语言的 Hello, World! 作为对比 int main(void){printf("Hello, World!\n");return 0; }大家应该知道这个代码运行在CPU上吧&#xff0c;我们CP…