按产品思维开发,是先把模块的程序编写好,比如按键、串口,RS-485、WIFI等,这样就会有很多.c源文件和.h头文件,最后再按照程序流程图将功能组合在一起,就形成了一个完整的产品
一个物联网项目的雏形,可以采集设备温度,可以控制灯,温度可以通过网络上传到服务器,服务器也可以下发指令控制灯,也可通过Modbus协议进行本地设备的通信,走的是RS-485接口,上位机显示设备温度,同时可以设置设备的灯的亮度,通过长按按键可以控制设备进入低功耗模式,单击按键将设备唤醒,服务器也可以发送待机指令控制设备进入低功耗模式
数码管显示PCB板温度;
上位机通过RS-485接口,Modbus协议与开发板通讯;
TCP服务器通过WIFI模块,TCP协议与开发板通讯;
按键1长按2s系统待机,待机功耗低至1.1uA;
按键1单击,系统恢复正常运行;
按键2单击,切换PWM灯亮度;
按键2双击,连接TCP服务器;
按键2长按,WIFI模块进入配网模式。
main.c ->主函数文件,包含 main函数,主要的系统框架,分为系统运行和系统待机,根据系统状态位改变;
Public.c ->公共函数文件,包含Delay延时函数、Memory_Clr内存清理函数、Error_Handler错误处理函数、Sys_Soft_Reset系统软件复位函数;
LED.c->LED外设函数,包含LED打开、关闭、翻转函数;
Sys_init.c->系统初始化函数,包含GPIO初始化函数、Power_ON_indication上电指示、IE_Init中断初始化函数、Sys_Init系统初始化函数,包含各种外设的初始化函数;
Timer0.c ->定时器函数,包含定时器初始化,中断服务函数;
KEY1.c->按键1函数,包含按键检测,使用的是外部中断检测方法,检测单击和长按;
KEY2.c->按键2函数,使用的是状态机来扫描按键2;
PWM.c ->PWM初始化、亮度调节、占空比储存与恢复函数等;
IAP.c->字节读、字节写、扇区擦除等函数;
UART1.c ->串口1初始化、发送字节、发送字符串、发送数组,RS-485开启和关闭,printf函数重定向,中断、串口协议解析等函数;
ADC.c->ADC初始化,采集ADC值、滤波算法函数等;
TM1620.c ->驱动IC TM1620的初始化,协议,温度显示等函数。
NTC.c ->NTC外设函数,包含获取NTC电压值,查表,获取环境温度;
CRC-16.c ->校验函数,主要包含CRC-16校验函数;
Modbus.c ->Modbus外设函数,主要包含Modbus 协议解析函数、根据功能码分为读保持寄存函数,写保持寄存器函数;
ESP8266.c -> WIFI模块函数,包含模块初始化、配网,连接服务器,发送PCB板温度、接收服务器信息等函数;
UART2.c->串口2的相关函数,发送字节、发送字符串、发送数组,中断,主要与WiFi模块通信;
System.c->系统文件,包含系统运行,进入待机配置,退出待机配置,系统待机等函数;
WatchDog.c ->看门狗文件,包含看门狗初始化与喂狗函数;
如果产品有低功耗,则可以使用这个系统框架,根据系统状态位切换系统状态,分为系统运行和系统待机
因为public.h中没有预编译宏定义Monitor_Run_Code,所以不会往串口输出信息,而且这些本就是调试信息,在出产品时是不需要的
/*
* @name main
* @brief 主函数
* @param void
* @retval int
*/
int main(void)
{
//系统初始化
Hradware.Sys_Init();
//串口1发送初始化信息
#ifdef Monitor_Run_Code
printf("Initialization completed,system startup!\r\n\r\n");
#endif
//系统主循环
while(1)
{
if(System.ucSystem_Statues == System_Run)
{
System.Run(); //系统运行
}
else
{
System.StandBy(); //系统待机
}
}
}
系统运行函数Run(),系统正常运行时,所执行的功能
/*
* @name Run
* @brief 系统运行
* @param None
* @retval None
*/
static void Run()
{
static uint16_t i = 0;
//获取PCB板温度
NTC.Get_Temperature_Value();
//数码管显示温度
TM1620.Disp_Tempareture();
//按键2双击
if(KEY2.Double_Click == TRUE)
{
KEY2.Double_Click = FALSE;
//连接TCP服务器
ESP8266.TCP_Connect_Server();
}
//接收TCP服务器信息
ESP8266.Receive_Information();
//将温度通过TCP传到服务器
ESP8266.Transfer_Tempareture(NTC.fTemperature);
i = 500;
while(i--)
{
Public.Delay_ms(1);
//状态机扫描按键2
KEY2.KEY_Detect();
if(UART1.ucRec_Flag == TRUE)
{
break;
}
if(KEY1.KEY_Flag == TRUE)
{
break;
}
if(KEY2.KEY_Flag == TRUE)
{
break;
}
}
//按键1检测,长按2S进入待机模式
KEY1.KEY_Detect();
//模块配网
ESP8266.SmartConfig();
//按键1触发调整PWM灯亮度
PWM.PWM_LED_Adjust_Brightness();
//串口1协议解析
UART1.Protocol();
}
系统进入停机模式函数Halt(),由StandBy()函数调用;
/*
* @name Halt
* @brief 系统停机模式,只能外部中断唤醒
* @param None
* @retval None
*/
static void Halt()
{
//串口打印调试信息
#ifdef Monitor_Run_Code
printf("The MCU enter halt mode!\r\n\r\n");
#endif
//喂狗
WatchDog.Feed();
//进入停机配置
Halt_Enter_Config();
//进入掉电模式
PCON |= BIT1; //这里单片机已经停止运行,只有外部中断来了才能唤醒继续向下执行
/*被唤醒后,手册要求执行4个空操作,因为单片机启动后可能不太稳定,所以
用空操作等待系统稳定*/
_nop_();
_nop_();
_nop_();
_nop_();
//退出停机配置
Halt_Exit_Config();
//串口打印调试信息
#ifdef Monitor_Run_Code
printf("The MCU Exit halt mode!\r\n");
#endif
System.ucSystem_Statues = System_Run; //系统正常运行
KEY1.KEY_Flag = FALSE;
}
1、通过外部中断检测
2、长按超过2s,系统进入待机模式
3、单击,系统退出待机模式
1、通过状态机扫描
2、单击,切换PWM灯亮度
3、双击,连接TCP服务器
4、长按超过2s,WIFI模块进行配网
间隔5ms中断一次,运行指示灯间隔1s闪烁一次