一个学妹写的按键检测函数把我秀翻了!

摘要:今年实验室来了三个学妹,其中一个学妹以前是物联网专业的,进了实验室老师二话没说:先把STM32单片机过一遍04ec8a0143d170e4e7aaf1a6f0cd60cc.png

上来第一个例程就是使用按键点亮一个LED灯,好家伙。点灯小师弟比较在行,毕竟32、FPGA、Linux的小灯都被小师弟点了一遍。哈哈哈!所以今天还是来说一说按键检测吧!5e27dd9a0500e91cd7ae41862a399dba.png

一、如何进行按键检测

检测按键有中断方式和GPIO查询方式两种。推荐大家用GPIO查询方式。

1.从裸机的角度分析

  • 中断方式:中断方式可以快速地检测到按键按下,并执行相应的按键程序,但实际情况是由于按键的机械抖动特性,在程序进入中断后必须进行滤波处理才能判定是否有效的按键事件。如果每个按键都是独立的接一个 IO 引脚,需要我们给每个 IO 都设置一个中断,程序中过多的中断会影响系统的稳定性。中断方式跨平台移植困难。

  • 查询方式:查询方式有一个最大的缺点就是需要程序定期的去执行查询,耗费一定的系统资源。实际上耗费不了多大的系统资源,因为这种查询方式也只是查询按键是否按下,按键事件的执行还是在主程序里面实现。

2.从OS的角度分析

  • 中断方式:在 OS 中要尽可能少用中断方式,因为在RTOS中过多的使用中断会影响系统的稳定性和可预见性。只有比较重要的事件处理需要用中断的方式。

  • 查询方式:对于用户按键推荐使用这种查询方式来实现,现在的OS基本都带有CPU利用率的功能,这个按键FIFO占用的还是很小的,基本都在1%以下。

二、最简单的按键检测程序

先给他说了一种经典的按键检测代码,相信大多数人使用按键函数都见过它,很简单就不过多介绍了!

#define KEY0_PRES 1  //KEY0  
#define KEY1_PRES 2  //KEY1 
#define WKUP_PRES 3  //WK_UP u8 KEY_Scan(u8 mode)
{  static u8 key_up=1;//按键按松开标志if(mode)key_up=1;  //支持连按    if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)){delay_ms(10);//去抖动 key_up=0;if(KEY0==0)return KEY0_PRES;else if(KEY1==0)return KEY1_PRES;else if(WK_UP==1)return WKUP_PRES; }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;       return 0;// 无按键按下
}int main(void)
{ u8 t=0;   delay_init();       //延时函数初始化   LED_Init();       //初始化与LED连接的硬件接口KEY_Init();           //初始化与按键连接的硬件接口LED=0;     //点亮LEDwhile(1){t=KEY_Scan(0);  //得到键值switch(t){     case KEY0_PRES:  //如果KEY0按下LED=!LED;break;default:delay_ms(10); } }
}

如果你在工作中使用这种代码,有可能会被同事笑话。当然我这里并不是说这种代码不好,不管黑猫白猫,能抓住老鼠就是好猫。只要能满足项目需求实现对应的功能就是好代码。但是如果你使用下面这种个人感觉可能会更好。

其实也并没有什么神秘感,就是使用了FIFO机制。参考的就是安富莱的按键例程,不过源代码相对比较复杂,对于初学者并不友好,所以小小的修改了一下,仅供参考!

在前面分享了使用系统滴答定时器实现了多个软件定时器,在按键FIFO中也需要使用这个定时器。在系统的开始我们会启动一个10ms的软件定时器。在这个10ms的软件定时器中不断的进行按键扫描,与其他的任务互不影响。

三、为什么要了解FIFO

要回答什么是FIFO,先要回答为什么要使用FIFO。只有搞清楚使用FIFO的好处,你才会有意无意的使用FIFO。学习FIFO机制和状态机机制一样,都是在裸机编程中非常重要的编程思想。编程思想很重要。初级coder总是在关注代码具体是怎么写,高级coder关注的是程序的框架逻辑,而不是某个细节。只要你框架逻辑通了,则一通百通。

四、什么是FIFO

FIFO是先入先出的意思,即谁先进入队列,谁先出去。比如我们需要串口打印数据,当使用缓存将该数据保存的时候,在输出数据时必然是先进入的数据先出去,那么该如何实现这种机制呢?首先就是建立一个缓存空间,这里假设为10个字节空间进行说明。

c98bfacf3ca3d3d379b809045df2140a.png

从这张图就知道如果要使用FIFO,就要定义一个结构体,而这个结构体至少应该有三个成员。数组buf、读指针read、写指针write

typedef struct
{uint8_t Buf[10];  /* 缓冲区 */uint8_t Read;   /* 缓冲区读指针*/uint8_t Write;   /* 缓冲区写指针 */
}KEY_FIFO_T;

缓存一开始没有数据,并且用一个变量write指示下一个写入缓存的索引地址,这里下一个存放的位置就是0,用另一个变量read 指示下一个读出缓存的索引地址,并且下一个读出数据的索引地址也是0。目前队列中是没有数据的,也就是不能读出数据,队列为空的判断条件在这里就是两个索引值相同。

现在开始存放数据:

8192c7d945f615dfbbfb68a1988e1df5.png

在这里可以看到队列中加入了9个数据,并且每加入一个数据后队尾索引加 1,队头不变,这就是数据加入队列的过程。但是缓存空间只有10个,如何判断队列已满呢?如果只是先一次性加数据到队列中,然后再读出数据,那这里的判断条件显然是队尾索引为9。

好了这就是FIFO的基本原理,下面来看一下按键FIFO是怎么操作的

我们这里以5个字节的FIFO空间进行说明。Write变量表示写位置,Read 变量表示读位置。初始状态时,Read = Write = 0。

6044287383905f8cbb4858c2677cca51.png

我们依次按下按键 K1,K2,那么FIFO中的数据变为:

32c7b576bc99a16f9389b4d67448096b.png

如果 Write!= Read,则我们认为有新的按键事件。我们通过函数KEY_FIFO_Get()读取一个按键值进行处理后,Read 变量变为 1。Write 变量不变。

7bc3a3be9beb8759ae706be9d98ca837.png

继续通过函数KEY_FIFO_Get()读取 3 个按键值进行处理后,Read 变量变为 4。此时Read = Write= 4。两个变量已经相等,表示已经没有新的按键事件需要处理。

有一点要特别的注意,如果 FIFO 空间写满了,Write 会被重新赋值为 0,也就是重新从第一个字节空间填数据进去,如果这个地址空间的数据还没有被及时读取出来,那么会被后来的数据覆盖掉,这点要引起大家的注意。我们的驱动程序开辟了 10 个字节的 FIFO 缓冲区,对于一般的应用足够了。

五、按键FIFO的优点

  1. 可靠地记录每一个按键事件,避免遗漏按键事件。特别是需要实现按键的按下、长按、自动连发、弹起等事件时。

  2. 读取按键的函数可以设计为非阻塞的,不需要等待按键抖动滤波处理完毕。

  3. 按键 FIFO 程序在嘀嗒定时器中定期的执行检测,不需要在主程序中一直做检测,这样可以有效地降低系统资源消耗。

六、按键 FIFO 的实现

1.定义结构体

在我们的key.h文件中定义一个结构体类型为KEY_FIFO_T的结构体。就是前面说的那个结构体。这只是类型声明,并没有分配变量空间。

typedef struct
{uint8_t Buf[10];  /* 缓冲区 */uint8_t Read;   /* 缓冲区读指针*/uint8_t Write;   /* 缓冲区写指针 */
}KEY_FIFO_T;

接着在key.c 中定义 s_tKey 结构变量, 此时编译器会分配一组变量空间。

static KEY_FIFO_T s_tKey;/* 按键FIFO变量,结构体 */

好了按键FIFO的结构体数据类型就定义完了,很简单吧!

2.将键值写入FIFO

既然结构体都定义好了,接着就是往这个FIFO的数组中写入数据,也就是按键的键值,用来模拟按键的动作了。

/*
**********************************************************
* 函 数 名: KEY_FIFO_Put
* 功能说明: 将1个键值压入按键FIFO缓冲区。可用于模拟一个按键。
* 形    参:  _KeyCode : 按键代码
* 返 回 值: 无
**********************************************************
*/
void KEY_FIFO_Put(uint8_t _KeyCode)
{s_tKey.Buf[s_tKey.Write] = _KeyCode;if (++s_tKey.Write  >= KEY_FIFO_SIZE){s_tKey.Write = 0;}
}

函数的主要功能就是将按键代码_KeyCode写入到FIFO中,而这个FIFO就是我们定义结构体的这个数组成员,每写一次,就是每调用一次KEY_FIFO_Put()函数,写指针write就++一次,也就是向后移动一个空间,如果FIFO空间写满了,也就是s_tKey.Write >= KEY_FIFO_SIZE,Write会被重新赋值为 0。

3.从FIFO读出键值

有写入键值当然就有读出键值。

/*
***********************************************************
* 函 数 名: KEY_FIFO_Get
* 功能说明: 从按键FIFO缓冲区读取一个键值。
* 形    参: 无
* 返 回 值: 按键代码
************************************************************
*/
uint8_t KEY_FIFO_Get(void)
{uint8_t ret;if (s_tKey.Read == s_tKey.Write){return KEY_NONE;}else{ret = s_tKey.Buf[s_tKey.Read];if (++s_tKey.Read >= KEY_FIFO_SIZE){s_tKey.Read = 0;}return ret;}
}

如果写指针和读出的指针相等,那么返回值就为0,表示按键缓冲区为空,所有的按键时间已经处理完毕。如果不相等就说明FIFO的缓冲区不为空,将Buf中的数读出给ret变量。同样,如果FIFO空间读完了,没有缓存了,也就是s_tKey.Read >= KEY_FIFO_SIZE,Read也会被重新赋值为 0。按键的键值定义在key.h 文件,下面是具体内容:

typedef enum
{KEY_NONE = 0,   /* 0 表示按键事件 */KEY_1_DOWN,    /* 1键按下 */KEY_1_UP,    /* 1键弹起 */KEY_1_LONG,    /* 1键长按 */KEY_2_DOWN,    /* 2键按下 */KEY_2_UP,    /* 2键弹起 */KEY_2_LONG,    /* 2键长按 */KEY_3_DOWN,    /* 3键按下 */KEY_3_UP,    /* 3键弹起 */KEY_3_LONG,    /* 3键长按 */
}KEY_ENUM;

必须按次序定义每个键的按下、弹起和长按事件,即每个按键对象占用 3 个数值。推荐使用枚举enum, 不用#define的原因是便于新增键值,方便调整顺序。使用{ } 将一组相关的定义封装起来便于理解。编译器也可帮我们避免键值重复。

4.按键检测程序

上面说了如何将按键的键值存入和读出FIFO,但是既然是按键操作,就肯定涉及到按键消抖处理,还有按键的状态是按下还是弹起,是长按还是短按。所以为了以示区分,我们用还需要给每一个按键设置很多参数,就需要再定义一个结构体KEY_T,让每个按键对应1个全局的结构体变量。

typedef struct
{/* 下面是一个函数指针,指向判断按键手否按下的函数 */uint8_t (*IsKeyDownFunc)(void); /* 按键按下的判断函数,1表示按下 */uint8_t  Count;   /* 滤波器计数器 */uint16_t LongCount;  /* 长按计数器 */uint16_t LongTime;  /* 按键按下持续时间, 0表示不检测长按 */uint8_t  State;   /* 按键当前状态(按下还是弹起) */uint8_t  RepeatSpeed; /* 连续按键周期 */uint8_t  RepeatCount; /* 连续按键计数器 */
}KEY_T;

在key.c 中定义s_tBtn结构体数组变量。

static KEY_T   s_tBtn[3] = {0};

每个按键对象都分配一个结构体变量,这些结构体变量以数组的形式存在将便于我们简化程序代码行数。因为我的硬件有3个按键,所以这里的数组元素为3。使用函数指针IsKeyDownFunc可以将每个按键的检测以及组合键的检测代码进行统一管理。

因为函数指针必须先赋值,才能被作为函数执行。因此在定时扫描按键之前,必须先执行一段初始化函数来设置每个按键的函数指针和参数。这个函数是void KEY_Init(void)

void KEY_Init(void)
{KEY_FIFO_Init();  /* 初始化按键变量 */KEY_GPIO_Config();  /* 初始化按键硬件 */
}

下面是KEY_FIFO_Init函数的定义:

static void KEY_FIFO_Init(void)
{uint8_t i;/* 对按键FIFO读写指针清零 */s_tKey.Read = 0;s_tKey.Write = 0;/* 给每个按键结构体成员变量赋一组缺省值 */for (i = 0; i < HARD_KEY_NUM; i++){s_tBtn[i].LongTime = 100;/* 长按时间 0 表示不检测长按键事件 */s_tBtn[i].Count = 5/ 2; /* 计数器设置为滤波时间的一半 */s_tBtn[i].State = 0;/* 按键缺省状态,0为未按下 */s_tBtn[i].RepeatSpeed = 0;/* 按键连发的速度,0表示不支持连发 */s_tBtn[i].RepeatCount = 0;/* 连发计数器 */}/* 判断按键按下的函数 */s_tBtn[0].IsKeyDownFunc = IsKey1Down;s_tBtn[1].IsKeyDownFunc = IsKey2Down;s_tBtn[2].IsKeyDownFunc = IsKey3Down;
}

我们知道按键会有机械抖动,你以为按键按下就是低电平,其实在按下的一瞬间会存在机械抖动,如果不做延时处理,可能会出错,一般如果按键检测到按下后再延时50ms检测一次,如果还是检测低电平,才能说明按键真正的被按下了。反之按键弹起时也是一样的。所以我们程序设置按键滤波时间50ms, 因为代码每10ms扫描一次按键,所以按键的单位我们可以理解为10ms,滤波的次数就为5次。这样只有连续检测到50ms状态不变才认为有效,包括弹起和按下两种事件,即使按键电路不做硬件滤波(没有电容滤波),该滤波机制也可以保证可靠地检测到按键事件。

判断按键是否按下,用一个HAL_GPIO_ReadPin就可以搞定。

static uint8_t IsKey1Down(void) 
{if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == GPIO_PIN_RESET) return 1;else return 0;
}
static uint8_t IsKey2Down(void) 
{if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == GPIO_PIN_RESET) return 1;else return 0;
}static uint8_t IsKey3Down(void) 
{if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2) == GPIO_PIN_RESET) return 1;else return 0;
}

下面是KEY_GPIO_Config函数的定义,这个函数就是配置具体的按键GPIO的,就不需要过多的解释了。

static void KEY_GPIO_Config(void)
{ GPIO_InitTypeDef GPIO_InitStructure;/* 第1步:打开GPIO时钟 */__HAL_RCC_GPIOE_CLK_ENABLE();/* 第2步:配置所有的按键GPIO为浮动输入模式(实际上CPU复位后就是输入状态) */GPIO_InitStructure.Mode = GPIO_MODE_INPUT;      /* 设置输入 */GPIO_InitStructure.Pull = GPIO_NOPULL;                 /* 上下拉电阻不使能 */GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  /* GPIO速度等级 */GPIO_InitStructure.Pin = GPIO_PIN_4;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.Pin = GPIO_PIN_3;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_InitStructure.Pin = GPIO_PIN_2;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

5.按键扫描

按键扫描函数KEY_Scan()每隔 10ms 被执行一次。RunPer10ms函数在 systick中断服务程序中执行。

void RunPer10ms(void)
{KEY_Scan();
}void KEY_Scan(void)
{uint8_t i;for (i = 0; i < HARD_KEY_NUM; i++){KEY_Detect(i);}
}
/*

每隔10ms所有的按键GPIO均会被扫描检测一次。KEY_Detect函数实现如下:

static void KEY_Detect(uint8_t i)
{KEY_T *pBtn;pBtn = &s_tBtn[i];if (pBtn->IsKeyDownFunc()){//这个里面执行的是按键按下的处理if (pBtn->Count < KEY_FILTER_TIME){//按键滤波前给 Count 设置一个初值pBtn->Count = KEY_FILTER_TIME;}else if(pBtn->Count < 2 * KEY_FILTER_TIME){//实现 KEY_FILTER_TIME 时间长度的延迟pBtn->Count++;}else{if (pBtn->State == 0){pBtn->State = 1;/* 发送按钮按下的消息 */KEY_FIFO_Put((uint8_t)(3 * i + 1));}if (pBtn->LongTime > 0){if (pBtn->LongCount < pBtn->LongTime){/* 发送按钮持续按下的消息 */if (++pBtn->LongCount == pBtn->LongTime){/* 键值放入按键FIFO */KEY_FIFO_Put((uint8_t)(3 * i + 3));}}else{if (pBtn->RepeatSpeed > 0){if (++pBtn->RepeatCount >= pBtn->RepeatSpeed){pBtn->RepeatCount = 0;/* 长按键后,每隔10ms发送1个按键 */KEY_FIFO_Put((uint8_t)(3 * i + 1));}}}}}}else{//这个里面执行的是按键松手的处理或者按键没有按下的处理if(pBtn->Count > KEY_FILTER_TIME){pBtn->Count = KEY_FILTER_TIME;}else if(pBtn->Count != 0){pBtn->Count--;}else{if (pBtn->State == 1){pBtn->State = 0;/* 发送按钮弹起的消息 */KEY_FIFO_Put((uint8_t)(3 * i + 2));}}pBtn->LongCount = 0;pBtn->RepeatCount = 0;}
}

这个函数还是比较难以理解的,主要是结构体的操作。所以好好学习结构体,不要见了结构体就跑。

分析:首先读取相应按键的结构体地址赋值给结构体指针变量pBtn ,因为程序里面每个按键都有自己的结构体,只有通过这个方式才能对具体的按键进行操作。(在前面我们使用软件定时器时也使用了这中操作,在滴答定时器的中断服务函数中)。

static KEY_T s_tBtn[3];//程序里面每个按键都有自己的结构体,有三个按键
KEY_T *pBtn;//定义一个结构体指针变量pBtn
pBtn = &s_tBtn[i];//将按键的结构体地址赋值给结构体指针变量pBtn

然后接着就是给按键滤波前给Count设置一个初值,前面说按键初始化的时候已经设置了Count =5/2。然后判断是否按下的标志位,如果按键按下了,这里就将其设置为 1,如果没有按下这个变量的值就会一直是 0。这里可能不理解是就是按键按下发送的键值是3 * i + 1。按键弹起发送的键值是3 * i + 2,按键长按发送的键值是3 * i + 3。也就是说按键按下发送的键值是1和4和7。按键弹起发送的键值是2和5和8,按键长按发送的键值是3和6和9。看下面这个枚举enum你就明白了。

typedef enum
{KEY_NONE = 0,   /* 0 表示按键事件 */KEY_1_DOWN,    /* 1键按下 */KEY_1_UP,    /* 1键弹起 */KEY_1_LONG,    /* 1键长按 */KEY_2_DOWN,    /* 2键按下 */KEY_2_UP,    /* 2键弹起 */KEY_2_LONG,    /* 2键长按 */KEY_3_DOWN,    /* 3键按下 */KEY_3_UP,    /* 3键弹起 */KEY_3_LONG,    /* 3键长按 */}KEY_ENUM;

7.试验演示

int main(void)
{uint8_t KeyCode;/* 按键代码 */KEY_Init();while (1){  /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用KEY_FIFO_Get读取键值即可。 */KeyCode = KEY_FIFO_Get(); /* 读取键值, 无键按下时返回 KEY_NONE = 0 */if (KeyCode != KEY_NONE){switch (KeyCode){case KEY_DOWN_K1:   /* K1键按下 */printf("K1键按下\r\n");break;case KEY_UP_K1:    /* K1键弹起 */printf("K1键弹起\r\n");break;case KEY_DOWN_K2:   /* K2键按下 */printf("K2键按下\r\n");break;case KEY_UP_K2:    /* K2键弹起 */printf("K2键弹起\r\n");break;case KEY_DOWN_K3:   /* K3键按下 */printf("K3键按下\r\n");break;case KEY_UP_K3:    /* K3键弹起 */printf("K3键弹起\r\n");break;        default:/* 其它的键值不处理 */break;}}}
}

不知道学妹看懂没,没看懂就多看几遍。代码例程已上传至Gitee。

https://gitee.com/zhiguoxin/Wechat-Data.git

fedf56e2bda90526e4b21c73e74b5141.png

End


推荐阅读:

专辑|Linux文章汇总

专辑|程序人生

专辑|C语言

我的知识小密圈

关注公众号,后台回复「1024」获取学习资料网盘链接。

欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~

2877446a24417d014cf6c5b696d7dd94.png

嵌入式Linux

微信扫描二维码,关注我的公众号

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/465234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式行业需要什么样的技术人才?

关注「嵌入式大杂烩」&#xff0c;选择「星标公众号」一起进步&#xff01;来源 | 巧学模电数电单片机嵌入式行业需要什么样的技术人才&#xff1f;仔细观察各种招聘的岗位要求吧&#xff0c;无非是两方面。1&#xff09;通用要求比如什么学历&#xff0c;多少年工作经验&#…

消除VIM光标闪烁

2019独角兽企业重金招聘Python工程师标准>>> VIM光标闪烁比较影响人读代码的心情&#xff0c;消除光标闪烁&#xff0c;在配置文件中写下set gcra:block-blinkon0 保存并重启VIM 即可消除光标闪烁。 转载于:https://my.oschina.net/tonyyang/blog/10240

java异常个人理解

废话不说先贴图 所有的异常和错误都继承与Throwable类&#xff0c;它的下面又分为两大子类。 1.Error(uncheck) Error,错误。它是java程序中不被捕获的错误&#xff0c;并且总是不被控制。 例如&#xff1a;OutOfMemoryError 2.Exception(check) Exception,异常。所有的异常类都…

华为专家助你1个月拿下物联网高工认证,首次提供全方位就业指导!

物联网职业方向主要包括研究型岗位、研发型岗位、技术型岗位和技能型岗位4类&#xff1a;技能型岗位&#xff1a;工作内容主要是系统部署实施、运维管理等技术支持服务。技术型岗位&#xff1a;工作内容主要是负责物联网系统规划、设计、集成、技术咨询。研发型岗位&#xff1a…

php获取linux是几核的,linux下怎么查看机器cpu是几核的

linux下怎么查看机器cpu是几核的&#xff1f;linux下查看机器是cpu是几核的几个cpumore /proc/cpuinfo |grep "physical id"|uniq|wc -l每个cpu是几核(假设cpu配置相同)more /proc/cpuinfo |grep "physical id"|grep "0"|wc -lcat /proc/cpuinfo…

我的自学编程之路!

大家好&#xff0c;我是写代码的篮球球痴昨晚上打车回家&#xff0c;接我的滴滴司机是一个年纪比较大的大姐&#xff0c;她说她儿子毕业了&#xff0c;但是找不到好的工作&#xff0c;就报名参加了编程培训&#xff0c;培训费两万多。我就问&#xff0c;那谁给他学费&#xff0…

鹰眼拓扑锁定跟踪 网络管理一目了然

为什么要在网管软件中引入“鹰眼”的概念&#xff1f; 企业网管经常遇到的问题是&#xff1a;为什么业务人员访问生产系统&#xff08;例如ERP等&#xff09;速度非常慢&#xff0c;甚至无法访问&#xff1f;邮件系统无法使用&#xff1f;下载数据时总是无法连接&#xff…

读工作31年退休的同事离职信有感

大家好&#xff0c;我是写代码的篮球球痴&#xff0c;下面的文章是我的朋友痞子衡写的。大家好&#xff0c;我是痞子衡&#xff0c;是正经搞技术的痞子。今天痞子衡不讲技术&#xff0c;跟大家随便唠唠嗑。这周五&#xff0c;痞子衡收到了一位来自恩智浦深圳分公司同事的离职信…

Silverlight 计时器

代码 publicpartialclassMainPage : UserControl{inti 1;TextBlock myTextBlock;publicMainPage(){ InitializeComponent(); myTextBlock newTextBlock(); myTextBlock.Loaded this.StartTimer; myTextBlock.Margin newThickness(30); LayoutRoot.Children.Add(myTextBlock);}p…

这三个Offer,你怎么选?

前几天&#xff0c;有读者给我发信息&#xff0c;让我帮他看一下offer的选择&#xff1a;字节、腾讯天美、阿里云&#xff0c;随便一个都是羡煞旁人的offer&#xff0c;这家伙一下拿了三个&#xff0c;我都开始酸了起来。于是我让他总结一下面试过程中考察的知识点&#xff0c;…

挖掘经典:几乎被人遗忘的HTML七种用法 (转)

转来学习http://www.cnblogs.com/netWild/archive/2010/11/30/1892418.html 当今的WEB领域&#xff0c;新概念、新技术不断涌现 -- WEB2.0、AJAX、HTML5.0... ... 但在几乎被我们忽略的HTML和XHTML里&#xff0c;还有许多并不为人所知的&#xff0c;但却非常有用的一些TAG 以下…

这种扯淡的嵌入式项目,尽量不要碰

第一种&#xff1a;画大饼的项目不接。有的客户会很热情的找到你&#xff0c;告诉你这个项目如何如何地有前途&#xff0c;产品研发成功了之后会有多么大的出货量&#xff0c;会有多么丰厚的利润&#xff0c;建议你一起跟他创业合作&#xff0c;成功了之后给你高额提成。但是产…

高频面试题,malloc实现

面试官&#xff1a;你好&#xff0c;请先做自我介绍我&#xff1a;巴拉巴拉&#xff0c;我喜欢打篮球……面试官&#xff1a;请解释下malloc的实现原理我&#xff1a;我不会面试官&#xff1a;那就先这样&#xff0c;我们就不浪费大家的时间了。在开发c或c时&#xff0c;经常需…

单机版简易考试系统开发过程讲解(C#注册机、用户注册、考试系统、×××全部源码)...

前些日子有个朋友&#xff0c;找我做一个考试软件&#xff0c;浙江省驾驶员理论包考专用系统&#xff0c;总共也没几个页面需要做&#xff0c;但是他以前的数据库VFP的数据&#xff0c;题库里有几千条数据&#xff0c;若人工录入几乎比太可行&#xff0c;他把原来的软件&#x…

CF 1025C Plasticine zebra

昨晚忘记判只有一个字符的情况fst了呜呜呜 挺有趣的题&#xff0c;昨晚连刚带猜弄出结论 考虑答案的取值&#xff0c;最优答案可能是一个后缀&#xff0c;或者是一个前缀&#xff0c;或者是一个后缀加上前缀 那么翻转之后最优答案的可选值就有了1的前缀n的后缀 对于一个合法的z…

嵌入式|这些年我都是怎么找兼职的?

‍‍‍‍1这篇文章是在上一篇文章之后写的&#xff0c;因为上篇文章写到了嵌入式找兼职的一些坑&#xff0c;下面有同学留言说要怎么找到兼职项目&#xff0c;所以这篇文章会写一些我的兼职经历还有总结建议&#xff0c;希望对大家如何找兼职有一些帮助。这篇文章是写第二次了&…

linux java的启动内存溢出,java - Java的R代码可在Linux上运行,但不能在Windows中运行 - 堆栈内存溢出...

我在做什么&#xff1f;我正在用Java编写一个数据分析程序&#xff0c;该程序依赖R的arulesViz库来挖掘关联规则。我想要什么&#xff1f;我的目的是将规则存储在Java中的String变量中&#xff0c;以便以后进行处理。它是如何工作的&#xff1f;该代码分别使用String.format和e…

这届毕业生薪资高,是真的

‍‍‍‍最近一份校招的薪资上了知乎的热搜榜。大家想查薪资的&#xff0c;可以用这个微信小程序offershow>查薪资的小程序<可以看到这个小程序可以查到的企业还挺多的&#xff0c;方便大家在选择工作谈薪资的时候做下参考。额&#xff0c;我这篇文章不是为了介绍这个小程…

这辆迷你摩托车,很酷!

摘要&#xff1a;平衡小车大家应该见到过很多了&#xff0c;比如最有名的要数野生钢铁侠稚晖君的自行车平衡小车了。那个玩意太高级一般人做不出来&#xff0c;作为学生或者DIY爱好者做一辆小型的mini平衡小车还是可以实现的。随着16届恩智浦杯全国大学生智能汽车大赛新增单车拉…

linux errno 线程,Linux errno 错误对照表

errno 在 中定义&#xff0c;错误 Exx 的宏定义在 /usr/include/asm-generic 文件夹下面的 errno-base.h 和 errno.h&#xff0c;分别定义了 1-34 、35-132 的错误定义。strerror() 函数依据 errno 值返回错误描述字符串&#xff0c;下面程序打印对照表&#xff1a;Code highli…