完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
` 本帖最后由 patele 于 2016-3-17 23:07 编辑 Stm8s开发需要准备的材料: 电脑,开发板,stlink 1, 下载安装软件 1.1本教程开发和烧录软件使用stvd + sdvp,请自行下载安装sttoolset***.exe, 安装后有如下2个软件工具(ST Visual Develop用于编译和debug, ST Visual Programmer用于烧写) , 如果你使用的是其他开发工具,如iar,本教程的代码可作参考。 1.2 stm8编译器 stm8 32K cosmic 下载安装(记住安装路径, 我的是C:Program Files (x86)COSMICCXSTM8_32K),Cosmic 公司发布的一个免费版的Cosmic STM8编译器 ,也可直接用于stm8的软件开发,但是一般作为插件提供给stvd。 1.3 本教程debug烧录器使用stlink, 请自行安装 stlink 驱动,本人电脑win7 64bit 版,stlink 驱动是插上stlink后自动安装的,安装成功后在设备管理器中可以看到如下图中标出的一项。 2,创建stm8工程 本人的stm8的具体型号是stm8s103f3p6, 所以后面都默认建立stm8s103f3p6 的工程。 2.1 启动stvd 2.2 file --> New Workspace --> Creat workspace and project --> 确定 2.3 在弹出的对话框中填写workspace名和workspace 存放路径 ---> ok 2.4 在弹出的对话框中填写 project名和路径,然后选择编译工具链,本教程使用 stm8 cosmic,然后填写工具链的路径,即上述1.2提示记住的安装路径,本人填写 C:Program Files (x86)COSMICCXSTM8_32K ---> ok 2.5 在弹出的对话框中的 filter 中输入自己的芯片型号,选择对应芯片,点击 Seclet, 本人使用 stm8s103f3p6 --> ok 2.6 工程已经基本创建完成,如下图(红线标注的分别是workspace 和 project) 双击 source file 展开可以看到在该目录下有2个重要文件main.c 和 stm8_interrupt_vector.c Main.c 中的函数 main() 是应用程序入口, stm8_interrupt_vector.c 中主要是中断向量表 2.7 背景:stvd自动生成的main.c 和 stm8_interrupt_vector.c 更适合一些人所说的寄存器开发,寄存器开发相对库函数开发具有代码更紧凑占用flash少,cpu效率高等优势,而库函数开发的优势则是减轻开发工作,缩短开发周期,而且用库函数开发好程序之后,是可以很方便的修改成寄存器版本的,对于目前的市场来说,缩短开发周期意味着产品更快的推向市场,既可以缩减成本,也可以抢占市场,所以使用库函数开发是最佳选择。 2.7.1 为了方便使用库函数,本人将从ST官方获取的 main.c 和 stm8_interrupt_vector.c 替换掉本工程源码中的对应文件,直接用新的 mian.c 和 stm8_interrupt_vector.c 覆盖原来的即可。 2.7.2 将获取得到 stm8s_conf.h、 stm8s_it.c 和 stm8s_it.h 拷贝到工程源码中。 2.7.3 将获取到的库STM8S_StdPeriph_Driver添加到工程源码中。 进入 STM8S_StdPeriph_Driver 可以看到2个重要文件夹 inc 和src, 其中 inc 中都是***.h头文件,除文件stm8s.h之外的文件都是包含库函数的声明,如 stm8s_adc1.h 是用于 adc1 的所有库函数声明,而stm8s则定义了一些重要的编译信息以及将寄存器地址映射成符号等; Src中则是库函数的实现,每一个xxx.c 文件在inc中都有一个对应名的xxx.h 文件。 2.7.4 在stvd工程新建库函数目录 STM8S_StdPeriph_Driver,用于添加库文件到工程中 在 stvd 的工程中选中工程,然后右键 --> New Folder 在弹出的对话框中输入 STM8S_StdPeriph_Driver -- > ok 2.7.5 编译工程 直接按快捷键 F7 或者点击工具栏图标
(中间红圈的) 或者点击菜单栏 Build --> Build. 即可编译工程。 提示错误: #error opstm8 main.c:24 can’t open stm8s.h 提示很明确:无法打开stm8s.h, 文件stm8s.h 就是前面介绍的库中的inc 中的文件,显然是因为没有添加到stvd工程中。 解决办法:选中 include files --> 右键 --> add files to folder , 找到stm8s.h,添加到工程中, 再编译,出现很多如下类型错误: #error ****************** "Please select first the target STM8S/A device used in your application (in stm8s.h file)" 提示需要在stm8s.h中先选中芯片型号 解决办法:打开stm8s.h, 找到 (因为本人使用的是stm8s103f3p6) /* #define STM8S103*/ /*!< STM8S Low density devices */ 改成 #define STM8S103 /*!< STM8S Low density devices */ 再编译,出现很多如下类型错误: #error clnk Debugcreatproject.lkf:1 symbol *********************** 原因是在 stm8_interrupt_vector.c 定义的中断向量表中引用了很多函数指针,但是编译器找不到该函数。将上述拷贝的 stm8s_it.c 添加到 Source files 中,将 stm8s_it.h 和 stm8s_conf.h 添加到 include files 中。 再编译,通过。 0 error(s), 0 warning(s) 2.7.6 保存 File --> Save Workspace ` |
|
相关推荐
|
|
收藏好资料
|
|
|
|
|
|
第二讲:GPIO之点亮led 讲在前面: 接触stm8是机缘巧合,某天在淘宝买元件,看到一个stm系列的最小系统板,售价4块多钱,看着便宜就顺手放进了购物车,如下图: 到手之后就跑一下例程咯,奈何只有一个led+复位按键,玩了两例程就迫切想扩展一点功能出来玩一玩。 于是又在某宝找了一个51开发板的原理图,把别人的什么蜂鸣器、按键、数码管等功能一参考,自己在洞洞板上简单的焊出一个外围扩展板了 (如下图),本想打个样回来的,摸摸口袋念头又打消了。 而写这个教程呢,是因为某宝店家提供的例程才5个,在网上关于stm8的教程也太少了,而且很少有连贯的系列教程,有一个比较好的叫风驰的,功能扩展确实很多,还带lcd,但是他家的一个是开发板不便宜,二则是像很多人表达出的stm8不需要搞那么复杂(驱动lcd屏幕等),三则是型号不一样啊喂,我买的stm8s103f3p6 比他家的芯片低端多了,资源配置也差很多。 OK,扯淡的话不多讲,下面接着教程。 实验目的: 1,学习编写一个最简单的点亮led程序 2,学习使用stvd 的debug 调试程序。 下面进入正题: 1,查看原理图(核心板),根据原理图可知:IO口 PB5输出低电平可点亮led,输出高电平则led熄灭。 2,本实验用到stm8 的通用io口,所以需要用到库文件 stm8s_gpio.c 中的库函数。 在stvd中将库文件stm8s_gpio.c添加进工程的STM8S_StdPeriph_Driver目录中,因为本人的工程目录 External Dependencies 中自动将库中的inc中xxx.h 全部添加,所以不必再添加对应的 stm8s_gpio.h 文件,如果作为学习者的你发现工程中没有 stm8s_gpio.h ,请用同样的方法将 stm8s_gpio.h 添加进工程的 External Dependencies 目录或者 Include Files 目录。 3,根据设计思路敲代码。 主要代码如下: void main(void) { // 初始化 PB5 为推挽输出 // 库文件中提供的函数 GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // 控制PB5 输出低电平 // 库文件中提供的函数 GPIO_WriteLow(GPIOB, GPIO_PIN_5); /* Infinite loop */ while (1); } 编译工程,通过。 4,在开发板上跑一下 4.1 需要先对 stvd 进行一些配置, 打开菜单“Debug instrument”选择“Target Settings”选项,在下拉菜单中选择使用的debug烧写器,本人使用的是st link v2,所以选择 Swim ST-Link,如下图,然后确认。 4.2 开发板通过 st link 连接电脑 4.3 选择“Debug”菜单下的“Start Debugging”选项,或点击工具栏
按钮。等待程序下载到flash中 4.4 现在可以使用 工具栏上的 快捷按钮很方便的进行调试, 下面介绍几个我常用的项:
: 程序复位,即跳转到main() 入口处
: 继续运行
: 单步运行,不进入函数内部
: 单步运行,进入函数内部
: 退出调试 另外还可以设置断点,如在下图箭头标示处双击,即可放置一个断点,然后调试程序时程序运行到断点处就会自动停下来。 在断点处单击可以取消断点。 4.5 然而调试不是让程序执行一步或者N步,而是执行到某个需要的地方,然后查看程序是否执行正确,同时进行分析程序下一步的走向,然后在后面适当的地方再设置断点进行调试。 那么怎么查看程序是否执行正确以及判断之后走向呢? 对于本实验很简单,如上图处设置好断点之后,从头开始运行程序,然后程序会停在断点处,这时查看led是熄灭的,然后单步运行(不进入函数内部),程序执行将会跳过断点,这时可以看到led被点亮了,说明 GPIO_WriteLow(GPIOB, GPIO_PIN_5); 确实控制 PB5输出低电平了。 上面的方法确实可以进行调试,但是有很大的局限性,因为很多时候我们并不是让程序点灯,比如通过AD采集获取到了转换后的数字信号,我们这时需要查看转换结果,怎么办呢?玩过单片机的应该都知道,像这种debug都会有一个功能,那就是查看ram中的数据,具体怎么做呢? 下面就给大家讲解一下。 5, debug 调试时让程序暂停,查看运行数据是否正确 5.1 为了进行演示,需要将程序修改一下,修改后的代码如下: void main(void) { volatile int i; i = 0; i = 5; i = -1; // 初始化 PB5 为推挽输出 // 库文件中提供的函数 GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_SLOW); // 控制PB5 输出低电平 // 库文件中提供的函数 GPIO_WriteLow(GPIOB, GPIO_PIN_5); /* Infinite loop */ while (1); } 就是在程序的开头处定义了一个 int 型的变量 i,然后对 i 分别赋值 0, 5, -1. Volatile 是一个关键字,用于确保数据一致,初学者如果不理解暂时不必深究。 5.2 然后我们来编译,进入debug 模式,然后让程序复位,跳转到main() 入口,如下图: 5.3 双击选中变量i,然后右键选中quick watch,弹出一个对话框,可以看到变量i的值,类型,地址。同时右侧有个下拉按钮,可以选择变量值的显示方式( 标准/ 十六进制/ 十进制/ 无符号型/二进制).如下图,实际调试时根据自己的方便选择显示方式。然后点击 Add Watch 将变量i添加到监视窗口如下图所示。 5.4 此时看到变量i是一个随机值0x100, 单步运行后,i的值变为0,如下图所示。 再单步运行后,i的值变为5 这种调试方法对于stm8和stm32都非常简单有效,请大家务必掌握。 |
|
|
|
|
|
第三讲:GPIO之驱动蜂鸣器 实验目的: 1,学会使用最基本的延时函数,空指令延时 2,学习修改stm8的系统时钟源 3,学习使用stm8特有的beep输出功能 实现现象: 1,用gpio实现驱动蜂鸣器鸣叫 2,用空指令延时实现蜂鸣器鸣叫——停止——鸣叫——停止 循环交替。 3,用stm8片上beep功能驱动蜂鸣器鸣叫 下面进入正题: 1,查看原理图,根据原理图可知,IO口 PD4输出低电平驱动蜂鸣器鸣叫,输出高电平蜂鸣器停止鸣叫。 2, 同第二讲中实验,只需将PB5改成PD4即可。 编译仿真即可听到蜂鸣器鸣叫。 3,编写空指令延时函数,在while(1)死循环中使蜂鸣器鸣叫——停止——鸣叫——停止,交替出现。 代码如下: // 空指令延时函数 void delay(u32 dly) { u32 i, j; for(i = 0; i < dly; i++) { for(j = 0; j < 300; j++) { ; } } } void main(void) { // 初始化 PD4 为推挽输出 // 库文件中提供的函数 GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); /* Infinite loop */ while (1) { delay(100); // 延时,等一段时间再驱动蜂鸣器鸣叫 // 控制PD4 输出低电平 // 库文件中提供的函数 GPIO_WriteLow(GPIOD, GPIO_PIN_4); delay(100); // 延时,使蜂鸣器鸣叫持续一段时间 // 控制PD4 输出高电平 // 库文件中提供的函数 GPIO_WriteHigh(GPIOD, GPIO_PIN_4); } } 4, 编译仿真即可听到蜂鸣器鸣叫——停止——鸣叫——停止 循环交替。 5, 根据stm8s103手册,芯片在上电启动时,缺省使用HSI的8分频作为系统时钟,其中HSI是高速内部时钟16MHz,所以缺省系统时钟是2MHz。 这里教大家怎么将系统时钟改成HSI,即16MHz。 5.1 代码如下: // 空指令延时函数 void delay(u32 dly) { u32 i, j; for(i = 0; i < dly; i++) { for(j = 0; j < 300; j++) { ; } } } void clk_config(void) { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // HSI时钟预分频, 分频系数1 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // 系统时钟配置, HSI, 分频系数1 CLK_HSICmd(ENABLE); // 使能HSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_HSIRDY)); // 等待HSI ready } void main(void) { // 配置系统时钟为HSI clk_config(); // 初始化 PD4 为推挽输出 // 库文件中提供的函数 GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); /* Infinite loop */ while (1) { delay(100); // 控制PD4 输出低电平 // 库文件中提供的函数 GPIO_WriteLow(GPIOD, GPIO_PIN_4); delay(100); // 控制PD4 输出高电平 // 库文件中提供的函数 GPIO_WriteHigh(GPIOD, GPIO_PIN_4); } } 5.2 由于void clk_config(void) 使用了库函数,需要将库文件stm8s_clk.c添加到工程,添加方法见第二讲 5.3 编译仿真即可听到蜂鸣器鸣叫——停止——鸣叫——停止 循环交替。 可以听出蜂鸣器的循环周期变短了。实际上是因为系统时钟变快了(由原来的2MHz变成16MHz) 6, stm8s103f3p6 芯片集成了beep功能,主要是通过引脚PD4对外输出1kHz / 2kHz / 4kHz的频率,下图摘自手册,欲知详情请看手册的beep章节。 由图中可知,根据选项字的位CKAWUSEL 可以选择使用外部时钟还是内部时钟作为Beep的输入时钟fLS, 然后有5bits计数器分频,再然后有3bit 计数器分频,最终输出1kHz / 2kHz / 4kHz 的Beep驱动信号。 6.1 代码如下: void delay(u32 dly) { u32 i, j; for(i = 0; i < dly; i++) { for(j = 0; j < 300; j++) { ; } } } // 选择LSI RC 128KHz 为beep输入时钟 static void Beep_inputClkSelect(void) { uint16_t value; value = FLASH_ReadOptionByte(0x4807); // 读出选项字OPT4地址0x4807 存储的数据 if(value&0x0400) // 判断CKAWUSEL 位是否为1,若是1,将该位清零 FLASH_ProgramOptionByte(0x4807,(uint8_t)((value&0xFBFF)>>8)); } void clk_config(void) { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // HSI时钟预分频, 分频系数1 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // 系统时钟配置, HSI, 分频系数1 CLK_HSICmd(ENABLE); // 使能HSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_HSIRDY)); // 等待HSI ready } void LSI_enable(void) { CLK_LSICmd(ENABLE); // 使能LSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_LSIRDY)); // 等待LSI ready } void init_beep(void) { BEEP_DeInit(); //Set_Beep_OptionByte(); // select LSI128k as LS clock LSI_enable(); BEEP_LSICalibrationConfig(128000); BEEP_Init(BEEP_FREQUENCY_1KHZ); // BEEP_Cmd(ENABLE); } void change_beep_freq(u8 freq_group) { disableInterrupts(); BEEP_Cmd(DISABLE); switch(freq_group) { case 0: BEEP_Init(BEEP_FREQUENCY_1KHZ); break; case 1: BEEP_Init(BEEP_FREQUENCY_2KHZ); break; case 2: BEEP_Init(BEEP_FREQUENCY_4KHZ); break; default: BEEP_Init(BEEP_FREQUENCY_1KHZ); break; } BEEP_Cmd(ENABLE); enableInterrupts(); } /* * while(1) 死循环中,变量i 一直增加,然后通过i%3 得到 * 0~2的值作为参数传递到函数change_beep_freq,将使beep 的 * 输出频率在1kHz / 2kHz / 4kHz中循环切换 */ void main(void) { u8 i; i = 0; // 关中断 disableInterrupts(); // 系统时钟配置 clk_config(); // 初始化Beep init_beep(); // 开中断 enableInterrupts(); /* Infinite loop */ while (1) { i++; change_beep_freq(i%3); delay(500); } } 6.2 由于Beep功能用到了库函数,所以需要将相应的函数库文件stm8s_beep.c, stm8s_flash.c添加到工程中。 6.3 编译仿真即可听到蜂鸣器有3种不同的鸣叫声音 循环交替。 工程代码请到 http://pan.baidu.com/s/1eRuuaEu 下载 |
|
|
|
|
|
第四讲:定时器 实验目的: 1,学习使用单片机的定时器,体会定时器的好处。 2,了解中断向量表,中断服务函数,函数指针 实验现象: 2, 在第二讲中用空指令延时实现蜂鸣器鸣叫——停止——鸣叫——停止 循环交替。 这里的延时时间是不好控制的,那么如果我们要让蜂鸣器鸣叫0.5s——停止0.5s——鸣叫0.5s——停止0.5s循环交替,该怎么做呢? 基础知识讲解 1.1,对于单片机来讲,定时器是比较简洁的实现较精确计时的资源,一般单片机中都会集成定时器,stm8s103f3p6 片内有2x16bit 定时器和1x8bir 定时器,本实验中使用8bit的timer4. 1.2, 定时器的使用如之前使用gpio或beep一样,需要先初始化,使能之后才能按设计工作,有一点不同的是,我们一般用定时器会用到定时器中断,然后在定时器中断服务函数中实现一些需要周期执行的动作。 在第一讲中有提到,stm8_interrupt_vector.c 文件中有stm8的中断向量表,该中断向量表保存着对应每个中断的中断服务函数的指针(地址),如果cpu发生了中断(同时使能中断),cpu将会到中断向量表中查询获得中断服务函数指针(地址), 然后跳转执行中断服务函数,等中断服务函数执行完再回到之前被中断的地方继续执行程序。 例如本实验的定时器4,按照手册中的中断向量表(上图),定时器4的中断号为23,中断向量地址为0x008064, 在stm8_interrupt_vector.c 中可以找到中断号为23的定义: {0x82, (interrupt_handler_t)TIM4_UPD_OVF_IRQHandler},/* irq23 - TIM4 Update/Overflow interrupt */ 所以定时器的中断服务函数指针为:(interrupt_handler_t)TIM4_UPD_OVF_IRQHandler,对应的函数实现在 stm8s_it.c 中: INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23) { /* In order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction. */ } 一旦发生定时器4中断,cpu就会根据stm8_interrupt_vector.c 内中断向量表的定义找到中断服务函数指针(interrupt_handler_t)TIM4_UPD_OVF_IRQHandle,然后通过指针跳转执行函数INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23),等中断服务函数执行完毕再回到原来被中断的地方继续执行原来的程序。 下面进入正文: 大家应该都想到了用定时器来实现精确计时,下面就是我用定时器4实现的让蜂鸣器鸣叫0.5s——停止0.5s——鸣叫0.5s——停止0.5s循环交替,请看代码: Main.c: void clk_config(void) { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // HSI时钟预分频, 分频系数1 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // 系统时钟配置, HSI, 分频系数1 CLK_HSICmd(ENABLE); // 使能HSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_HSIRDY)); // 等待HSI ready } /* * stm8s103f3p6 的定时器4使用的是 系统时钟 * 本程序中将系统时钟配置为HSI = 16MHz */ void Init_Timer4(void) { // 配置定时器4的预分频值16分频和计数值100 // time4 定时中断周期T = 100*(16/16MHz) = 0.1ms TIM4_TimeBaseInit(TIM4_PRESCALER_16, 100); /* Clear TIM4 update flag */ TIM4_ClearFlag(TIM4_FLAG_UPDATE); /* Enable update interrupt */ TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE); // 使能time4中断 TIM4_Cmd(ENABLE); // 使能timer4 } void main(void) { // 关中断 disableInterrupts(); // 系统时钟配置 clk_config(); // PD4, output GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); Init_Timer4(); // 初始化并使能time4 // 开中断 enableInterrupts(); /* Infinite loop */ while (1) { } } stm8s_it.c: INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23) { /* In order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction. */ static u16 cnt = 0; cnt++; if(cnt >= 5000) // 0.1ms *5000 = 0.5s { cnt = 0; GPIO_WriteReverse(GPIOD, GPIO_PIN_4); // PD4 输出电平翻转 } TIM4_ClearITPendingBit(TIM4_IT_UPDATE); // 清除中断标志位 } 3, 程序中使用了定时器4的库函数,需要将库文件stm8s_tim4.c添加到工程中, 编译仿真,效果:蜂鸣器鸣叫0.5s——停止0.5s——鸣叫0.5s——停止0.5s循环交替 工程代码请到 http://pan.baidu.com/s/1eRuuaEu 下载 |
|
|
|
|
|
|
|
|
|
|
|
stvd 的使用请参考第一讲&第二讲,另外所有的学习都需要动手实践,你需要手边有个开发板跑一跑你写的程序,这样才能真正学到东西 |
|
|
|
|
|
第五讲:GPIO之按键输入 实验目的: 1,学会使用gpio输入 2,理解按键消抖 3,单片机上实现多任务伪并行运行 注:多任务伪并行运行技术源自单片机大神吴坚鸿,在此感谢吴坚鸿的开源精神。 实验现象: 1,当按键按下时,蜂鸣器鸣叫;松开则停止鸣叫 2, 双击按键,蜂鸣器鸣叫;单击则停止鸣叫 下面进入正文: 1,查看原理图, 从原理图知: 1.1, 当key1按下时,PB4为低电平;Key1松开时PB4为高电平 当key2按下时,PB5为低电平;Key2松开时PB5为高电平 本实验只用到Key1 2,当按键按下时,蜂鸣器鸣叫;松开则停止鸣叫 代码如下: void clk_config(void) { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // HSI时钟预分频, 分频系数1 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // 系统时钟配置, HSI, 分频系数1 CLK_HSICmd(ENABLE); // 使能HSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_HSIRDY)); // 等待HSI ready } void main(void) { // 关中断 disableInterrupts(); // 系统时钟配置 clk_config(); // PB4, key1, input GPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_IN_FL_NO_IT); // PD4, beep, output GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); // 开中断 enableInterrupts(); /* Infinite loop */ while (1) { if(RESET == GPIO_ReadInputPin(GPIOB, GPIO_PIN_4)) // 低电平, 按下 GPIO_WriteLow(GPIOD, GPIO_PIN_4); // 蜂鸣器鸣叫 else GPIO_WriteHigh(GPIOD, GPIO_PIN_4); // 蜂鸣器停止鸣叫 delay(50); } } 3, 编译仿真, 按下key1蜂鸣器鸣叫,松开则蜂鸣器停止鸣叫 4,双击按键,蜂鸣器鸣叫;单击则停止鸣叫 代码如下: /* * 需要特别注意的是,下面3个宏定义的时间 * 各个项目用到的值不会一样,需要实际调 * 试选择比较合适的值 */ #include "stm8s.h" /* * 第一次按下按键后,等待双击的时间 * 如果等待超时,则认为是单击 */ #define KEY_PRESS_TIMEOUT 15000 /* * 按下按键时消抖时间 */ #define KEY_DOWN_DEBOUNCE 100 /* * 松开按键时消抖时间 */ #define KEY_UP_DEBOUNCE 50 u8 beep_update = 0; u8 key_press_cnt = 0; /* Private defines -----------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ void clk_config(void) { CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // HSI时钟预分频, 分频系数1 CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1); // 系统时钟配置, HSI, 分频系数1 CLK_HSICmd(ENABLE); // 使能HSI while(RESET == CLK_GetFlagStatus(CLK_FLAG_HSIRDY)); // 等待HSI ready } void key_scan_task(void) { static u8 key_scan_step = 0; static u8 down_debounce_cnt = 0; static u8 up_debounce_cnt = 0; static u32 wait_next_key_cnt = 0; switch(key_scan_step) { case 0: if(RESET == GPIO_ReadInputPin(GPIOB, GPIO_PIN_4)) // 低电平 { down_debounce_cnt++; // 消抖 if(down_debounce_cnt >= KEY_DOWN_DEBOUNCE) // 连续扫描KEY_DEBOUNCE 次PB4, 都是低电平才算按键按下 { key_press_cnt = 1; // 标记按键按下(1次) } } else if(key_press_cnt == 1) // 按键未按下,但是有标记,说明第一次按下按键已松开 { up_debounce_cnt++; if(up_debounce_cnt >= KEY_UP_DEBOUNCE) { up_debounce_cnt = 0; down_debounce_cnt = 0; // clear wait_next_key_cnt = 0; // clear key_scan_step = 1; // 跳转next step, 检测是否有连续按键 } } else // 无按键按下,无按下标记 { down_debounce_cnt = 0; // 清零消抖计数,抗干扰 up_debounce_cnt = 0; } break; case 1: if(RESET == GPIO_ReadInputPin(GPIOB, GPIO_PIN_4)) // 低电平 { down_debounce_cnt++; // 消抖 if(down_debounce_cnt >= KEY_DOWN_DEBOUNCE) // 连续扫描KEY_DEBOUNCE 次PB4, 都是低电平才算按键按下 { key_press_cnt = 2; // 标记连续按键 } } else if(key_press_cnt == 2) // 按键未按下,但是有标记,说明第二次按下按键已松开 { up_debounce_cnt++; if(up_debounce_cnt >= KEY_UP_DEBOUNCE) { wait_next_key_cnt = 0; // clear up_debounce_cnt = 0; // clear down_debounce_cnt = 0; key_scan_step = 0; // 回到step0,检测新按键 beep_update = 1; // 通知beep_task 有新的按键事件 } } else { down_debounce_cnt = 0; up_debounce_cnt = 0; wait_next_key_cnt++; if(wait_next_key_cnt >= KEY_PRESS_TIMEOUT) // 等待连续按键超时,说明是按键单击 { wait_next_key_cnt = 0; // clear up_debounce_cnt = 0; // clear down_debounce_cnt = 0; // clear key_scan_step = 0; // 回到step0,检测新按键 beep_update = 1; // 通知beep_task 有新的按键事件 } } break; } } /* * beep_tast 收到beep_update != 0的更新通知后, * 就会检查key_press_cnt 从而知道是单击还是双击 */ void beep_task(void) { if(beep_update == 0) return; beep_update = 0; // clear if(key_press_cnt == 1) GPIO_WriteHigh(GPIOD, GPIO_PIN_4); // PD4 输出高电平,停止蜂鸣器 else if(key_press_cnt == 2) GPIO_WriteLow(GPIOD, GPIO_PIN_4); // PD4 输出低电平,蜂鸣器鸣叫 key_press_cnt = 0; // clear } void main(void) { // 关中断 disableInterrupts(); // 系统时钟配置 clk_config(); // PB4, key1, input GPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_IN_FL_NO_IT); // PD4, beep, output GPIO_Init(GPIOD, GPIO_PIN_4, GPIO_MODE_OUT_PP_HIGH_SLOW); // 开中断 enableInterrupts(); /* Infinite loop */ while (1) { key_scan_task(); beep_task(); } } 工程代码请到 http://pan.baidu.com/s/1eRuuaEu 下载 |
|
|
|
|
|
|
|
|
|
|
|
发烧级合格的
|
|
|
|
|
|
2234 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
2053 浏览 3 评论
4664 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
2197 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
2734 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 16:32 , Processed in 1.207871 second(s), Total 76, Slave 68 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号