(三)STM32F407 cubemx串口中断通讯

(三)STM32F407 cubemx串口中断通讯

这篇文章主要是个人的学习经验,想分享出来供大家提供思路,如果其中有不足之处请批评指正哈。

废话不多说直接开始主题,本人是基于STM32F407VET6芯片,但是意在你看懂这篇文章后,不管是F1,F4,H7等一系列系统串口通讯配置都能明白如何通过参考手册去学习配置。而不是Ctrl c,Ctrl v。
串口通讯其实cubemx已经把步骤精简的不能再精简了,但是秉持着不断学习的理念,有必要去大概学习一下串口协议,以及cubemx是如何根据参考手册封装的。

在这里插入图片描述以上是串口协议比较重要的几个点,跟着参考手册一个一个点的讲解,了解串口协议的重点。首先串口主要有3根线TX、RX、GND。根据以上的几个点知一个串口数据包主要由1.起始位 2.数据字 3.停止位构成其他的寄存器都是围绕它们为它们服务。

在这里插入图片描述接下来我将参照串口助手一步步讲解。
在这里插入图片描述
1)波特率
发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。波特率从某种意义上来说,它就是时钟的一种表现形式,115200意思是1秒内产生115200个高低电平变化。一个8位字长的串口数据=8字长+1起始位+1停止位=10;也就是1秒钟能发送115200/10=11520个数据,也就是1/11520=86us平均86us发一个‘A’(数据)。

在这里插入图片描述2)停止位
USART支持多种停止位的配置:0.5、1、1.5和2个停止位。
1.1个停止位:停止位位数的默认值。
2.2个停止位:可用于常规USART模式、单线模式以及调制解调器模式
3. 0.5个停止位:在智能卡模式下接收数据时使用。
4.1.5个停止位:在智能卡模式下发送和接收数据时使用。空闲帧包括了停止位
在这里插入图片描述停止位是通讯双方需要一致的,通常情况都是默认为1。在USART__CR2中编程停止位的位数。
若停止位需要更改长度
方一cubemx自动配置在这里插入图片描述
方二代码hal库代码调用
在这里插入图片描述在这里插入图片描述hal库只提供了两种一个停止位,和两个停止位的定义

3)起始位中断信号产生
在USART中,如果辨认出一个特殊的采样序列,那么就认为侦测到一个起始位。该序列为:1110X0X0X0000
在这里插入图片描述
起始位的开始标志是1110低电平下降沿,当下降沿触发时它会间隔采样,X就是它采样的信号是不确定的,但是若需要进入串口中断必须每一次采样的X(采样点为3,5,7,8,9,10)都为0才可确认收到起始位,这时设置RXNE标志位,如果RXNEIE=1,则产生中断。若其中采样X为1不管什么原因,返回空闲状态等待下降沿。

4)数据位
USART可以根据USART_CR1的M位接收8位或9位的数据字
8位数据的信号是和ASCLL码表一一对应的比如‘A’=65=0x41=01000001 8位传输的数据就是低高低低低低低高
在这里插入图片描述
5)奇偶校验
设置USART_CR1寄存器上的PCE位,可以使能奇偶控制(发送时生成一个奇偶位,接收时进行奇偶校验)。根据M位定义的帧长度,可能的USART帧格式列在下表中。
在这里插入图片描述偶校验:校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为偶数。
例如:数据=00110101,有4个1’,如果选择偶校验(在USART_CR1中的PS=0),校验位将是’0’。4个1凑个偶数,校验位为0
奇校验:此校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为奇数。
例如:数据=00110101,有4个’1’,如果选择奇校验(在USART_CR1中的PS=1),校验位将是’1’。4个1凑个奇数,还需一个1,校验位为1
这是参考手册的例子很清楚,但是可能是自己才疏学浅本人并不知道校验位有什么很大的作用,有大佬的话可以评论区指导一下。
在这里插入图片描述

我们默认使用的是第一种00
在这里插入图片描述

0x0000u这样的常数一律默认为int型0,不是什么字节。

关键内容讲完了开始cubemx配置实现功能

基础时钟配置等请见:http://t.csdnimg.cn/XQ0L6
在这里插入图片描述中断勾上
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述
keil5处点击魔术棒,给红框处打上勾。

在这里插入图片描述
dubug处我用的下载器是DAP可能和你们的不一样,勾上重新下载程序后复位功能。

  uint8_t AA='A';HAL_UART_Transmit(&huart1,&AA,1,10000);

HAL_UART_Transmit函数语法很简单,只要配置串口号,内容,内容长度,发送时间即可
主函数

int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */uint8_t AA='A';HAL_UART_Transmit(&huart1,&AA,1,10000);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

考虑到重定义函数printf只有一个串口可用,本人给大家提供了printf转译其他串口也可使用printf
主函数部分

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern UART_HandleTypeDef huart1;
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t uart1_rxbuf[10];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */uint8_t AA='A';HAL_UART_Transmit(&huart1,&AA,1,10000);u1_printf("您的身高:%.0f cm\r\n",2);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLM = 4;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}
#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

usart.h

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "main.h"
#include "stdarg.h"		//包含需要的头文件 
#include "string.h"     //包含需要的头文件
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
#define USART1_RX_ENABLE     0      //是否开启接收功能  1:开启  0:关闭
#define USART1_TXBUFF_SIZE   256    //定义串口1 发送缓冲区大小 256字节
#define USART1_RXBUFF_SIZE   256               //定义串口1 接收缓冲区大小 256字节
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
void u3_printf(char* fmt,...) ;
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */

usart.c

/* Includes ------------------------------------------------------------------*/
#include "usart.h"/* USER CODE BEGIN 0 *//* USER CODE END 0 */UART_HandleTypeDef huart1;/* USART1 init function */void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 115200;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{GPIO_InitTypeDef GPIO_InitStruct = {0};if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspInit 0 *//* USER CODE END USART1_MspInit 0 *//* USART1 clock enable */__HAL_RCC_USART1_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;GPIO_InitStruct.Alternate = GPIO_AF7_USART1;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);/* USART1 interrupt Init */HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);HAL_NVIC_EnableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspInit 1 *//* USER CODE END USART1_MspInit 1 */}
}void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{if(uartHandle->Instance==USART1){/* USER CODE BEGIN USART1_MspDeInit 0 *//* USER CODE END USART1_MspDeInit 0 *//* Peripheral clock disable */__HAL_RCC_USART1_CLK_DISABLE();/**USART1 GPIO ConfigurationPA9     ------> USART1_TXPA10     ------> USART1_RX*/HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);/* USART1 interrupt Deinit */HAL_NVIC_DisableIRQ(USART1_IRQn);/* USER CODE BEGIN USART1_MspDeInit 1 *//* USER CODE END USART1_MspDeInit 1 */}
}/* USER CODE BEGIN 1 */
__align(8) char Usart1_TxBuff[USART1_TXBUFF_SIZE];  
void u1_printf(char* fmt,...) 
{  unsigned int i,length;va_list ap;va_start(ap,fmt);vsprintf(Usart1_TxBuff,fmt,ap);va_end(ap);	length=strlen((const char*)Usart1_TxBuff);		while((USART1->SR&0X40)==0);for(i = 0;i < length;i ++){			USART1->DR = Usart1_TxBuff[i];while((USART1->SR&0X40)==0);	}	
}
/* USER CODE END 1 */

实验效果
在这里插入图片描述
串口发送中断主函数

	HAL_UART_Receive_IT(&huart1,&Data, sizeof(Data));//开启中断接收数据uint8_t IT_SEND[]="IT_SEND";HAL_UART_Transmit_IT(&huart1,IT_SEND, sizeof(IT_SEND)/sizeof(IT_SEND[0]));u1_printf("\r\n");

只需要在主函数处添加这一部分代码。
效果图
在这里插入图片描述串口中断接收本人采用的方式是结束位为0x5c串口接收结束,长度不可大于15个字节,实例代码如下

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart == &huart1){if(recv_end_flag_1==1){for(int i=0;i<sizeof(rx_buffer1)/sizeof(rx_buffer1[0]);i++){rx_buffer1[i]=0;}recv_end_flag_1 = 0;          //数据清空}rx_buffer1[Cnt]=Data;Cnt++;if(rx_buffer1[Cnt-1] == 0x5C)//判断是否为'\'结尾{//rx_buffer1[Cnt-1] = 0x0a; u1_printf("\n接收到的数据为:\n");HAL_UART_Transmit(&huart1,rx_buffer1,Cnt,10000);//显示在串口助手Cnt = 0;recv_end_flag_1 = 1;          //数据接收完成for(int i=0;i<sizeof(rx_buffer1)/sizeof(rx_buffer1[0]);i++){rx_buffer1[i]=0;}}HAL_UART_Receive_IT(&huart1,&Data,1);//继续接收数据}
} 

在这里插入图片描述显示效果如图。

整体的所有代码以及数据手册链接:
链接:https://pan.baidu.com/s/1xmEUNd82dunSmh_NyI09fg?pwd=6dqr
提取码:6dqr
希望这篇文章对你有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MyBatisPlus学习二:常用注解、条件构造器、自定义sql

常用注解 基本约定 MybatisPlus通过扫描实体类&#xff0c;并基于反射获取实体类信息作为数据库表信息。可以理解为在继承BaseMapper 要指定对应的泛型 public interface UserMapper extends BaseMapper<User> 实体类中&#xff0c;类名驼峰转下划线作为表名、名为id的…

python的课后练习总结4(while循环)

for循环用于针对序列中的每个元素的一个代码块。 while循环是不断的运行&#xff0c;直到指定的条件不满足为止。 while 条件&#xff1a; 条件成立重复执行的代码1 条件成立重复执行的代码2 …….. i 1while i < 5:print(i)i i 11、使用wh…

后端中的Dao层、Service层、Impl层、utils层、Controller层

Java Dao层 dao层叫数据访问层&#xff0c;全称为data access object&#xff0c;属于一种比较底层&#xff0c;比较基础的操作&#xff0c;具体到对于某个表、某个实体的增删改查&#xff0c;对外提供稳定访问数据库的方法 Mapper:&#xff08;DAO&#xff09; 访问数据库&am…

工作中人员离岗识别摄像机

工作中人员离岗识别摄像机是一种基于人工智能技术的智能监控设备&#xff0c;能够实时识别员工离岗状态并进行记录。这种摄像机通常配备了高清摄像头、深度学习算法和数据处理系统&#xff0c;可以精准地监测员工的行为&#xff0c;提高企业的管理效率和安全性。 工作中人员离岗…

TDD-LTE TAU流程

目录 1. TAU成功流程 1.1 空闲态TAU 1.2 连接态TAU 2. TAU失败流程 当UE进入一个小区&#xff0c;该小区所属TAI不在UE保存的TAI list内时&#xff0c;UE发起正常TAU流程&#xff0c;分为IDLE和CONNECTED&#xff08;即切换时&#xff09;下。如果TAU accept分配了一个新的…

Java程序设计——GUI设计

一、目的 通过用户图形界面设计&#xff0c;掌握JavaSwing开发的基本方法。 二、实验内容与设计思想 实验内容&#xff1a; 课本验证实验&#xff1a; Example10_6 图 1 Example10_7 图 2 图 3 Example10_15 图 4 设计思想&#xff1a; ①学生信息管理系统&#xff1a…

如何在2024年编写Android应用程序

如何在2024年编写Android应用程序 本文将介绍以下内容&#xff1a; 针对性能进行优化的单活动多屏幕应用程序 &#x1f92b;&#xff08;没有片段&#xff09;。应用程序架构和模块化 → 每个层面。Jetpack Compose 导航。Firestore。应用程序架构&#xff08;模块化特征驱动…

JS 手写 new 函数

工作中我们经常会用到 new 关键字&#xff0c;new 一个构造函数生成一个实例对象&#xff0c;那么new的过程中发生了什么呢&#xff0c;我们今天梳理下 创建一个对象对象原型继承绑定函数this返回对象 先创建一个构造函数&#xff0c;原型上添加一个方法 let Foo function (n…

Python元组与字典的基础介绍

元组(tuple) 在Python中,元组是不可变的有序元素的序列 即创建后不可以被修改 创建方式val_name ([val],[val].....) #----------声明------------ tuple_1 (1,2,3) print(tuple_1)元组的运算 虽然说元组的额元素是不可以更改的,但元组之间可以使用,,*号进行运算,运算后会…

静态网页设计——宠物狗狗网(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 感谢大佬的视频&#xff1a; https://www.bilibili.com/video/BV1nk4y1X74M/?vd_source5f425e0074a7f92921f53ab87712357b 使用技术&#xff1a;HTMLCSSJS&#xff08;…

can/CANFD数据记录仪——冬标神器

冬测案例 新能源电池在冬标中要测试电池的电性能&#xff0c;热管理&#xff0c;充电&#xff0c;SOC的性能电动车的关键组之一是动力电池&#xff0c;动力电池的表现&#xff0c;除了依赖自身的材料&#xff0c;工艺等硬件素质外&#xff0c;还依赖电池管理系统的表现&#xf…

小型洗衣机哪个牌子质量好?五款内衣洗衣机便宜好用的牌子推荐

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…

Java设计模式-享元模式

目录 一、网站项目需求 二、传统方案 三、享元模式 &#xff08;一&#xff09;基本介绍 &#xff08;二&#xff09;原理类图 &#xff08;三&#xff09;内部状态和外部状态 &#xff08;四&#xff09;享元模式解决网站展现项目 &#xff08;五&#xff09;注意事项…

Linux系统安全

作为一种开放源代码的操作系统&#xff0c;linux服务器以其安全、高效和稳定的显著优势而得以广泛应用。 账号安全控制 用户账号是计算机使用者的身份凭证或标识&#xff0c;每个要访问系统资源的人&#xff0c;必须凭借其用户账号 才能进入计算机.在Linux系统中&#xff0c;提…

继电器光耦在微控制器中的应用

继电器是电子系统中的重要组件&#xff0c;用作使用低功率信号控制高功率电路的开关。继电器与微控制器的集成在各种应用中变得越来越普遍。该领域的一个重大进步是继电器光耦合器的使用&#xff0c;这是一种增强基于微控制器的系统的性能和可靠性的关键技术。 继电器光耦概述…

web3d-three.js场景设计器-TransformControls模型控制器

场景设计器-TransformControls 控制器 该控制器可以指定模型进入可控制模式-如图有三种控制方式 translate --移动模式 rotate -- 旋转模式 scale -- 缩放模式 方便布局过程中快捷对模型进行摆放操作。 引入方式 import { TransformControls } from three/examples/jsm/…

2024程序员应对挑战新方式竟然是……

2024年即将来临&#xff0c;无论2023是顺心还是不如意&#xff0c;一切都已经成为了过去式。无论在过去我们是陷入了一时的困窘&#xff0c;还是沉浸在繁花似锦的喜悦&#xff0c;我们都要保持头脑冷静&#xff0c;不被眼前迷障所困&#xff1b;我们任然要勇往直前&#xff0c;…

如何在Linux上部署1Panel面板并远程访问内网Web端管理界面

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

看CHAT如何判断php Imagick writeImages写入gif已经完毕

CHAT回复&#xff1a;Imagick::writeImages() 是同步执行的&#xff0c;也就是说这个函数会阻塞直到 GIF 文件被完全写出。所以如果这个函数没有报错并成功返回&#xff0c;那么你可以认为 GIF 文件已经被完全写出了。 如果你想要在写出 GIF 文件后立即做一些操作&#xff08;例…

使用Gitea搭建自己的git远程仓库

Gitea 为什么需要自建仓库 原因只有一个&#xff1a;折腾。其实国内的码云加上github已经足够用了。 官方原话 Gitea 的首要目标是创建一个极易安装&#xff0c;运行非常快速&#xff0c;安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言&#xff0c;这使我们…