定时器模块介绍(timer.c):
也是用到了两个定时器,定时器二和定时器三。定时器二用来按键扫描。定时器三用来作为本地是时钟源,运行解析出来的时间。
贴一下定时器的初始化代码,STM32的定时器模块还是比较复杂的,不懂的自己去学习一下。
//==================================================================
//函 数 名:TIM3_Int_Init(u32 arr,u16 psc)
//功能描述:定时器3初始化函数
//输入参数:arr-自动重装载值 pre-预分频值
//定时长度:72000000/(arr+1)*(pre+1)
//==================================================================
void TIM3_Int_Init(u32 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig( TIM3,TIM_IT_Update,ENABLE); //使能或者失能指定的TIM中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
//==================================================================
//函 数 名:Timer2_Init(void)
//功能描述:定时器2初始化函数
//输入参数:无
//==================================================================
void Timer2_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 999;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
时间解析模块介绍(time.c):
时间戳转换模块是我在网上摘抄下来的,我看csdn平台也有人写相关的,但我看的文章不是csdn的。但现在找关键词找不到了,这里就直接引用了。感谢原作者!
#include <stdio.h>
#include "OLED.h"
#include "timer.h"
#include "time.h"
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
//全局变量,存储时间
int year_1,month_1,day_1,hour_1,minter_1,sec_1,week_1;
//暂时没用的,有时间改一下函数结构,去冗余
const uint16_t month_days_table[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
/**
* @brief 判断是否为闰年 注:原文的链接忘记复制了,感谢该作者
* @param [in] year 年
* @retval 1 为闰年
* @retval 0 为平年
*/
uint16_t fml_leap_year(uint16_t year)
{
return (((year % 4 == 0)&&(year % 100 != 0)) || (year % 400 == 0));
}
/**
* @brief 日期转时间戳
* @param [in] date 日期值
* @retval =0 成功
* @retval 非0 失败,以及失败原因
*/
uint32_t fml_time_to_stamp(date_time_t date)
{
static uint32_t dax = 0;
static uint32_t day_count = 0;
uint16_t leap_year_count = 0;
uint16_t i;
// 计算闰年数
for (i = 1970; i < date.year; i++)
{
if (fml_leap_year(i))
{
leap_year_count++;
}
}
// 计算年的总天数
day_count = leap_year_count * 366 + (date.year - 1970 - leap_year_count) * 365;
// 累加计算当年所有月的天数
for (i = 1; i < date.month; i++)
{
if ((2 == i) && (fml_leap_year(date.year)))
{
day_count += 29;
}
else
{
day_count += month_days_table[i];
}
}
// 累加计算当月的天数
day_count += (date.day - 1);
dax = (uint32_t)(day_count * 86400) + (uint32_t)((uint32_t)date.hour * 3600) + (uint32_t)((uint32_t)date.min * 60) + (uint32_t)date.sec;
/* 北京时间补偿 */
dax = dax - 8*60*60;
return dax;
}
//==================================================================
//函 数 名:fml_stamp_to_time;
//功能描述:时间戳转换年月日
//输入参数:无
//公式来源:蔡勒公式
//==================================================================
uint32_t fml_stamp_to_time( uint32_t timep, date_time_t *date)
{
uint32_t days = 0;
uint32_t rem = 0;
/* 北京时间补偿 */
timep = timep + 8*60*60;
// 计算天数
days = (uint32_t)(timep / 86400);
rem = (uint32_t)(timep % 86400);
// 计算年份
uint16_t year;
for (year = 1970; ; ++year)
{
uint16_t leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
uint16_t ydays = leap ? 366 : 365;
if (days < ydays)
{
break;
}
days -= ydays;
}
date->year = year;
// 计算月份
static const uint16_t days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
uint16_t month;
for (month = 0; month < 12; month++)
{
uint16_t mdays = days_in_month[month];
if (month == 1 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
{
mdays = 29;
}
if (days < mdays)
{
break;
}
days -= mdays;
}
date->month = month;
date->month += 1;
// 计算日期
date->day = days + 1;
// 计算时间
date->hour = rem / 3600;
rem %= 3600;
date->min = rem / 60;
date->sec = rem % 60;
year_1 = data1.year;
month_1 = data1.month;
day_1 = data1.day;
hour_1 = data1.hour;
minter_1 = data1.min;
sec_1 = data1.min;
return 0;
}
//==================================================================
//函 数 名:get_week();
//功能描述:计算出星期
//输入参数:无
//公式来源:蔡勒公式
//==================================================================
void get_week(void)
{
if (month_1 == 1 || month_1 == 2)//判断month是否为1或2
year_1--, month_1 += 12;
int c = year_1 / 100;
int y = year_1 - c * 100;
int week = y + y / 4 + c / 4 - 2 * c + 26 * (month_1 + 1) / 10 + day_1 - 1;
while (week < 0)
week += 7;
week %= 7;
week_1 = week;
}
我这里修改了时间戳转换日期的函数、新增一个星期转换函数。传入10位数的时间戳就可得出对应的日期。
OLED模块介绍(oled.c):
OLED的库函数我也是走了不少弯路才找到的,过程曲折,没想到答案远在天边近在眼前。感谢中景园电子!
这个库函数的功能是我见过比较丰富的了,还有更复杂的是GITHUB上的自制驱动,但是他们用的OLED屏幕和我用的不一样,试图移植过但失败了。贴上.h文件,给大伙看看这个驱动能实现的功能。
/*初始化函数*/
void OLED_Init(void);
/*更新函数*/
void OLED_Update(void);
void OLED_UpdateArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
/*显存控制函数*/
void OLED_Clear(void);
void OLED_ClearArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
void OLED_Reverse(void);
void OLED_ReverseArea(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height);
/*显示函数*/
void OLED_ShowChar(uint8_t X, uint8_t Y, char Char, uint8_t FontSize);
void OLED_ShowString(uint8_t X, uint8_t Y, char *String, uint8_t FontSize);
void OLED_ShowNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
void OLED_ShowSignedNum(uint8_t X, uint8_t Y, int32_t Number, uint8_t Length, uint8_t FontSize);
void OLED_ShowHexNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
void OLED_ShowBinNum(uint8_t X, uint8_t Y, uint32_t Number, uint8_t Length, uint8_t FontSize);
void OLED_ShowFloatNum(uint8_t X, uint8_t Y, double Number, uint8_t IntLength, uint8_t FraLength, uint8_t FontSize);
void OLED_ShowChinese(uint8_t X, uint8_t Y, char *Chinese);
void OLED_ShowImage(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, const uint8_t *Image);
void OLED_Printf(uint8_t X, uint8_t Y, uint8_t FontSize, char *format, ...);
/*绘图函数*/
void OLED_DrawPoint(uint8_t X, uint8_t Y);
uint8_t OLED_GetPoint(uint8_t X, uint8_t Y);
void OLED_DrawLine(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1);
void OLED_DrawRectangle(uint8_t X, uint8_t Y, uint8_t Width, uint8_t Height, uint8_t IsFilled);
void OLED_DrawTriangle(uint8_t X0, uint8_t Y0, uint8_t X1, uint8_t Y1, uint8_t X2, uint8_t Y2, uint8_t IsFilled);
void OLED_DrawCircle(uint8_t X, uint8_t Y, uint8_t Radius, uint8_t IsFilled);
void OLED_DrawEllipse(uint8_t X, uint8_t Y, uint8_t A, uint8_t B, uint8_t IsFilled);
void OLED_DrawArc(uint8_t X, uint8_t Y, uint8_t Radius, int16_t StartAngle, int16_t EndAngle, uint8_t IsFilled);
支持两个大小的汉字显示、数字字母显示、图像显示、简单的图形绘制、图片显示。
还是和第一版的一样,我会把取字模和图像的软件打包起来,直接用就可以。
ESP8266驱动函数(ESP8266.c):
这个就比较的复杂了,直接贴代码吧,大家看了有不懂的评论区留言我帮忙解答
#include "stm32f10x.h"
#include "sys.h"
#include "string.h"
#include "stdlib.h"
#include "esp8266.h"
#include "usart2.h"
#include "delay.h"
#include "init.h"
//WIFI的SSID和密码
#define ESP8266_WIFI_INFO "AT+CWJAP=\"TP-LINK_C568\",\"zz18835784774\"\r\n"
//心知天气API
#define Xinzhi_TCP "AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80\r\n"
//拼多多API
#define Time_TCP "AT+CIPSTART=\"TCP\",\"qapi.pinduoduo.com\",80\r\n"
//获取当天天气
#define Now_GET "GET https://api.seniverse.com/v3/weather/now.json?key=SEa9maHD3BOFLn8Xv&location=beijing&language=en&unit=c\r\n"
//获取天气预报
#define Forcast_GET "GET https://api.seniverse.com/v3/weather/daily.json?key=SEa9maHD3BOFLn8Xv&location=beijing&language=en&unit=c&start=0&days=3\r\n"
//获取生活指数
#define Life_GET "GET https://api.seniverse.com/v3/life/suggestion.json?key=SEa9maHD3BOFLn8Xv&location=shanghai&language=en&days=1\r\n"
//获取拼多多时间戳
#define Time_GET "GET https://api.pinduoduo.com/api/server/_stm\r\n"
unsigned char esp8266_buf[1000] = {0};
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0;
unsigned char Xinzhi_buff[1000];
unsigned char Time_buff[50];
//==================================================================
//函 数 名:ESP8266_Init()
//功能描述:ESP8266模块初始化
//介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
//助手分别发送相应的AT指令验证是否能初始化
//==================================================================
void ESP8266_Init(void)
{
ESP8266_Clear();
while(ESP8266_SendCmd("+++", ""));
while(ESP8266_SendCmd("AT+RESTORE\r\n", "OK"));
while(ESP8266_SendCmd("AT\r\n", "OK"));
ESP8266_SendCmd("AT+RST\r\n", "");
delay_ms(500);
ESP8266_SendCmd("AT+CIPCLOSE\r\n", "");
delay_ms(500);
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"));
while(ESP8266_SendCmd("AT+CIPMUX=0\r\n", "OK"));
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "WIFI GOT IP"));
}
//==================================================================
//函 数 名:Get_current_time(unsigned char s)
//功能描述:获取指定API的数据
//介绍说明:按照我写的第一篇文章的步骤,使用串口助手在电脑端通过串口
//助手分别发送相应的AT指令验证是否能获取到对应的数据报文
//==================================================================
void Get_current_time(unsigned char s)
{
ESP8266_Init();
ESP8266_Clear();
switch(s)
{
//case 0:获取时间戳报文
case 0 :{while(ESP8266_SendCmd(Time_TCP, "CONNECT"));
while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
ESP8266_SendData((u8 *)Time_GET, sizeof(Time_GET));
ESP8266_GetIPD_GET(50, Time_buff);
} break;
//case 1:获取当前天气报文
case 1 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
ESP8266_SendData((u8 *)Now_GET, sizeof(Now_GET));
ESP8266_GetIPD_GET(200, Xinzhi_buff);
} break;
//case 2:获取天气预报报文
case 2 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
ESP8266_SendData((u8 *)Forcast_GET, sizeof(Forcast_GET));
ESP8266_GetIPD_GET(1000, Xinzhi_buff);
} break;
//case 3:未完成部分-生活指数
// case 3 :{while(ESP8266_SendCmd(Xinzhi_TCP, "CONNECT"));
// while(ESP8266_SendCmd("AT+CIPMODE=1\r\n", "OK"));
// ESP8266_SendData((u8 *)Life_GET, sizeof(Life_GET));
// ESP8266_GetIPD_GET(1000, Xinzhi_buff);
// } break;
}
ESP8266_Clear();
while(ESP8266_SendCmd("+++", ""));
}
#define NAME_ADD_DRES 7
#define TEXT_ADD_DRES 7
#define TEMP_ADD_DRES 14
#define HIGH_TEMP_DRES 7
#define LOW_TEMP_DRES 6
#define RAINFALL_DRES 11
#define WIND_DIRCTION 17
#define WIND_SCAL 13
#define HUMITIDITY 11
//#define AIR_POLLU 25
#define TIME_ADD_DRES 13
int TEXT,TEMP,LOW_TEMP,HIGH_TEMP,RAINFALL,WIND_DIR,SCAL_WIND,HUMI;
unsigned int stamp;
//==================================================================
//函 数 名:cJSON_Time_Parse(unsigned char s)
//功能描述:通过标志位启动不同的数据解析方式
//输入参数:0-解析时间、1-解析当前天气、2-解析天气预报
//==================================================================
void cJSON_Time_Parse(unsigned char s)
{
char *data_pt;
char *text_string;
char *temp_string;
char *time;
switch(s){
case 0:{
data_pt = strstr((const char *)Time_buff, (const char *)"server_time");
time = data_pt + TIME_ADD_DRES;
stamp = Get_Time(time);
} break;
case 1:{
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"code");
text_string = data_pt + TEXT_ADD_DRES;
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"temperature");
temp_string = data_pt + TEMP_ADD_DRES;
TEXT = Get_Temp(text_string);
TEMP = Get_Temp(temp_string);
} break;
case 2:{
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"high");
time = data_pt + HIGH_TEMP_DRES;
HIGH_TEMP = Get_Temp(time);
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"low");
time = data_pt + LOW_TEMP_DRES;
LOW_TEMP = Get_Temp(time);
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"rainfall");
time = data_pt + RAINFALL_DRES;
RAINFALL = Get_Temp(time);
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_direction");
time = data_pt + WIND_DIRCTION;
WIND_DIR = Get_Wind(time);
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"wind_scale");
time = data_pt + WIND_SCAL;
SCAL_WIND = Get_Temp(time);
data_pt = strstr((const char *)Xinzhi_buff, (const char *)"humidity");
time = data_pt + HUMITIDITY;
HUMI = Get_Temp(time);
} break;
//未完成部分-生活指数
// case 3:{
// data_pt = strstr((const char *)Xinzhi_buff, (const char *)"air_pollution");
// text_string = data_pt + AIR_POLLU;
// data_pt = strstr((const char *)Xinzhi_buff, (const char *)"comfort");
// temp_string = data_pt + TEMP_ADD_DRES;
// TEXT = Get_Temp(text_string);
// TEMP = Get_Temp(temp_string);
// } break;
}
}
//==================================================================
//函 数 名:Get_Wind(char *y)
//功能描述:得到风向
//输入参数:无
//介绍说明:不同的返回值代表不同的风向
//==================================================================
int Get_Wind(char *y)
{
char dir[] = {'W','E','N','S','"'};// 0 1 2 3
int flag , i , j = 1;
for(i=0;i<4;i++)
{
if(*y == dir[i]){
flag = i+4;
}
}
for(i=0;i<2;i++)
{
for(j=0;j<2;j++)
{
if(*y == dir[i + 2] && *(y+1) == dir[j]){
if(i==0){flag = i*2 + j*1;};
if(i==1){flag = i*2 + j*1;};
}
}
}
return flag;
}
//==================================================================
//函 数 名:Get_Temp(char *y)
//功能描述:从字符串中取出时间戳-最长10位长度
//输入参数:无
//==================================================================
int Get_Temp(char *y)
{
int temp_return;
temp_return = atoi(y);
return temp_return;
}
//==================================================================
//函 数 名:Get_Time(char *d)
//功能描述:从字符串中取出数字-最长7位长度
//输入参数:无
//==================================================================
unsigned int Get_Time(char *d)
{
unsigned int u;
char dest[10] = {0};
strncpy(dest, d, 10);
u = atoi(dest);
return u;
}
//==================================================================
//函 数 名:USART2_IRQHandler(void)
//功能描述:串口2中断控制函数
//输入参数:无
//介绍说明:将串口2接收到的数据转发出去
//==================================================================
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0;
esp8266_buf[esp8266_cnt++] = USART2->DR;
USART_SendData(USART1,USART2->DR);
USART_ClearFlag(USART2, USART_FLAG_RXNE);
}
}
/**下面的代码来源于:
************************************************************
************************************************************
************************************************************
* 文件名: esp8266.c
*
* 作者: 张继瑞
*
* 日期: 2017-05-08
*
* 版本: V1.0
*
* 说明: ESP8266的简单驱动
*
* 修改记录:
************************************************************
************************************************************
************************************************************
**/
//==========================================================
// 函数名称: ESP8266_Clear
//
// 函数功能: 清空缓存
//
// 入口参数: 无
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_Clear(void)
{
memset(esp8266_buf, 0, sizeof(esp8266_buf));
esp8266_cnt = 0;
}
//==========================================================
// 函数名称: ESP8266_SendData
//
// 函数功能: 发送数据
//
// 入口参数: data:数据
// len:长度
//
// 返回参数: 无
//
// 说明:
//==========================================================
void ESP8266_SendData(unsigned char *data, unsigned short len)
{
char cmdBuf[32];
ESP8266_Clear(); //清空接收缓存
sprintf(cmdBuf, "AT+CIPSEND\r\n"); //发送命令
if(!ESP8266_SendCmd(cmdBuf, ">")) //收到‘>’时可以发送数据
{
//printf("8.开始处于透传发送状态!\r\n");
/*发送请求数据*/
Usart_SendString(USART2, data, len); //发送设备连接请求数据
}
}
//==========================================================
// 函数名称: ESP8266_GetIPD
//
// 函数功能: copy天气数据到buff数组里面
//
// 返回参数: 平台返回的原始数据
//
// 说明: copy天气数据到buff
//==========================================================
unsigned char *ESP8266_GetIPD_GET(unsigned short timeOut, u8 *buff) //这里我用了一个全局变量将esp8266buf储存到这个全局变量里面
{
do
{
delay_ms(5);
}
while(timeOut--);
strcpy((char*)buff, (char*)esp8266_buf);
return buff;
}
//==========================================================
// 函数名称: ESP8266_WaitRecive
//
// 函数功能: 等待接收完成
//
// 入口参数: 无
//
// 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成
//
// 说明: 循环调用检测是否接收完成
//==========================================================
_Bool ESP8266_WaitRecive(void)
{
if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数
return REV_WAIT;
if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕
{
esp8266_cnt = 0; //清0接收计数
return REV_OK; //返回接收完成标志
}
esp8266_cntPre = esp8266_cnt; //置为相同
return REV_WAIT; //返回接收未完成标志
}
//==========================================================
// 函数名称: ESP8266_SendCmd
//
// 函数功能: 发送命令
//
// 入口参数: cmd:命令
// res:需要检查的返回指令
//
// 返回参数: 0-成功 1-失败
//
// 说明:
//==========================================================
_Bool ESP8266_SendCmd(char *cmd, char *res)
{
unsigned char timeOut = 250;
Usart_SendString(USART2, (unsigned char *)cmd, strlen((const char *)cmd));
while(timeOut--)
{
if(ESP8266_WaitRecive() == REV_OK) //如果收到数据
{
if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词
{
ESP8266_Clear(); //清空缓存
return 0;
}
}
delay_ms(10);
}
return 1;
}
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/6105296745867ec824ec07da44066162.png)
![img](https://img-blog.csdnimg.cn/img_convert/4b0a305775df7918def1fb01cda38eac.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/b91b184ea5b3f04379c428076d8ec0d5.png)
![img](https://img-blog.csdnimg.cn/img_convert/f859b1f4c7091e6aff19aa139676e752.png)
![img](https://img-blog.csdnimg.cn/img_convert/c8920a24ca0cf31582dd8c125e830157.png)
![img](https://img-blog.csdnimg.cn/img_convert/55cfbbd055b7e4dc2812486f7df08a8b.png)
![](https://img-blog.csdnimg.cn/img_convert/ef6c61b289987a7ac3ee3181f049fe12.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中...(img-Fbs0weBF-1715574849270)]
[外链图片转存中...(img-IZLwk3SL-1715574849271)]
[外链图片转存中...(img-SUfFZPbs-1715574849271)]
[外链图片转存中...(img-ILuUkBVL-1715574849272)]
[外链图片转存中...(img-bNkzq2K1-1715574849272)]
[外链图片转存中...(img-1kyUP5Wb-1715574849273)]
[外链图片转存中...(img-h8u9gshj-1715574849273)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!