收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
char text[30];
sprintf(text," 1 ");
LCD_DisplayStringLine(Line3,(uint8_t*)text);
}
}
在setup中初始化,清屏,设置背景色,设置文本色。显示背景色和前景色就是backcolor和textcolor,用LCD\_SetBackColor()LCD\_SetTextColor();来实现。选中括号中的颜色后,比如设置的是Black,右键选择Go to Definition Of ‘Black’,能看到定义的其他颜色可填。
LCD_Init();
LCD_Clear(Black);
LCD_SetBackColor(Black);
LCD_SetTextColor(White);
在官方例程里面main.c就是官方的写法例子,在LCD\_Init()后就是LCD使用的具体语法。
定义一个数组紧接着写中括号并且加上数字表示数组长度,sprintf(第一个参数text就是定义的数组,”第二个参数写自己想要打印的内容”),打印的内容后面可跟一个数据,则需要加第三个参数并且指名所跟数据的类型和名称,使用sprintf函数显示百分号%时输入%%即可。如
char text[30];
sprintf(text," PA1 “);
LCD_DisplayStringLine(Line3,(uint8_t*)text);
sprintf(text,” F:%dHz “,pa1_frq);
LCD_DisplayStringLine(Line4,(uint8_t*)text);
sprintf(text,” D:%d%% ",pa1_duty);
LCD_DisplayStringLine(Line5,(uint8_t*)text);
能选择的行数Line为0到9一共十行。
### 高亮显示
如果需要高亮显示,在写sprintf和LCD\_DisplayStringLine之前写一下需要高亮的颜色的背景色设置语句,以设置line0为黄色高亮显示为例,其他行不变。
LCD_SetBackColor(Yellow);
Sprintf(text,“numbe”,1);
LCD_DisplayStringLine(Line0,(uint8_t *)text,);
LCD_SetBackColor(Black);
Sprintf(text,“numbe”,1);
LCD_DisplayStringLine(Line0,(uint8_t *)text,);
### lcd翻转显示
在所给的lcd液晶控制器资料中手册中,搜索diretion能够找到对于显示方向的描述SS位控制横向扫描方向,GS位控制纵向扫描方向,搜索GS和SS,能看到分别所在的寄存器位置
搜索60h和01h,能找到GS和SS设置。这里的60和01都是16进制,对应1和96号寄存器。
lcd.c中将函数void REG\_932X\_Init(void)复制粘贴到下方,更名为void REG\_932X\_Init1(void),其中1和96号寄存器对应的SS和GS的值更改即可,此时看到代码注释给了需要更改的值,更改即可。


void REG_932X_Init1(void)
{
LCD_WriteReg(R227, 0x3008); // Set internal timing
LCD_WriteReg(R231, 0x0012); // Set internal timing
LCD_WriteReg(R239, 0x1231); // Set internal timing
LCD_WriteReg(R1, 0x0100); // set SS and SM bit //0x0100
LCD_WriteReg(R2, 0x0700); // set 1 line inversion
LCD_WriteReg(R3, 0x1030); // set GRAM write direction and BGR=1.
LCD_WriteReg(R4, 0x0000); // Resize register
LCD_WriteReg(R8, 0x0207); // set the back porch and front porch
LCD_WriteReg(R9, 0x0000); // set non-display area refresh cycle ISC[3:0]
LCD_WriteReg(R10, 0x0000); // FMARK function
LCD_WriteReg(R12, 0x0000); // RGB interface setting
LCD_WriteReg(R13, 0x0000); // Frame marker Position
LCD_WriteReg(R15, 0x0000); // RGB interface polarity
/**************Power On sequence ***************/
LCD_WriteReg(R16, 0x0000); // SAP, BT[3:0], AP, DSTB, SLP, STB
LCD_WriteReg(R17, 0x0007); // DC1[2:0], DC0[2:0], VC[2:0]
LCD_WriteReg(R18, 0x0000); // VREG1OUT voltage
LCD_WriteReg(R19, 0x0000); // VDV[4:0] for VCOM amplitude
// Delay_Ms(200); // Delay 200 MS , Dis-charge capacitor power voltage
HAL_Delay(200);
LCD_WriteReg(R16, 0x1690); // SAP, BT[3:0], AP, DSTB, SLP, STB
LCD_WriteReg(R17, 0x0227); // R11H=0x0221 at VCI=3.3V, DC1[2:0], DC0[2:0], VC[2:0]
// Delay_Ms(50); // Delay 50ms
HAL_Delay(50);
LCD_WriteReg(R18, 0x001D); // External reference voltage= Vci;
// Delay_Ms(50); // Delay 50ms
HAL_Delay(50);
LCD_WriteReg(R19, 0x0800); // R13H=1D00 when R12H=009D;VDV[4:0] for VCOM amplitude
LCD_WriteReg(R41, 0x0014); // R29H=0013 when R12H=009D;VCM[5:0] for VCOMH
LCD_WriteReg(R43, 0x000B); // Frame Rate = 96Hz
// Delay_Ms(50); // Delay 50ms
HAL_Delay(50);
LCD_WriteReg(R32, 0x0000); // GRAM horizontal Address
LCD_WriteReg(R33, 0x0000); // GRAM Vertical Address
/ ----------- Adjust the Gamma Curve ---------- /
LCD_WriteReg(R48, 0x0007);
LCD_WriteReg(R49, 0x0707);
LCD_WriteReg(R50, 0x0006);
LCD_WriteReg(R53, 0x0704);
LCD_WriteReg(R54, 0x1F04);
LCD_WriteReg(R55, 0x0004);
LCD_WriteReg(R56, 0x0000);
LCD_WriteReg(R57, 0x0706);
LCD_WriteReg(R60, 0x0701);
LCD_WriteReg(R61, 0x000F);
/ ------------------ Set GRAM area --------------- /
LCD_WriteReg(R80, 0x0000); // Horizontal GRAM Start Address
LCD_WriteReg(R81, 0x00EF); // Horizontal GRAM End Address
LCD_WriteReg(R82, 0x0000); // Vertical GRAM Start Address
LCD_WriteReg(R83, 0x013F); // Vertical GRAM Start Address
LCD_WriteReg(R96, 0xA700); // Gate Scan Line 0xA700
LCD_WriteReg(R97, 0x0001); // NDL,VLE, REV
LCD_WriteReg(R106, 0x0000); // set scrolling line
/ -------------- Partial Display Control --------- /
LCD_WriteReg(R128, 0x0000);
LCD_WriteReg(R129, 0x0000);
LCD_WriteReg(R130, 0x0000);
LCD_WriteReg(R131, 0x0000);
LCD_WriteReg(R132, 0x0000);
LCD_WriteReg(R133, 0x0000);
/ -------------- Panel Control ------------------- /
LCD_WriteReg(R144, 0x0010);
LCD_WriteReg(R146, 0x0000);
LCD_WriteReg(R147, 0x0003);
LCD_WriteReg(R149, 0x0110);
LCD_WriteReg(R151, 0x0000);
LCD_WriteReg(R152, 0x0000);
/ Set GRAM write direction and BGR = 1 /
/ I/D=01 (Horizontal : increment, Vertical : decrement) /
/ AM=1 (address is updated in vertical writing direction) */
LCD_WriteReg(R3, 0x01018); //0x1018
LCD_WriteReg(R7, 0x0173); // 262K color and display ON
}
随后在lcd.h中将新的函数声明。
void REG_932X_Init1(void);
通过搜索发现此函数是在lcd初始化函数中调用的,所以在需要反转显示的位置前调用LCD\_Clear(Black)清屏后调用新的修改后的函数void REG\_932X\_Init1(void)。
## PWM
以PA6和PA7为例,引脚配置为CH1通道,CH1N为互补PWM波,不选,选择定时器和对应的CH1的模式,以TIM16为例,TIM17同理。预分频(psc):CPU运行频率先经过它分频再进入计时器,如CPU运行在 x Mhz 下,预分频为 y,那进入计时器的频率也就为 x/(y+1) Mhz(因为从0计数,所以是y+1)。自动重装值(arr):指一次周期的计数长度。脉冲长度(pulse):指输出脉冲的计数长度。
频率=外部总线频率/(PSC+1)/(ARR+1),占空比为pulse/(ARR+1),PSC,ARR,pulse在代码中可以重新设置和调整:
/*
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,180); //修改CCR寄存器
__HAL_TIM_SET_AUTORELOAD(&htim2,1440); //修改ARR寄存器,没有通道限制,整个定时器一起改
__HAL_TIM_SET_PRESCALER(TIM_TypeDef* TIMx, uint32_t Prescaler);//修改预分频系数
举一个例子:
__HAL_TIM_SET_PRESCALER(&htim2, 1000);
这条语句就是把定时器二的预分频系数设为1000
*/

my\_main.h代码
void pwm_proce(void);
my\_main.c代码
int pa6_duty=200;
int pa7_duty=100;
void pwm_proce(void)
{
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,pa6_duty);
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,pa7_duty);
}
setup中开启
HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_1);
## 输入捕获频率和占空比
### 可用CH1和CH2通道的引脚(主要)
通过跳线帽连接到PA15和PB4引脚,选择CH1通道,只有ch1和ch2可以从模式清除,这里选择了TIM2和TIM3的CH1。

TI1FP1对上升沿敏感,TI1FP2对下降沿敏感,CNT计数器在80MHZ经过分频后的频率下进行计数(比如分频80,则一微秒计时一次),而ccr1和ccr2则会在上升和下降沿来临时记录下cnt的值,cnt通过从模式在上升沿来临时,ccr1存下数据后归位。由此ccr1就是一个周期的计数,ccr2就是高电平时间的计数。**输入到从模式控制器的信号只能是TI1FP1和TI1FP2,所以只能用CH1和CH2通道**
tim2配置,tim3同理,channel1和2的直接模式和间接模式:所用的引脚直接相连的通道号为直接模式,如果引脚是channel2,则channel2为直接模式,否则间接模式占用直接相连的通道,直接模式会另开一个引脚

.h
void in_pro(void);
setup中开启IC输入捕获(频率和占空比分别是上升和下降的通道,防止开错或者忘开,都开启)
HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim2,TIM_CHANNEL_2);
HAL_TIM_IC_Start(&htim3,TIM_CHANNEL_1);
HAL_TIM_IC_Start(&htim3,TIM_CHANNEL_2);
.c
float frq1=0,frq2=0;
float duty1=0,duty2=0;
void in_pro(void)
{
frq1=1000000.0f/(HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1)+1);
duty1=(HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_2)+1)*100.0f/(HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1)+1);
frq2=1000000.0f/(HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1)+1);
duty2=(HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_2)+1)*100.0f/(HAL_TIM_ReadCapturedValue(&htim3,TIM_CHANNEL_1)+1);
}
### 不可用CH1和CH2的引脚(备用)
和能使用从模式清零的方式相比,此方式通用性更好,有的引脚不能使用CH1和CH2,区别在于
1.需首先开启的输入捕获是在中断方式下,函数多了"IT"。
2.判断中断的计时器号,然后判断htim->Channel==HAL\_TIM\_ACTIVE\_CHANNEL\_1
3.此方式在读取两个通道的寄存器的值后需要手动归零计时器并且重新打开输入捕获的中断模式。Clock Source选择Integral Clock,Channel1选择 Input Capture direct mode
对方波的一个周期的上升沿时间计数,frq=(80000000/80)/得到的时间值。测量占空比的原理:高电平的时间比上一个周期的时间。在定时器的另外一个通道,配置成间接模式Input Capture indirect mode,直接模式去测上升沿,间接模式去测下降沿。Polarity Selection一个是Rinsing Edge,另一个是falling Edge。打开中断使能。
剩下一个定时器TIM3同理。
setup初始化的地方把定时器中断打开。
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
my\_main.h
//
my\_main.c
double val1a=0,val2a=0;
uint val1b=0,val2b=0;
uint frq1=0,frq2=0;
float duty1=0,duty2=0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->InstanceTIM2)
{
if(htim->ChannelHAL_TIM_ACTIVE_CHANNEL_1)
{
val1a=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
val1b=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
__HAL_TIM_SetCounter(htim,0);
frq1=(80000000/80)/val1a;
duty1=(val1b/val1a)*100;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
if(htim->Instance==TIM3)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)
{
val2a=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
val2b=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
__HAL_TIM_SetCounter(htim,0);
frq2=(80000000/80)/val2a;
duty2=(val2b/val2a)*100;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
}
`HAL\_TIM\_IC\_Start`: 这个函数用于启动定时器的输入捕获模式,但不启用中断。当只需要获取输入捕获的值而不需要中断处理时,可以使用这个函数。
`HAL\_TIM\_IC\_Start\_IT`: 这个函数不仅启动了定时器的输入捕获模式,还启用了中断。这样,当输入捕获事件发生时,会触发中断,并调用相应的中断处理函数。这个函数适用于需要在输入捕获事件发生时执行某些特定操作的情况。
如果中断回调函数太长记不住也可以在如下文件的后部分找到
## ADC\_DMA

### 单通道
用到接口PB15和PB12选择ADC IN,左边Analog里面找到PB15用到的ADC1,ADC1里面找到用到的IN 11,选择single-ended,ADC2中的IN15同理。采样时间增大对于稳定结果的作用有限,这里采用一个过采样。
.h文件代码
#include “adc.h”
float GETADC_value(ADC_HandleTypeDef *ADCx);
setup校准ADC1
HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);
.c文件代码
float GETADC_value(ADC_HandleTypeDef ADCx)
{
float prpt;
HAL_ADC_Start(ADCx);
prpt=HAL_ADC_GetValue(ADCx);
return prpt3.3f/65536.0f;
}
一般要求浮点保留两位小数,在LCD显示时形式为 V:=%.2f V:=后面要跟数字,数字是浮点型保留两位小数
定义函数时要写函数返回值类型,后面形参括号里面写形参或者void,主函数调用时就只用写函数名加分号。
获取值代码
ADC_value=GETADC_value(&hadcx);//传参数例如&hadc1给上面的ADCx
### DMA(建议)
使用PB12和PB14演示,PB15已经用以上单通道演示过。
使能引脚单端触发,添加DMA设置为扫描模式。参数设置中通道数设置为2,使能扫描模式、连续请求模式、DMA连续请求。使能定时请求,通道采样时间尽可能大,否则占用过多CPU程序可能卡死。

开头设置缓冲区
uint16_t adc_buf[2];
setup中校准和开启DMA
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1,(uint32_t *)adc_buf,2);
数据存在了缓冲区adc\_buf[0]和adc\_buf[1],可以随时使用,例如显示在屏幕上 ,但是要经过一下处理,例如保留三位小数adc\_buf[0]\*3.3f/4096.0f,adc\_buf[1]\*3.3f/4096.0f
char text[30];
sprintf(text,“ADC1:%.3f,2:%.3f”,adc_buf[0]*3.3f/4096.0f,adc_buf[1]*3.3f/4096.0f);
LCD_DisplayStringLine(Line0,(uint8_t *)text);
## USART
### 空闲中断回调函数(简单)
接受时所使用的空闲中断回调函数是只有在V1.4.0及其之后的版本固件包才有,没有此函数需要用中断回调函数,会麻烦的多,但是可以实现,见后文。
关于格式化输入输出:
例:sscanf(rxdata,"%c",&uart\_flag[3]);中的%c是char类型,也可以转化为string类型,需要写为%xs,x为数字,是要转化的长度,例如
sscanf(rxdata,“%4s:%4s:%12s”,car_type,car_data,car_time);
在这个sscanf语句中,冒号在格式字符串中的作用是用于匹配输入字符串中的冒号字符。这表示在rxdata中,字符串应该以冒号分隔,并且sscanf将会按照这种格式解析输入字符串。
具体来说:
“%4s” 表示读取最多4个非空格字符的字符串,这里用于匹配 car\_type。“:” 表示在输入字符串中匹配冒号字符。
“%4s” 再次表示读取最多4个非空格字符的字符串,用于匹配 car\_data。“:” 再次表示匹配冒号。
“%12s” 表示读取最多12个非空格字符的字符串,用于匹配 car\_time。
这样的格式说明符确保了输入字符串的特定格式,其中每个字符串之间由冒号分隔。如果rxdata不符合这种格式,sscanf可能无法正确解析字符串,或者解析结果可能不符合预期。
C语言中,& 操作符用于获取变量的地址。在 sscanf 函数中,如果你想将读取到的值存储到变量中,需要传递该变量的地址。如果不使用 &,则传递的是变量的值,而不是地址。
对于数组来说,数组名本身就是数组的地址。所以,当你传递数组名时,不需要使用 &。但如果你传递数组的某个元素(如字符或者整型数),那么需要使用 & 来获取该元素的地址。如下:
char usart_flag[5];
// 不使用&,传递的是数组的地址
sscanf(rxdata, “%c”, usart_flag);
// 使用&,传递的是数组的第一个元素的地址
sscanf(rxdata, “%c”, &usart_flag[0]);
Cubemx配置,在通信位置connectivity选择uart1,mode改成第二项异步模式Asynchronous。没有配置过LCD的话会默认使用PC4和PC5,需要手动改成PA10和PA9。Baud rate波特率一般使用9600,nvic setting中断打开。
空闲中断回调函数位置,建议复制粘贴
 setup
HAL_UARTEx_ReceiveToIdle_IT(&huart1,(uint8_t *)uart_rx,50);
开启中断只能使用一次,所以接收函数最后还要再开启一次
.h
#include “usart.h”
#include “string.h”
void uart_tx_proce(void);
.c(以接收到四位字符串为例)
char uart_tx[50];
char uart_rx[50];
char text1 [4]=“0000”;
char text[30];
void uart_tx_proce(void)
{
sprintf(uart_tx,“tx”);
HAL_UART_Transmit(&huart1,(uint8_t *)uart_tx,strlen(uart_tx),50);
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
sscanf(uart_rx,“%3s”,text1);
sprintf(text,“text1:%s”,text1);
LCD_DisplayStringLine(Line1,(uint8_t *)text);
HAL_UARTEx_ReceiveToIdle_IT(&huart1,(uint8_t *)uart_rx,50);
}
### 另一种中断回调函数方法(应该用不到)
.c文件中利用中断回调函数进行接收,然后进行处理(示例)及发送写法
char rxdata[30];
uint8_t rxdat;
uchar rx_pointer;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef huart)//接收中断
{
rxdata[rx_pointer++]=rxdat;
HAL_UART_Receive(&huart1,&rxdat,1,10);
}
//char uart_data[3];
void uart_rx_proce()//数据接收处理程序
{
if(rx_pointer>0)//说明有数据被接收,进行处理
{ /
if(rx_pointer=1) 恰好接收一位数据
sscanf(rxdata,“%c”,&uart_data[3]); 格式化处理接收到的数据
else
{
char temp[20]; 发送示例
sprintf(temp,"Error"); 发送示例
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50); 发送示例
}*/
rx_pointer=0;memset(rxdata,0,30);//清零
}
}
在 HAL\_UART\_RxCpltCallback 回调函数中,HAL\_UART\_Receive\_IT 函数首先被调用,它开始等待接收一个字节的数据。然后,rxdata[rx\_pointer++]=rxdat; 语句被执行,将接收到的数据存储在 rxdata 数组中,并且 rx\_pointer 递增。因此,rx\_pointer 表示已经接收到的数据数量。虽然 rxdata[rx\_pointer++]=rxdat; 在 HAL\_UART\_Receive\_IT 函数之后,但由于这是在回调函数中,它们实际上是异步执行的。即,当有数据可用时,回调函数被触发,首先会启动下一次接收,然后处理已接收的数据。所以,rxdata[rx\_pointer++]=rxdat; 的执行确实发生在HAL\_UART\_Receive\_IT(&huart1,&rxdat,1); 之后。
在C语言中,`uint8\_t`是一个无符号的8位整数类型,因此可以用来接收字符。字符在C语言中实际上是以ASCII码的形式存储的,而ASCII码的取值范围是0到255,恰好与`uint8\_t`类型的取值范围一致。因此,可以使用`uint8\_t`类型来接收字符。
main.c中进行初始化
extern uint8_t rxdat;
HAL_UART_Receive_IT(&huart1,&rxdat,1);
## SYSTICK
只需要知道SysTick\_Handler(void)是一毫秒进入一次的函数,至于上面编写何种功能的函数,然后放在SysTick\_Handler(void)里面从而实现何种功能就根据实际而来,这里的是0.1s反转flag的函数。
//stm32Gxx_it.c里面
//------------------------------------------
int flag=0;
int count=0;
void sys_tim(int time_set)//time_set放在一毫秒进入一次的中断SysTick_Handler(void)里面,所以单位是毫秒
{
count++;
if(count==time_set)
{
flag=!flag;
count=0;
}
}
//------------------------------------------------
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 /
HAL_IncTick();
/ USER CODE BEGIN SysTick_IRQn 1 /
sys_tim(100);//可以改变为其他时间
/ USER CODE END SysTick_IRQn 1 */
}
//控制LED可在其他地方引用flag
还可以用另一种由系统设置好的变量来操作,原理一样,每1ms执行一次HAL\_InTick函数
HAL\_InTick的具体作用是自增1
在程序初始化位置定义计时节点
uint32_t 500ms;
500ms=uwTick;
在主程序中书写需要定时的操作,类似地,还可以同时存在其他时长的定时程序,但是此方法精确度不太高。
if(uwTick-500ms>500)
{
//此处写需要具体执行的操作
500ms=uwTick;
}
## RTC实时时钟
以秒为单位进行计时,相当于钟表,配置时最下方year会自动补齐20xx,所以20不用写
.h
#include “rtc.h”
如果只读取时间,也需要把日期读一下,只有日期读完后,数据才会从影子寄存器读取。
.c,sprintf格式化部分多打几个空格清屏防止残留。


**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**
书写需要定时的操作,类似地,还可以同时存在其他时长的定时程序,但是此方法精确度不太高。
if(uwTick-500ms>500)
{
//此处写需要具体执行的操作
500ms=uwTick;
}
## RTC实时时钟
以秒为单位进行计时,相当于钟表,配置时最下方year会自动补齐20xx,所以20不用写
.h
#include “rtc.h”
如果只读取时间,也需要把日期读一下,只有日期读完后,数据才会从影子寄存器读取。
.c,sprintf格式化部分多打几个空格清屏防止残留。
[外链图片转存中...(img-6dB8ikwl-1715638316421)]
[外链图片转存中...(img-P1ceORnv-1715638316422)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**