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

stm32f103c8t6学习笔记(学习B站up江科大自化协)-USART串口-软件部分

前言:

        本文属于软件部分,具体的串口硬件部分可见http://t.csdnimg.cn/afh48,对于串口的工作原理以及各个寄存器工作流程的记录十分详细。

一、接线图

二、stm32发送-电脑串口助手接收

1.USART初始化流程图

·1.开启时钟

        把需要使用的USART和GPIO的时钟打开

·2.GPIO初始化

        把TX配置成复用输出,RX配置成输入

·3.配置USART

        直接使用一个结构体即可将所有参数配置完成

·4.开关控制

        如果需要仅发送的功能,就直接开启USART,初始化到此结束

        如果还需要接收的功能,可能还需要配置中断,那么就在开启USART之前加上IT_Config和NVIC的代码即可。

2.代码-发送字节数据

        在Serial.c部分输入以下代码,并将两个函数放到头文件声明

#include "stm32f10x.h"void Serial_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // USART1是APB2的外设RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//引脚是PA9和PA10(根据表),需开启时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;	//TX引脚是USART外设控制的输出引脚,要用复用推挽输出GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOA,&GPIO_InitStructure);	USART_InitTypeDef USART_InitStruture;USART_InitStruture.USART_BaudRate = 9600;USART_InitStruture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控,选择noneUSART_InitStruture.USART_Mode = USART_Mode_Tx;//如果既需要发送又接收就 TX | RXUSART_InitStruture.USART_Parity = USART_Parity_No;//无需校验位USART_InitStruture.USART_StopBits = USART_StopBits_1; //一位停止位USART_InitStruture.USART_WordLength = USART_WordLength_8b; //无需校验位USART_Init(USART1,&USART_InitStruture);USART_Cmd(USART1,ENABLE);
}void Serial_sendByte(uint8_t Byte)
{USART_SendData(USART1,Byte);while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}

        然后到main.c包含头文件#include "Serial.h" 。main部分的代码为

#include "stm32f10x.h"                
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"int main()
{OLED_Init();Serial_Init();Serial_sendByte(0x11);while(1){}
}

        接下来需要打开串口助手,注意串口助手内的参数要和代码一致。切记按下打开串口以及选择正确的串口号。

         在接收区将会受到11,烧录程序的时候自动复位会发送一次,后续手动按下复位键也会发送11.

3.数据模式

        

        ·HEX模式,有的地方也称为十六进制模式或二进制模式,这些称呼都是一个意思,他表示的都是以原始数据的形式显示,收到什么数据就把这个数据本身显示出来,在这种模式下,只能显示一个个的十六进制数,比如11 7A 8B 33,不能显示文本比如helloworld 和各种符号!,。等

        ·如果要显示文本就要对一个个的数据进行编码了。这就叫文本模式或字符模式,是以原始数据编码后的形式显示,在这种模式下每一个字节数据通过查找字符集编码成一个字符。图中做下角的表就是ASCII码字符集

        ·右方模式图描述的是字符和数据在发送和接收的转换关系

4.代码-发送数组

        在Serial.c部分加入一下代码,记得在Serial.h里面进行声明

void Serial_SendArray(uint8_t *Array,uint16_t Length)	//数组的传递需要指针
{uint16_t i;for(i = 0;i < Length;i++) //对数组进行遍历{Serial_sendByte(Array[i]);	//一次取出数组的每一项,通过sendbyte进行发送}
}

        同时在main函数加入以下函数,

main()
{	uint8_t MyArray[] = {0x42,0x43,0x44,0x45,0x46};Serial_SendArray(MyArray,5);while(1){}
}

        编译烧录得到以下结果

5.代码-发送字符串

        同上,在Serial.c部分加入一下代码,记得在Serial.h里面进行声明

void Serial_SendString(char *String)	//字符串自带一个结束标志位,所以不需要再传递长度参数
{uint8_t i;for(i = 0; String[i] != 0;i++)	//这里的0对应空字符,是字符串结束标志位 如果不等于0就还没结束{										//也可以写成字符的形式 '\0' Serial_sendByte(String[i]);}								
}

        在main的函数如下:


main()
{	Serial_SendString("helloword!!\r\n");	//在写完这个字符串之后,编译器会自动补上结束标志位,//所以字符串的存储空间会比字符大一//如果要执行换行操作,要使用	\r\n	两个转义字符	都是不可见的控制字符while(1){}
}

        在串口助手记得选上文本模式。第一行的数字是我选择了hex模式时出现的不正常显示的现象

6.代码-发送数字

        在Serial.c部分加入一下代码,记得在Serial.h里面进行声明

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{	//需要将Number的个位十位百位以十进制拆分开,依次变成字符数字对应的数据发送出去uint8_t i;for(i = 0;i < Length;i ++)	//参数会以十进制由高位向低位依次发送{					
//	 由于最终是以字符的形式显示,所以要根据ASCII表进行偏移 + 0x30 或 '0'Serial_sendByte(Number / Serial_Pow(10,Length - i - 1) %10 + '0');}
}

        举个例子,假设取的数字是12345,那么取万位就是12345 / 10000 % 10 =1

取千位就是 12345 / 1000 %10 = 2 取百位就是 12345 / 100 % 10 = 3,以此类推。也就是取某一位就是将数字除以10的位数次方,这是为了去掉这一位数右边的数,再对10取余,这是去掉这一位左边的数。 所以需要写一个次方函数,得到X的Y次方


uint32_t Serial_Pow(uint32_t X,uint32_t Y)	//计算数字的某一位对应的数位(百位或千位等)
{uint32_t result = 1;while(Y --){result *= X;}return result;
}

         在main函数写下如下内容


main()
{	Serial_SendNumber(12345,5);while(1){}
}

        得到运行结果如下

三、printf函数的移植 

 1、准备工作

使用printf之前需要先打开工程选项,把use microLIB选项打开。microlib是keil为嵌入式平台优化的一个精简库,本文使用到的printf将会用到这个microlib。

2、对printf进行重定向

        将printf打印的东西输出到串口,由于printf默认输出到屏幕,但是单片机没有屏幕,所以要进行重定向。

        a.在串口的c文件里加上#include<stdio.h>

        b.在后面重写fputc函数

int fputc(int ch, FILE *f)	//参数按此配置即可,
{	//将fputc重定向到串口Serial_sendByte(ch);	return ch;	
}

        c.fputc和printf之间的联系

        fputc是printf的底层,printf函数在打印的时候,就是不断调用fputc一个一个打印的。我们把fputc重定向到串口,那么printf自然就输出到串口。

        d.main函数中调用

        经过上面的步骤,printf已经移植完成。在主函数输入下面内容

#include "stm32f10x.h"                  
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"int main()
{OLED_Init();Serial_Init();printf("date:%d\r\n",20240312);while(1){}
}

        程序烧录后,单片机将会直接在串口输出        date:20240312        这行内容,并自动进行换行处理(\r\n)。

3、多串口使用printf

        使用sprintf,sprintf可以把格式化字符输出到一个字符串里面。

	char string[100];sprintf(string,"date:%d\r\n",2024031266);Serial_SendString(string);	//把字符串string通过串口发送出去//因为sprintf可以指定打印位置,不涉及重定向的东西,所以每个串口都可以使用sprintf进行格式化打印

4、封装sprintf

        由于printf这类函数比较特殊,支持可变参数。

首先在串口的头文件里添加#include<stdarg.h>,然后在最末尾处对printf函数进行封装。

void Serial_printf(char *format,...) //第一个参数用来接收格式化字符串 三个点用来接收可变参数列表
{char string[100];va_list arg;	//定义一个参数列表变量 va_list是类型名 arg是变量名va_start(arg,format);	//	从format位置开始接收参数表,放在arg里面vsprintf(string,format,arg);//这里的sprintf要改成vsprintf 前者只能接收直接写的参数 对于封装格式要用vsprintfva_end(arg);//释放参数表Serial_SendString(string);//把string发送出去
}

在main部分进行调用

Serial_printf("date:%d\r\n",20240312);

 输出结果如下

5、printf显示汉字的方法

        在keil里面我们选择的汉字编码格式是utf8,所以发送到串口的时候汉字会以utf8的方式编码。在串口助手也得选择utf8才能解码正确,为了防止写入中文的时候编译器报错,需要先在小魔术棒里面的c/c++处输入下述参数。(这个步骤是针对于使用utf8的用户)

        

--no-multibyte-chars

        由于我编译器使用的是GB2312编码,所以串口助手处要使用GBK解码。如图,正确解码得到“你好,世界”,中间的乱码部分是因为我选择了UTF8进行解码。

四、串口接收数据

        1.代码-接收字节数据

#include "stm32f10x.h"
#include <stdio.h>
#include <stdarg.h>void Serial_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // USART1是APB2的外设RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);	//引脚是PA9和PA10(根据表),需开启时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;	//TX引脚是USART外设控制的输出引脚,要用复用推挽输出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;	//RX PA10初始化GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	GPIO_Init(GPIOA,&GPIO_InitStructure);USART_InitTypeDef USART_InitStruture;USART_InitStruture.USART_BaudRate = 9600;USART_InitStruture.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控,选择noneUSART_InitStruture.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//既发送又接收USART_InitStruture.USART_Parity = USART_Parity_No;//无需校验位USART_InitStruture.USART_StopBits = USART_StopBits_1; //一位停止位USART_InitStruture.USART_WordLength = USART_WordLength_8b; //无需校验位USART_Init(USART1,&USART_InitStruture);//中断USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);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);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]);	//一次取出数组的每一项,通过sendbyte进行发送}
}void Serial_SendString(char *String)	//字符串自带一个结束标志位,所以不需要再传递长度参数
{uint8_t i;for(i = 0; String[i] != 0;i++)	//这里的0对应空字符,是字符串结束标志位 如果不等于0就还没结束{										//也可以写成字符的形式 '\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 ++)	//参数会以十进制由高位向低位依次发送{					//	 由于最终是以字符的形式显示,所以要根据ASCII表进行偏移 + 0x30 或 '0'Serial_sendByte(Number / Serial_Pow(10,Length - i - 1) %10 + '0');}
}int fputc(int ch, FILE *f)	//参数按此配置即可,
{	//将fputc重定向到串口Serial_sendByte(ch);	return ch;	
}void Serial_printf(char *format,...) //第一个参数用来接收格式化字符串 三个点用来接收可变参数列表
{char string[100];va_list arg;	//定义一个参数列表变量 va_list是类型名 arg是变量名va_start(arg,format);	//	从format位置开始接收参数表,放在arg里面vsprintf(string,format,arg);//这里的sprintf要改成vsprintf 前者只能接收直接写的参数 对于封装格式要用vsprintfva_end(arg);//释放参数表Serial_SendString(string);//把string发送出去
}

        这部分代码和串口发送数据部分没什么本质区别,仅仅添加了开启引脚PA10,以及或上了RX部分。

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

提个醒,自己的stm32接收不到数据,但是程序没有任何问题,接线也正常的情况下,应该是stm32内部的ttl和串口转ttl模块冲突了,可参考http://t.csdnimg.cn/q08TD这篇文章。由于我也出现了这种情况,所以暂时没有实验现象。

2.代码-中断接收字节数据并回传

在serial.c里面添加两个定义

uint8_t Serial_RXData;
uint8_t Serial_RXFlag;

并在末尾部分添加如下代码

//实现读后自动清除的功能
//意思是返回这个标志位,1就返回1,0就返回0,但给这个1复位一下,使得查一次就能复位1次
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_GetITStatus(USART1,USART_IT_RXNE) == SET){Serial_RXData = USART_ReceiveData(USART1);Serial_RXFlag = 1;USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}

接下来在main部分函数添加

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"uint8_t RXData;int main()
{OLED_Init();Serial_Init();OLED_ShowString(1,1,"RxData:");while(1){if(Serial_GetRxFlag() == 1){RXData = Serial_GetRxData();Serial_sendByte(RXData);OLED_ShowHexNum(1,8,RXData,2);}}
}

烧录后,在发送区输入数据后,将会显示到单片机连接的OLED显示屏上边,同时会返回一份数据到接收区

 五、数据包的定义  

        ·串口数据包:通常使用的是额外添加包头包尾的这种方式

        ·在HEX数据包里面,数据都是以原始的字节数据本身呈现的,而在文本数据包里面,每个字节就经过了一层编码和译码,最终表现出来的就是文本格式,但是实际上每个文本字节的背后都还是一个HEX数据。

        ·优缺点:

        HEX数据包优点:传输最直接,解析数据非常简单,比较适合一些模块发送原始的数据

                          缺点:灵活性不足,容易和包头包尾重复,

        文本数据包优点:数据直观易理解,非常灵活,比较适合一些输入指令进行人机交互的场合,比如蓝牙模块常用的AT指令,CNC和3D打印机常用的G代码,都是文本数据包的格式。

                          缺点:解析效率低,比如发送一个100,HEX数据包就是一个字节100,但是文本数据包就得是三个字节的字符‘1’‘0’‘0’,收到之后还要把字符转化成数据,才能得到100,

串口收发hex数据包

        ·固定包长:含包头包尾,每个数据包的长度都固定不变,数据包前面是包头,后面是包尾
        ·可变包长:含包头包尾,每一个数据包的长度可以是不一样的,前面是包头,后面是包尾。他的数据包格式可以根据用户需求自己规定。

        ·包头包尾和载荷重复解决办法:

        ·如果数据含有FF和FE,和包头包尾重复了怎么办?会引起误判,对于这个问题也有相应的几种解决方法:

        1.限制在和数据的范围,如果可以的话,可以在发送的时候对数据进行限幅。比如X、Y、Z三个数据的范围是0-100,那么可以在载荷中只发送0-100的数据,以此防止和包头包尾重复。

        2.如果无法避免载荷数据和包头包尾重复,则尽量使用固定长度的数据包,由于载荷数据是固定的,只要通过包头包尾对齐了数据,我们就可以严格知道,那个数据是包头包尾,哪个数据是载荷数据。

        在接收载荷数据的时候,我们并不会判断他是不是包头包尾,但是在判断包头包尾的时候会判断他是不是确实是包头包尾,用于数据对齐,在经过几个数据包对齐之后,剩下的数据包就不会出现问题了。

        3.增加包头包尾的数量,并且尽量让他呈现载荷数据出现不了的状态。比如我们使用FF、FE作为包头,FD、FC作为包尾,这样也可以避免包头包尾和载荷数据重复的情况发生。

        ·并不是所有的包头包尾都需要,可以只要一个包头,把包尾删掉,这样数据包的格式就是一个包头FF加四个数据。当监测到FF开始接收,当收够四个字节后置一个标志位,一个数据包接收完成。不过这样会加重载荷和包头重复的问题。最坏的情况下载荷全是FF,包头也是FF。如果加上了包尾FE,无论数据怎么变化都是可以分辨出包头包尾的。

        ·固定包长和可变包长的选择:

        对于HEX来说,如果载荷会出现包头和包尾重复的情况,最好是选择固定包长,以避免接收错误。如果重复还选择可变包长,数据容易乱套。如果包头包尾不会和载荷重复,可以选择可变包长

        ·关于各种数据转换为字节流的问题。

        数据包都一个字节一个字节组成的,如果想发送16、32位的整型数据,float、double甚至是结构体都没问题,因为内部是由一个字节一个字节组成的,仅需要用一个uint8_t的指针指向他,并把他们当做一个字节数组发送即可。

串口收发文本数据包

        ·由于数据译码成了字符形式,这样就会存在大量的字符可作为包头包尾,可以有效的避免载荷和包头包尾重复的问题。比如以@ 作为包头,以\r\n这两个换行字符作为包尾,在载荷数据中间可以出现除了包头包尾的任意字符。文本数据包基本不用担心包头包尾和载荷重复的问题,可变包长、各种符号、字母、数据都可以随意使用

        ·当接收到载荷数据之后,得到的就是一个字符串,在软件中对字符串进行操作和判断,就可以实现各种指令控制的功能,而且字符串数据包表达的意义很明显,可以把字符串数据包直接打印到串口助手上,各种指令和数据都可以一眼看清。

        ·文本数据包通常会以换行作为包尾,在打印的时候就可以一行一行显示,比较方便。

六、数据包的收发流程

HEX数据包接收(固定包长)

        在接收的时候,每收到一个字节,程序都会进一遍中断,在中断函数里面可以拿到这一个字节,在拿到数据之后就得退出中断了,每拿到一个数据都是一个独立的过程。对于数据包来说很明显有一个前后关联性,包头之后是数据,数据之后是包尾。对于包头数据包尾这三种不同的状态,我们需要有不同的处理逻辑,在程序中需设计一个记住不同状态的机制,在不同状态执行不同操作,同时还要进行状态的合理转移,这种程序设计的思想叫做状态机,接下来将使用状态机的方法来接收一个数据包。

执行流程:

        最开是S = 0,收到一个数据进中断,根据S = 0进第一个状态的程序,判断包头是不是FF,如果是代表收到包头,之后置S = 1,退出中断,结束。这样一来下次再进中断,S = 1就可以进行接收数据的程序了。

        在第一个状态如果收到的不是FF,证明数据包没有对齐,我们应该等待数据包包头的出现,这是状态仍然是0,下次进中断还是判断包头的逻辑,直到出现FF才能转入下一个状态,进入下一个状态时收到数据将存入数组中,另外再用一个变量记录接收数据的个数,如果没接收够4个数据就一直处于接收状态,如果收够了就置S = 2。下次进中断就进入下一个状态。

        最后的状态是等待包尾,判断数据是不是FE,如果是的话就可以置S = 0,回到最初的状态,开始下一个轮回。这个数据也可能不是FE,比如数据和包头重复,导致包头位置判断错误,那么这个包尾位置有可能不是FE,这时进入重复等待包尾的状态,直到接收到真正的包尾。这样的判断更能预防因数据和包头重复造成的错误。

使用状态机的基本步骤:

        1.先根据项目要求定义状态,画几个圈,然后考虑好各个状态在什么情况下会进行转移,如何转移,画好线和转移条件,最后根据图编程。比如做个菜单就可以用到状态机的思维,按什么键切换什么菜单,执行什么程序,还有一些芯片内部逻辑也会用到状态机,比如什么情况下进入待机状态,什么情况进入工作状态。

文本数据包接收:

接收流程:

        第一个状态,等待包头,判断是不是我们规定的@符号,如果收到@就进入接收状态,在这个状态下依次接受数据,同时这个状态还应该要兼具等待接收包尾的功能。因为这个是可变包长,接收数据的同时要时刻监视,是否收到了包尾,一旦收到了包尾就立刻结束。这个状态的逻辑就是判断接收的一个数据是不是\r,如果不是就正常接收,如果是则不接收,同时跳到下一个状态,等待包尾\n,因为数据包有两个包尾\r\n,所以需要第三个状态,如果只有一个包尾,那么在出现一个包尾之后就可以直接回到初始状态,即只需要两个状态即可。因为接收数据和等待包尾需要在一个状态里同时进行,由于串口的包头包尾不会出现在数据中,所以基本不会出现数据错位的现象

相关文章:

  • IBM DataStage服务的启动和停止
  • k8s编排系统
  • SQLiteC/C++接口详细介绍之sqlite3类(十三)
  • 用云服务器构建gpt和stable-diffusion大模型
  • 3D Occupancy 预测冠军方案:FB-OCC
  • Oracle SQL优化基本概念:直方图
  • 计算机网络——物理层(数据交换方式)
  • Task-balanced distillation for object detection用于
  • 编译原理-实现识别标识符的词法分析器——沐雨先生
  • ARM 汇编指令:(七) STM/LDM多寄存器加载/多存储指令
  • Python的Selenium库中的模块、类和异常的汇总
  • react可视化编辑器 第一章 拖拽
  • C语言如何进⾏字符数组的连接?
  • 2023年中国抗DDoS市场规模现状及竞争格局,公有云抗DDoS是主要增长点
  • 数学建模--MATLAB基本使用
  • Java Agent 学习笔记
  • JavaScript设计模式与开发实践系列之策略模式
  • Java知识点总结(JDBC-连接步骤及CRUD)
  • Kibana配置logstash,报表一体化
  • Otto开发初探——微服务依赖管理新利器
  • scala基础语法(二)
  • storm drpc实例
  • Swoft 源码剖析 - 代码自动更新机制
  • - 概述 - 《设计模式(极简c++版)》
  • 基于Android乐音识别(2)
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 七牛云假注销小指南
  • 如何用vue打造一个移动端音乐播放器
  • 山寨一个 Promise
  • ​猴子吃桃问题:每天都吃了前一天剩下的一半多一个。
  • #QT项目实战(天气预报)
  • #前后端分离# 头条发布系统
  • (二)Linux——Linux常用指令
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (收藏)Git和Repo扫盲——如何取得Android源代码
  • (四)docker:为mysql和java jar运行环境创建同一网络,容器互联
  • (四)汇编语言——简单程序
  • (转)scrum常见工具列表
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • .NET : 在VS2008中计算代码度量值
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .Net Web项目创建比较不错的参考文章
  • .NET 命令行参数包含应用程序路径吗?
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)...
  • .net流程开发平台的一些难点(1)
  • .sdf和.msp文件读取
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • @PreAuthorize注解
  • [ vulhub漏洞复现篇 ] JBOSS AS 4.x以下反序列化远程代码执行漏洞CVE-2017-7504
  • []我的函数库
  • [1525]字符统计2 (哈希)SDUT
  • [2023-年度总结]凡是过往,皆为序章
  • [Android实例] 保持屏幕长亮的两种方法 [转]
  • [CareerCup] 13.1 Print Last K Lines 打印最后K行