华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机

因为在做华为LiteOS任务挂起和恢复需要使用到按键去触发任务挂起和恢复动作,因为我就萌发出使用状态机这种架构做一个按键检测触发。回想已经也做过按键状态机,不过因为那时候是在裸机上做的按键状态机,比较简单,只有按下和释放功能,而且是单个按键检测的。
       这里我借此实验机会使用面向对象的编程思想设计一个可按用户按键个数的按键状态机。面向对象思想可以是驱动程序适合在多种平台快速移植开发,可读性高。
       状态机是一个抽象概念,表示把一个过程抽象为若干个状态之间的切换,这些状态之间存在一定的联系。状态机的设计主要包括4个要素:
①现态:是指当前所处的状态。
②条件:当一个条件满足,将会触发一个动作,或者执行一次状态的迁移。
③动作:表示条件满足后执行动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作要素不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
④次态:表示条件满足后要迁往的新状态。
       从上面解释可以清晰地知道,保存当前状态,判断条件是否满足,设置相应的动作标志(这是用于实际的事件触发的),然后将当前状态通过上面的条件判断设置到相应的下一个状态。

       这里我还提供了程序框图让大家参考,应该能解决大家对“有按键状态了,为什么还需要按键事件”这个问题,其实就是按键状态状态机循环扫描按键状态的根本,而按键事件时在按键状态中触发的动作。(按键实际状态只有:初始未按下态、按下态、长按态、释放态)

LiteOS 系统中的每一个任务都有多种运行状态,它们之间的转换称为任务状态迁移。如下图所示。

这里亮代码让大家理解了。
key.h

#ifndef __KEY_H
#define __KEY_H#include "stm32f10x.h"
#include "usart1.h"#define    KEY_UP_GPIO_CLK   RCC_APB2Periph_GPIOA
#define KEY_UP_GPIO_PORT  GPIOA
#define KEY_UP_GPIO_PIN     GPIO_Pin_0#define KEY0_GPIO_CLK         RCC_APB2Periph_GPIOE
#define KEY0_GPIO_PORT      GPIOE
#define KEY0_GPIO_PIN           GPIO_Pin_4#define KEY1_GPIO_CLK         RCC_APB2Periph_GPIOE
#define KEY1_GPIO_PORT      GPIOE
#define KEY1_GPIO_PIN           GPIO_Pin_3#define KEY2_GPIO_CLK         RCC_APB2Periph_GPIOE
#define KEY2_GPIO_PORT      GPIOE
#define KEY2_GPIO_PIN           GPIO_Pin_2/* 按键状态 */
typedef enum {KEY_NULL = 0,                        //初始空状态KEY_WAIT_CONFIRM_DOWN,   //等待确认按下状态KEY_CONFIRN_DOWN,             //确认按下状态KEY_LONG_DOWN,                  //长按状态KEY_UP,                                   //抬起状态
}Key_State_List;/* 按键类,此处添加按键 */
typedef enum {KEYUP = 0,       //key_up按键KEY0,                 //Key0按键KEY1,                   //Key1按键KEY2,                   //Key2按键KEY_CLASS_NUM,//按键类个数,即多少个按键
}Key_Class;/* 按键外设初始化结构体,移植可修改,不同单片机配置外设参数不一样 */
typedef struct {uint32_t RCC_APB2Periph_GPIOx;  //时钟GPIO_TypeDef* GPIOx;                        //端口uint16_t GPIO_Pin_x;                        //引脚GPIOMode_TypeDef GPIO_Mode;         //模式
}Key_Init;/* 状态机初始化结构体,移植可修改函数指针,不同单片机读取函数不一样 */
typedef struct {FunctionalState Key_Use_State;          //按键使用状态,使能还是失能BitAction Key_Press_Level;                    //表示按键按下,与实际的按下电平对应的BitAction   Key_Current_Level;              //表示按键标志,1表示按下,0表示松开uint8_t Key_Count;                                    //按键长按的时间Key_State_List Key_State;                      //用于状态机按键状态切换的Key_State_List Key_Event;                     //按键触发事件,用于外部按键判断是否按下,是长按还是短按BitAction (* Read_Pin_Level)(Key_Init KeyInit);  //函数指针,用于读取对应按键IO的输入电平
}Key_State_Machine;/* 按键结构体,类 */
typedef struct {Key_Init Key_Init;                                      //按键外设初始化Key_State_Machine Key_State_Machine;   //按键状态机初始化
}Key_Config;/* 定义长按时间 (47+3)T=按下的时间,T=20ms,Time=1S,移植可修改,根据个数要求定义长按的时间长度*/
#define Key_Long_Pree_Time 47extern Key_Config  KEY_CLASS[KEY_CLASS_NUM];void Key_Config_parameter(void);
void Read_Key_State(void);#endif

key.c

#include "key.h"/* 定义按键,便于添加按键 */
Key_Config  KEY_CLASS[KEY_CLASS_NUM];/* 读按键对应IO电平函数,移植可修改读取电平函数 */
static BitAction Key_Read_Pin_Level(Key_Init KeyInit) {return (BitAction)GPIO_ReadInputDataBit(KeyInit.GPIOx,KeyInit.GPIO_Pin_x);
}/* 创建按键,即初始化外设和状态机,Key_State_Machine* Key_State_Machine */
void Creat_Key(Key_Init* KeyInit) {uint8_t i;                               //用于逐个对按键状态机结构体赋值GPIO_InitTypeDef GPIO_InitStructure;for(i=0;i<KEY_CLASS_NUM;i++) {/*将配置好的Key_Init结构体参数赋予Key_Config结构体定义好的按键类对应的Key_Init结构体参数*/KEY_CLASS[i].Key_Init = *(KeyInit+i);    /* 按键初始化结构体复制初始化,移植可修改 */RCC_APB2PeriphClockCmd(KEY_CLASS[i].Key_Init.RCC_APB2Periph_GPIOx,ENABLE);      //时钟使能GPIO_InitStructure.GPIO_Pin = KEY_CLASS[i].Key_Init.GPIO_Pin_x;                              //IO引脚GPIO_InitStructure.GPIO_Mode = KEY_CLASS[i].Key_Init.GPIO_Mode;                              //IO模式GPIO_Init(KEY_CLASS[i].Key_Init.GPIOx,&GPIO_InitStructure);                                       //初始化IO/* KEYUP按键结构体中Key_State_Machine结构体赋值 */KEY_CLASS[i].Key_State_Machine.Key_Use_State = ENABLE;   //使能改按键KEY_CLASS[i].Key_State_Machine.Key_Count = 0;                   //长按次数初始化为0KEY_CLASS[i].Key_State_Machine.Key_Current_Level = Bit_RESET;       //初始化按键,为松开状态KEY_CLASS[i].Key_State_Machine.Key_State = KEY_NULL;   //当前按键状态为未按下状态,也就是初始状态KEY_CLASS[i].Key_State_Machine.Read_Pin_Level = Key_Read_Pin_Level;       //将函数指针实例化,指向定义的读IO电平函数/* 判断输入模式,移植可修改,不同的单片机库的输入模式不一样,这里是下拉输入和上拉输入 */if(KEY_CLASS[i].Key_Init.GPIO_Mode == GPIO_Mode_IPD) {  KEY_CLASS[i].Key_State_Machine.Key_Press_Level = Bit_SET;              //设置按键按下的实际电平为高电平} else {KEY_CLASS[i].Key_State_Machine.Key_Press_Level = Bit_RESET;   }       }
}/* 按键配置并初始化 */
void Key_Config_parameter(void) {/* 配置按键外设参数,移植可修改 */Key_Init KeyInit[KEY_CLASS_NUM] = {{KEY_UP_GPIO_CLK,KEY_UP_GPIO_PORT,KEY_UP_GPIO_PIN,GPIO_Mode_IPD},{KEY0_GPIO_CLK,KEY0_GPIO_PORT,KEY0_GPIO_PIN,GPIO_Mode_IPU},{KEY1_GPIO_CLK,KEY1_GPIO_PORT,KEY1_GPIO_PIN,GPIO_Mode_IPU},{KEY2_GPIO_CLK,KEY2_GPIO_PORT,KEY2_GPIO_PIN,GPIO_Mode_IPU},};/* 创建并实例化按键类 */Creat_Key(KeyInit);
}/* 获取按键值 */
static void Get_Key_Level(void)
{uint8_t i;for(i=0;i<KEY_CLASS_NUM;i++) {/* 判断按键是否使能,如果失能就判断下一个按键 */if(KEY_CLASS[i].Key_State_Machine.Key_Use_State==DISABLE) {continue;     //下一次循环}/* 读取当前按键IO的输入电平是否与该按键按下状态的电平一样 */if(KEY_CLASS[i].Key_State_Machine.Read_Pin_Level(KEY_CLASS[i].Key_Init)==KEY_CLASS[i].Key_State_Machine.Key_Press_Level) {KEY_CLASS[i].Key_State_Machine.Key_Current_Level = Bit_SET;      //设置当前为按下状态} else {KEY_CLASS[i].Key_State_Machine.Key_Current_Level = Bit_RESET;}}
}/* 读取按键的状态,状态机的4个要素:现态、条件、动作、次态 */
void Read_Key_State(void) {uint8_t i;Get_Key_Level();   //获取当前按键值for(i=0;i<KEY_CLASS_NUM;i++) {switch(KEY_CLASS[i].Key_State_Machine.Key_State) {/* 状态0:KEY_NULL按键初始状态,空状态,未按下状态 */case KEY_NULL:if(KEY_CLASS[i].Key_State_Machine.Key_Current_Level==Bit_SET) {   //有按键按下/* 将按键状态进入下一个按键等待确认按下状态 */KEY_CLASS[i].Key_State_Machine.Key_State = KEY_WAIT_CONFIRM_DOWN;KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_NULL;    //空事件触发} else {KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_NULL;    //空事件触发}break;/* 第二次扫描,判断按键进入按键等待确认按下状态 */case KEY_WAIT_CONFIRM_DOWN:/* 再次判断按键是否按下,起到消抖作用 */if(KEY_CLASS[i].Key_State_Machine.Key_Current_Level==Bit_SET) {/* 按键进入按键确认按键状态 */KEY_CLASS[i].Key_State_Machine.Key_State = KEY_CONFIRN_DOWN;KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_CONFIRN_DOWN; //按键按下触发KEY_CLASS[i].Key_State_Machine.Key_Count = 0;  //按键计数清零,为下面判断是否为长按做准备} else {/* 假按下,所有按键没有按下 */KEY_CLASS[i].Key_State_Machine.Key_State = KEY_NULL;KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_NULL;}break;/* 第三次扫描,按键按下,是否是长按还是短按 */case KEY_CONFIRN_DOWN:/* 如果按键在第三次扫描中判断出按键没有按下,说明是短按,按键已经释放 */if(KEY_CLASS[i].Key_State_Machine.Key_Current_Level!=Bit_SET) {KEY_CLASS[i].Key_State_Machine.Key_State = KEY_NULL; //按键释放了,进入最开始状态KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_UP;       //按键释放状态/* 判断按键仍然按下,并且按下是时间达到长按时间要求 */} else if((KEY_CLASS[i].Key_State_Machine.Key_Current_Level==Bit_SET) && (++KEY_CLASS[i].Key_State_Machine.Key_Count>=Key_Long_Pree_Time)){KEY_CLASS[i].Key_State_Machine.Key_State = KEY_LONG_DOWN;  //按键为长按状态KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_LONG_DOWN; //触发按键长按事件KEY_CLASS[i].Key_State_Machine.Key_Count = 0;    //按键计数清零,记录长按的了多少个扫描周期,即为按键触发了多少次按下事件} else {/* 按下状态但未达到长按状态 */KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_NULL;  //按键不触发事件}break;/* 第四次扫描,进入长按状态,需要计算按下长按的周期个数 */case KEY_LONG_DOWN:/* 如果按键进入长按状态,但是进入后此状态很快就释放了 */if(KEY_CLASS[i].Key_State_Machine.Key_Current_Level!=Bit_SET) {KEY_CLASS[i].Key_State_Machine.Key_State = KEY_NULL;    //按键为达成长按计算目的,重新回对初始状态KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_UP;        //按键按下后触发按键释放事件/* 如果按键仍然在按下,那么就记录长按了多少个扫描周期,即连续按了多少下 */} else if(KEY_CLASS[i].Key_State_Machine.Key_Current_Level==Bit_SET) {KEY_CLASS[i].Key_State_Machine.Key_Event = KEY_LONG_DOWN; //仍然处于长按触发事件++KEY_CLASS[i].Key_State_Machine.Key_Count;       //累加按键按下扫描周期个数}break;default:break;}}
}

main.c

#include "stm32f10x.h"
#include "los_sys.h "
#include "los_typedef.h"
#include "los_task.ph"#include "led.h"
#include "usart1.h"
#include "key.h"/* 定义任务 ID 变量 */
UINT32 Test1_Task_Handle;
UINT32 Test2_Task_Handle;
UINT32 Key_Task_Handle;/* 函数声明 */
static UINT32 AppTaskCreate(void);      //用于创建任务管理
static UINT32 Creat_Test1_Task(void);   //创建任务1函数
static UINT32 Creat_Test2_Task(void);   //创建任务2函数
static UINT32 Creat_Key_Task(void);     //创建任务3函数static void Test1_Task(void);                  //任务1函数
static void Test2_Task(void);                   //任务2函数
static void Key_Task(void);                     //任务3函数int main(void)
{   UINT32 uwRet = LOS_OK; //定义一个任务创建的返回值,默认为创建成功/*中断优先级分组为 4*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);/* 板载相关初始化 */Usart1_Config();LED_GPIO_Config();Key_Config_parameter();printf("KEYUP = %d\r\n",KEY_CLASS[KEYUP].Key_Init.GPIO_Pin_x);printf("KEY0 = %d\r\n",KEY_CLASS[KEY0].Key_Init.GPIO_Pin_x);printf("KEY1 = %d\r\n",KEY_CLASS[KEY1].Key_Init.GPIO_Pin_x);printf("KEY2 = %d\r\n",KEY_CLASS[KEY2].Key_Init.GPIO_Pin_x);printf("正点原子战舰开发板-LiteOS-SRAM 动态创建多任务!\r\n\r\n");/* LiteOS 内核初始化 */uwRet = LOS_KernelInit();if (uwRet != LOS_OK) {printf("LiteOS核心初始化失败!任务代码0X%X\r\n",uwRet);return LOS_NOK;}uwRet = AppTaskCreate();if (uwRet != LOS_OK) {printf("AppTaskCreate任务创建失败!任务代码0X%X\r\n",uwRet);return LOS_NOK;}/* 开启 LiteOS 任务调度 */LOS_Start();while(1){  //上面任务失败会进入此GPIO_SetBits(GPIOB, GPIO_Pin_5);GPIO_SetBits(GPIOE,GPIO_Pin_5);}
}/******************************************************************** @ 函数名 : AppTaskCreate* @ 功能说明: 任务创建,为了方便管理,所有的任务创建函数都可以放在这个函数里面* @ 参数 : 无* @ 返回值 : 无*************************************************************/
static UINT32 AppTaskCreate(void)
{/* 定义一个返回类型变量,初始化为 LOS_OK */UINT32 uwRet = LOS_OK;uwRet = Creat_Test1_Task();if (uwRet != LOS_OK) {printf("Test1_Task任务创建失败!失败代码0X%X\r\n",uwRet);return uwRet;}uwRet = Creat_Test2_Task();if (uwRet != LOS_OK) {printf("Test2_Task任务创建失败!失败代码0X%X\r\n",uwRet);return uwRet;}uwRet = Creat_Key_Task();if (uwRet != LOS_OK) {printf("Key_Task 任务创建失败!失败代码0X%X\r\n",uwRet);return uwRet;}return LOS_OK;
}/******************************************************************* @ 函数名 : Creat_Test1_Task* @ 功能说明: 创建 Test1_Task 任务* @ 参数 :* @ 返回值 : 无******************************************************************/
static UINT32 Creat_Test1_Task()
{//定义一个创建任务的返回类型,初始化为创建成功的返回值UINT32 uwRet = LOS_OK;//定义一个用于创建任务的参数结构体TSK_INIT_PARAM_S task_init_param;task_init_param.usTaskPrio = 5; /* 任务优先级,数值越小,优先级越高 */task_init_param.pcName = "Test1_Task";/* 任务名 */task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test1_Task;        //任务入口函数task_init_param.uwStackSize = 1024; /* 栈大小 */uwRet = LOS_TaskCreate(&Test1_Task_Handle, &task_init_param);    //Test1_Task_Handle任务IDreturn uwRet;
}/******************************************************************* @ 函数名 : Creat_Test1_Task* @ 功能说明: 创建 Test1_Task 任务* @ 参数 :* @ 返回值 : 无******************************************************************/
static UINT32 Creat_Test2_Task()
{//定义一个创建任务的返回类型,初始化为创建成功的返回值UINT32 uwRet = LOS_OK;//定义一个用于创建任务的参数结构体TSK_INIT_PARAM_S task_init_param;task_init_param.usTaskPrio = 4; /* 任务优先级,数值越小,优先级越高 */task_init_param.pcName = "Test2_Task";/* 任务名 */task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Test2_Task;        //任务入口函数task_init_param.uwStackSize = 1024; /* 栈大小 */uwRet = LOS_TaskCreate(&Test2_Task_Handle, &task_init_param);    //Test1_Task_Handle任务IDreturn uwRet;
}/******************************************************************** @ 函数名 : Creat_Key_Task* @ 功能说明: 创建 Key_Task 任务* @ 参数 :* @ 返回值 : 无******************************************************************/
static UINT32 Creat_Key_Task() {// 定义一个创建任务的返回类型,初始化为创建成功的返回值UINT32 uwRet = LOS_OK;TSK_INIT_PARAM_S task_init_param;task_init_param.usTaskPrio = 3; /* 任务优先级,数值越小,优先级越高 */task_init_param.pcName = "Key_Task"; /* 任务名*/task_init_param.pfnTaskEntry = (TSK_ENTRY_FUNC)Key_Task;task_init_param.uwStackSize = 1024; /* 栈大小 */uwRet = LOS_TaskCreate(&Key_Task_Handle,&task_init_param);/*创建任务 */return uwRet;
}/******************************************************************* @ 函数名 : Test1_Task* @ 功能说明: Test1_Task 任务实现* @ 参数 : NULL* @ 返回值 : NULL*****************************************************************/
static void Test1_Task(void) {int i = 0;/* 任务都是一个无限循环,不能返回 */while(1) {if(i%2==0) {GPIO_ResetBits(GPIOB, GPIO_Pin_5);} else {GPIO_SetBits(GPIOB, GPIO_Pin_5);}printf("任务1---1000ms\r\n");i++;if(i>100) i=0;LOS_TaskDelay(1000);}
}/******************************************************************* @ 函数名 : Test2_Task* @ 功能说明: Test2_Task 任务实现* @ 参数 : NULL* @ 返回值 : NULL*****************************************************************/
static void Test2_Task(void) {int i = 0;/* 任务都是一个无限循环,不能返回 */while(1) {if(i%2==0) {GPIO_SetBits(GPIOE,GPIO_Pin_5);} else {GPIO_ResetBits(GPIOE,GPIO_Pin_5);}printf("任务2---500ms\r\n");i++;if(i>100) i=0;LOS_TaskDelay(500);}
}/******************************************************************* @ 函数名 : Key_Task* @ 功能说明: Key_Task 任务实现* @ 参数 : NULL* @ 返回值 : NULL*****************************************************************/
static void Key_Task(void) {UINT32 uwRet = LOS_OK;UINT8 i,trigger_state;       UINT32 j=0;    //是用来检测长按按下的扫描周期数的while(1) {Read_Key_State();   //启动状态机检测按键状态for(i=0;i<KEY_CLASS_NUM;i++) {trigger_state = KEY_CLASS[i].Key_State_Machine.Key_Event; //获取按键触发状态if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEYUP)) {printf("挂起LED_Test2_Task 任务!\r\n");uwRet = LOS_TaskSuspend(Test2_Task_Handle);if (uwRet == LOS_OK) {printf("挂起LED_Test2_Task 任务成功!\r\n");}}if((trigger_state==KEY_CONFIRN_DOWN) && (i==KEY0)) {printf("恢复 LED_Test2_Task 任务!\r\n");uwRet = LOS_TaskResume(Test2_Task_Handle);if (uwRet == LOS_OK) {printf("恢复 LED_Test2_Task 任务成功!\r\n");}}/* 如果为KEY1和长按事件和长按的周期不为0次,那么我就自减按下的周期数 */if((trigger_state==KEY_LONG_DOWN) && (i==KEY1) && (KEY_CLASS[i].Key_State_Machine.Key_Count>=0)) {KEY_CLASS[i].Key_State_Machine.Key_Count--;++j;printf("count = %d\r\n",j);} else if((trigger_state==KEY_UP) && (i==KEY1)) {j = 0;}}LOS_TaskDelay(20);   }
}

实验现象如下图所示。

小编能力有限,希望大家多多体谅,大家一起相互学习探讨。
工程文件包:华为LiteOS任务挂起和恢复及按键状态机

基于STM32F103移植华为LiteOS_任务挂起与恢复_面向对象编程思想之按键状态机相关推荐

  1. 基于STM32F103移植华为LiteOS物联网系统

    基于STM32F103移植华为LiteOS物联网系统 本实验是通过学习野火的<物联网操作系统 LiteOS开发实战指南>参考学习的. 1.移植前的准备 LiteOS 的源码可从 LiteO ...

  2. 基于STM32F103移植FreeRTOS教程

    前言 本教程只针对移植实操,关于理论部分请另行百度学习. 本教程移植实操基于正点原子源码.教程手册进程移植. 移植实操 1.将正点原子跑马灯程序源码复制一份,添加一个文件夹对FreeRTOS源码进行管 ...

  3. 基于STM32F103移植canfestival协议栈(从站)CANopen

    CAN open是一个基于CAN串行总线的网络传输系统的应用层协议,遵循ISO/OSI协议.CAN现场总线只是实现了OSI七层架构中的物理层和数据链路层,而canopen协议是基于他之上的一个应用层协 ...

  4. 机智云代码移植_IoT开发者 | 基于STM32F103的机智云宠物屋外加4路继电器开源教程...

    [ 写在前面 ] 自智云社区开辟IoT开源项目专区以来,一直有IoT开发者在贡献案例.玛莉甄选了一些具有代表性的案例分享给IoT爱好者们,本文亦如此. 若你有好的案例,想和IoT爱好者们分享,欢迎投稿 ...

  5. 小熊派移植华为 LiteOS-M(基于MDK)

    文章目录 小熊派移植华为 LiteOS-M(基于MDK) 前言 一.软硬件介绍 1.1 小熊派开发板 1.2 LiteOS介绍 1.3 移植前言 二.CubeMX 配置 三.获取源码 四.源码移植 4 ...

  6. stm32f103移植ucosIII系统

    文章目录 一. 使用stmcubemax创建工程 二. 准备uCOSIII源码 三. 移植前准备 1. 在ucos源码中新建两个文件夹 2. 在uC-BSP文件夹下新建bsp.c和bsp.h文件 3. ...

  7. 个人项目 免费开源 基于STM32F103的oled示波器,频谱fft adc电压采集 spi接口0.96寸oled

    @基于stm32的adc检测波形 0.96oled显示(spi/iic)移植gui库(开源). 使用标准库开发,未使用dsp官方库. adc采集到电压并且在oled上显示. 支持0-3.3v电压采集显 ...

  8. 手把手教你丨小熊派移植华为 LiteOS-M

    摘要:本文详细讲解如何移植 LiteOS 到小熊派. 本文分享自华为云社区<小熊派移植华为 LiteOS-M(基于MDK)>,作者: JeckXu666. 前言 之前使用小熊派实现了鸿蒙动 ...

  9. 从站代码迁移,基于stm32f103与LAN9252

    从站代码迁移,基于stm32f103与LAN9252 最近刚刚完成了从站代码的迁移,新的控制芯片使用了stm32f103,通信芯片使用了LAN9252,过程当中碰到了很多问题,当然在知乎寻求帮助得到了 ...

最新文章

  1. 用C#钩子写一个改键外挂
  2. C 语言编程利器 之CLion
  3. dvwa如何打开_一篇文章让你搭建自己的Web安全测试平台(Dvwa)
  4. 【pmcaff专栏】陆蔚青:漫谈商业智能
  5. 《网络攻防实践》第七周作业
  6. window.event.srcElement (转)
  7. weakhashmap_Java WeakHashMap keySet()方法与示例
  8. 多台tomcat服务的session共享 memcached与redis
  9. JEECG第14期架构培训班开始招生啦,本周三开班!
  10. 【CSS】利用宽高比例的媒体查询
  11. 在GitHub存储库中创建标记
  12. WordPress学习笔记(二)插件安装
  13. 学习面向对象的三条主线之三 面向对象的三大特征 关键字
  14. 在线教育与计算机网络的融合发展,[浅谈线上教育和线下教育的融合]
  15. egret性能优化总结
  16. 为什么运营商的机顶盒(IPTV)看直播不卡?
  17. 浏览器书签有效性验证
  18. linux中pingpong测试程序的解读
  19. android手机wifi快的办法,手机wifi如何设置网速变快(这样设置网速瞬间堪比5G)...
  20. excel填充序列_猴哥讲述:excel的自动填充功能——自动填充单元格

热门文章

  1. 最有前景的RPO数字化趋势来到了,工作系统是怎么逆袭的?
  2. vue element 的 el-cascade 组件如何实现多选点击确认提交操作
  3. form提交前进行确认提示
  4. 量子前沿英雄谱|Marin Soljačić(马林·索尔季奇)
  5. Day65:Python获取阿里云产品云监控数据指标
  6. SpringBoot 之 PDF大文件分片加载(后端)
  7. word转PDF后mathtype公式乱码以及图片分辨率降低等一系列问题|完美解决
  8. web服务r oauth_通过OAuth访问社交网站,第3部分,将Web Twitter客户端部署到Google App Engine...
  9. 不放心把手机邮寄给苹果直营店怎么办?换电池还有其他绝招!
  10. Arduino Nano 做的一个航模小遥控器