stm8单片机点亮LED中断定时串口读写功能测试笔记

2019-04-15 16:33发布

class="markdown_views prism-github-gist"> STM8系列是意法半导体公司生产的8位的单片机。该型号单片机分为STM8A、STM8S、STM8L三个系列
使用测试例程, 测试例程均是来自于青风电子社区示例教程 这次例程使用的是 stm8s207 系列
用到的stm8s的IO定义以及函数封装来自 :stm8s.h、stm8s_clk.h、stm8s_gpio.h、stm8s_uart1.h
这里写图片描述
点亮LED灯并通过定时器进行500ms的频率闪烁 #include "stm8s.h" #include "stm8s_clk.h" #include "intrinsics.h" #include "led.h" #include "tim1.h" #include "server.h" void testDelay(); int main(void){ testDelay(); } void testDelay(){ CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); /*! LED_Init(); Tim1_Init(); LED1_Open(); Gpio_Led_Init(); __enable_interrupt(); while (1){ Gpio_Led_Reverse(); delay_ms(500); } } 这里用的函数
CLK_HSIPrescalerConfig()
配置HSI时钟驱动(HSI : High Speed Internal Clock Signal 内部高速时钟信号) LED_Init() 定义LED的管脚的模式 初始化对应的IO void LED_Init(void){ //定义LED的管脚的模式 GPIO_Init(LED1_PORT,LED1_PIN,GPIO_MODE_OUT_PP_HIGH_FAST ) GPIO_Init(LED2_PORT,LED2_PIN,GPIO_MODE_OUT_PP_HIGH_FAST ); GPIO_Init(LED3_PORT,LED3_PIN,GPIO_MODE_OUT_PP_HIGH_FAST ); } Tim1_Init() 启动中断的模式对内部时钟中断进行计数达到计时的功能 void Tim1_Init(void){ TIM1_TimeBaseInit(16,TIM1_COUNTERMODE_UP,1000,0);//16分频,向上计数,计数完了触发中断,如果想 //再 1MS 触发中断,则计数器应该计数 1000 次,1MHZ/1000=1KHZ,正好就是 1ms。 TIM1_ARRPreloadConfig(ENABLE);//使能自动重装 TIM1_ITConfig(TIM1_IT_UPDATE , ENABLE);//数据更新中断 TIM1_Cmd(ENABLE);//开定时器 } 定时器中断的计算方式:
32.768KHZ是标准的计时参考方案。 (百度百科)
32.768khz晶振是常用的时钟晶振,常用于手机 电脑 通信等
32.768KHz=2^15=32768Hz
所以16分频刚好是1Hz=1周期/秒
重装载之后的定时器的中断周期就是每秒中断1000次 就是每次中断为1毫秒
所以 TimingDelay_Decrement() 就是每1毫秒被调用一次 delay_ms() 函数的工作原理 static u32 TimingDelay; void TimingDelay_Decrement(void){ if (TimingDelay != 0x00){ TimingDelay--; } } void delay_ms( u32 nTime){ TimingDelay = nTime; while(TimingDelay != 0); } 而其中的 TimingDelay_Decrement() 函数是在中断函数中被调用
drive/stm8s_interruput.c中 #pragma vector=0xD __interrupt void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void) { TimingDelay_Decrement(); TIM1_ClearITPendingBit(TIM1_IT_UPDATE); } Gpio_Led_Init() 初始化GPIO口 对于的IO口上接了一个LED灯 void Gpio_Led_Init(){ GPIO_Init(GPIOD,GPIO_PIN_2,GPIO_MODE_OUT_PP_HIGH_FAST); } __enable_interrupt() 使中断进入工作状态 必须启动中断的工作模式 否则时钟中断不能正常工作
在 intrinsics.h 中定义 D:Program Files (x86)IAR SystemsEmbedded Workbench 7.3stm8inccintrinsics.h 按键动作检测功能 分别使用中断模式和轮询方式进行测试控制LED灯 #include "led.h" #include "tim1.h" #include "server.h" void testKeyInterrupt(); int main(void){ testKeyInterrupt(); } void testKeyInterrupt(){ Clk_Init(); Gpio_Led_Init(); __enable_interrupt(); Gpio_Key_Init(true); while(1); } #pragma vector = 7 __interrupt void EXIT_PORTC_IRQHander(void){ if(GPIO_ReadInputPin(GPIOC,GPIO_PIN_1) == Key_Down){ while(GPIO_ReadInputPin(GPIOC,GPIO_PIN_1) == Key_Down); Gpio_Led_Open(); __enable_interrupt(); delay_ms(1000); }else if(GPIO_ReadInputPin(GPIOC,GPIO_PIN_2) == Key_Down) { while(GPIO_ReadInputPin(GPIOC,GPIO_PIN_2) == Key_Down); Gpio_Led_Close(); __enable_interrupt(); delay_ms(1000); } } 根据例程中的配置 测试出来7是我配置的GPIO外部中断的向量值 上面定时器中使用的内部时钟中断向量值则是0xD
这里配置中断回调函数的语法是
1 关键字#pragma vector=7
2 函数的命名使用__interrupt修饰符 单片机的中断函数与我们普遍认为的基于事件触发回调函数的语法稍微不同 (回调函数是要设置函数指针的)
单片机的语法中就是直接使用关键字 vector 配置(修饰)好中断向量的值即可
使用关键修饰这点有些像安卓开发中的注解机制 // android 下的控件注解方式对控件进行实例化 @ViewInject(R.id.buy) private Button buy; 这里需要注意的是一个工程中不允许重复定义同一个向量的中断服务函数 Error[Li006]: duplicate definitions for "_interrupt_7"; in "D:Stm8_projectQF-stm8-masteruserDebugObjmain.o", and "D:Stm8_projectQF-stm8-masteruserDebugObjstm8s_interruput.o" Error while running Linker 所以当我这里把中断服务函数放在了main.c文件里面处理后,
需要把 stm8s_interruput.c 文件中的中断向量为 7 的中断服务函数注释掉 这里涉及到中断向量和中断向量表的概念 中断向量
是指中断服务程序入口地址的偏移量与段基值,一个中断向量占据4字节空间; 中断向量表
是8088系统内存中最低端1K字节空间
它的作用就是按照中断类型号从小到大的顺序存储对应的中断向量,总共存储256个中断向量; 这里在每次中断收到外部IO中断之后再次调用了 __enable_interrupt()
是因为在使用过程中出现因为外部中断导致了内部的时钟中断不再工作 需要重新调用 __enable_interrupt() 才能开始定时工作
产生问题应该是在产生外部中断时 停掉了内部时钟中断的正常工作 。 使用轮询的方式检查按键就比较简单了 #include "stm8s.h" #include "stm8s_clk.h" #include "led.h" #include "server.h" #include "tim1.h" int main(void){ CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); Clk_Init(); Gpio_Led_Init(); Gpio_Key_Init(true); while(1){ if(GPIO_ReadInputPin(GPIOC,GPIO_PIN_1) == Key_Down){ while(GPIO_ReadInputPin(GPIOC,GPIO_PIN_1) == Key_Down); Gpio_Led_Open(); }else if(GPIO_ReadInputPin(GPIOC,GPIO_PIN_2) == Key_Down) { while(GPIO_ReadInputPin(GPIOC,GPIO_PIN_2) == Key_Down); Gpio_Led_Close(); } } } 串口数据的读写功能 分别使用轮询方式和中断模式进行测试 #include "led.h" #include "tim1.h" #include "server.h" void testComSendRev(); static uint8_t Tx_Buffer[] = "hello[x]"; int main(void){ testComSendRev(); } void testComSendRev(){ /* 设置内部高速时钟16M为主时钟 */ CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); Tim1_Init(); __enable_interrupt(); InitUart1((u32)115200, FALSE); uint8_t recBuf; while (1){ recBuf = Uart1RecvData(); Tx_Buffer[6] = recBuf; Uart1SendData( Tx_Buffer, countof(Tx_Buffer)-1); delay_ms(10); } } 读串口有两种模式 一种是阻塞读取 另外一种是外部中断读取
上面的是阻塞读取方式,来看看中断模式下的读取数据 继续来找找中断向量了 就是 0x14 (例程中给出来的)
千万不要忘记到 stm8s_interruput.c 文件中把中断向量为 0x14 的中断服务函数注释掉。 #include "stm8s.h" #include "stm8s_clk.h" #include "led.h" #include "server.h" static uint8_t Tx_Buffer[] = "hello[x]"; int main(void){ /*设置内部高速时钟16M为主时钟*/ CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); LED_Init(); InitUart1((u32)115200, TRUE);//USART_Configuration();//串口配置 enableInterrupts(); //开启中断 while (1); } #pragma vector=0x14 __interrupt void UART1_RX_IRQHandler(void){ uint8_t Recvbuf; while (UART1_GetFlagStatus(UART1_FLAG_RXNE) == RESET);//接收数据寄存器非空 Recvbuf = UART1_ReceiveData8(); Tx_Buffer[6] = Recvbuf; Uart1SendData( Tx_Buffer, countof(Tx_Buffer)-1); return; } 这里写图片描述 查看源码时替换掉 main.c 文件中的代码运行测试即可 点击查看完成的工程源码