模吧

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

895查看 | 0回复

使用操作系统点亮LED灯 来自知乎李德强

[复制链接]
发表于 2022-11-22 21:59:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
自制小四轴-7-使用操作系统点亮LED灯 使用操作系统点亮LED灯   来自知乎李德强 四轴,AI,app,PCB 作者:15519743871 6237
李德强

欢迎关注公众号“编程外星人”













公众号:编程外星人
配置并自动生成代码之后,我们就可以根据自己的需要完成相关的功能开发了。然而传统的单片机开发的方式通常是使用一个main()主函数中的一个while循环(死循环)来完成整个开发过程。就好像我们使用CubeIDE工具生成的源代码中一样,main()主函数中有一个永远不会终止的while(1)循环来使得单片机处理器始终处于运行状态:
/** * @brief  The application entry point. * @retval int */int main(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */  HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */  SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */  MX_GPIO_Init();/* USER CODE BEGIN 2 *//* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1)  {/* USER CODE END WHILE *//* USER CODE BEGIN 3 */  }/* USER CODE END 3 */}
我们可以很清楚的看到while(1)是一个死循环,我们可以将所有需要开发的功能都写入这这个循环当中去。然而与具有多线程的操作系统相比,这样的单任务死循环方式在使用和开发时还是有一些不方便的地方,因此我们需要将我们的工程中加入一个嵌入式操作系统,来完成我们的开发需要。在这里我们选用的操作系统为笔者自行开发的Dolphin-OS,这一款支持多线程、信号量、堆内存资源申请与释放、统一读写接口的操作系统。其功能简约,性能较好,且是实时操作系统,使用起来也比较方便。关于Dolphin-OS相关内容请读者参考《自制嵌入式实时操作系统》。
这里,我们将直接把操作系统相关的代码加入到我们的工程中,参与整个工程的编译中去,从而完成操作系统的特定功能。我们把Dolphin-OS操作系统(后续简称为OS)源代码下载并解压之后,可以得到一个名为kernel的文件夹,其中包含了OS的全部源代码,如下:
使用操作系统点亮LED灯   来自知乎李德强 四轴,AI,app,PCB 作者:15519743871 5360
我们需要把这个文件夹加入到我们在上一节中所生成的工程中去,或直接使用一个新的Makefile工程。我们选择了后者,我们在Linux下使用Makefile来编译一个全新的工程。我们需要把使用CubeIDE工具生成的源代码复制到一个新的目录中,并加入我们的kernel操作系统源代码,如下:
使用操作系统点亮LED灯   来自知乎李德强 四轴,AI,app,PCB 作者:15519743871 1144
其中,src文件夹中的main.c、stm32f1xx_hal_msp.c、stm32f1xx_it.c、syscalls.c、sysmem.c、system_stm32f1xx.c、startup_stm32f103c8tx.s、以及ld链接文件都从前面所讲述的CubeIDE所生成的代码中复制过来的。此外还包括Hal库的一些源代码,我们把它们存放到了libs目录里。
之后,我们需要将OS的启用和配置加入到我们的工程中,首先需要在main.c中加入OS的头文件包含和OS的启动代码:
#include <core.h>#include <sche.h>int main(void){  HAL_Init();  SystemClock_Config();  //其它代码省略  kernel_startup();  _kernel_startup = 1;  while (1)  {  }
之后,我们需要在stm32f1xx_it.c系统时钟中断处理函数中加入我们的OS调度函数,如下:
void SysTick_Handler(void){  HAL_IncTick();  if (_kernel_startup)  {    sche_tick();  }}
这里我们使用的OS调度频率为1000Hz,也就是说OS每经过1MS会尝试进行一次调度。
最后,我们给出整个工程的Makefile内容,如下:
ROOTPATH  =  ../../#------------------------------------------------------------------------## OS ArchOS_ARCH    =  arm/armv7-m#------------------------------------------------------------------------## Output filesELF_FILE  =  dolphin-os.elfBIN_FILE  =  dolphin-os.binHEX_FILE  =  dolphin-os.hexINFO_FILE  =  dolphin-os.infoCODE_FILE  =  dolphin-os.code#------------------------------------------------------------------------## Cross CompilerCC_PRE    =  /data/app/local/gcc-arm-none-eabi-5_4-2016q3/bin/arm-none-eabiCC      =  $(CC_PRE)-gccOBJCOPY    =  $(CC_PRE)-objcopyOBJDUMP    =  $(CC_PRE)-objdumpREADELF    =  $(CC_PRE)-readelf#------------------------------------------------------------------------## Flags#CFLAGS    +=  -DDEBUGCFLAGS    +=  -Os -ffunction-sections -fdata-sectionsCFLAGS    +=  -std=gnu11 -DUSE_HAL_DRIVER -DSTM32F103xBCFLAGS    +=  -mcpu=cortex-m3 --specs=nosys.specs CFLAGS    +=  -Wl,--gc-sections -static --specs=nano.specs CFLAGS    +=  -mfloat-abi=soft -mthumb -Wl,--start-group -lc -lm -Wl,--end-groupCFLAGS    +=  -T"dolphin-os.ld"# LibrariesSTM32_LIBS  =  libs/STM32F1xx_HAL_DriverCFLAGS    +=  -I$(STM32_LIBS)/IncCFLAGS    +=  -Ilibs/CMSIS/IncludeCFLAGS    +=  -Ilibs/CMSIS/Device/ST/STM32F1xx/Include#------------------------------------------------------------------------## Kernel PathKERNEL_PATH  =  $(ROOTPATH)/kernelCFLAGS    +=  -I$(KERNEL_PATH)/kernelCFLAGS    +=  -I$(KERNEL_PATH)/libCFLAGS    +=  -I$(KERNEL_PATH)/fs#------------------------------------------------------------------------## Src PathSRCS    =  ./srcCFLAGS    +=  -I$(SRCS)CFLAGS    +=  -I./inc#------------------------------------------------------------------------## Drivers PathDRVS    =  $(ROOTPATH)/driversCFLAGS    +=  -I$(DRVS)CFLAGS    +=  -I$(DRVS)/led#------------------------------------------------------------------------## Modules PathMODULES    =  $(ROOTPATH)/modulesCFLAGS    +=  -I$(MODULES)CFLAGS    +=  -I$(MODULES)/led#------------------------------------------------------------------------## Libs PathLIBS    =  $(ROOTPATH)/libsCFLAGS    +=  -I$(LIBS)#------------------------------------------------------------------------## Main BoardSRC      +=  $(SRCS)/main.c#------------------------------------------------------------------------## DriversSRC      +=  $(DRVS)/led/led.c#------------------------------------------------------------------------## ModulesSRC      +=  $(MODULES)/led/led_task.c#------------------------------------------------------------------------## LibsSRC      +=  $(LIBS)/buff_s.c \$(LIBS)/crc.c \$(LIBS)/protocol.c \$(LIBS)/rand.c#------------------------------------------------------------------------## SystemSRC      +=  ./src/system_stm32f1xx.c \        ./src/sysmem.c \        ./src/syscalls.c \        ./src/stm32f1xx_it.c \        ./src/stm32f1xx_hal_msp.cSTARTUP    =  ./src/startup_stm32f103c8tx.s#------------------------------------------------------------------------## Hal LibrariesSRC      +=  $(STM32_LIBS)/Src/stm32f1xx_hal.c \$(STM32_LIBS)/Src/stm32f1xx_hal_cortex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_adc.c \$(STM32_LIBS)/Src/stm32f1xx_hal_adc_ex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_dma.c \$(STM32_LIBS)/Src/stm32f1xx_hal_exti.c \$(STM32_LIBS)/Src/stm32f1xx_hal_flash.c \$(STM32_LIBS)/Src/stm32f1xx_hal_flash_ex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_gpio.c \$(STM32_LIBS)/Src/stm32f1xx_hal_gpio_ex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_i2c.c \$(STM32_LIBS)/Src/stm32f1xx_hal_pwr.c \$(STM32_LIBS)/Src/stm32f1xx_hal_rcc.c \$(STM32_LIBS)/Src/stm32f1xx_hal_rcc_ex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_spi.c \$(STM32_LIBS)/Src/stm32f1xx_hal_tim.c \$(STM32_LIBS)/Src/stm32f1xx_hal_tim_ex.c \$(STM32_LIBS)/Src/stm32f1xx_hal_uart.c#------------------------------------------------------------------------## OS ArchSRC      +=  $(KERNEL_PATH)/arch/$(OS_ARCH)/stack.c \$(KERNEL_PATH)/arch/$(OS_ARCH)/switch.S#------------------------------------------------------------------------## OS KernelSRC      +=  $(KERNEL_PATH)/kernel/core.c \$(KERNEL_PATH)/kernel/pcb.c \$(KERNEL_PATH)/kernel/sche.c \$(KERNEL_PATH)/kernel/sem.c \$(KERNEL_PATH)/kernel/top.c \$(KERNEL_PATH)/fs/vfs.c \$(KERNEL_PATH)/fs/fs.c \$(KERNEL_PATH)/fs/fcntl.c \$(KERNEL_PATH)/lib/list.cSTARTUP_OBJ  =  startup_stm32f103c8tx.oall:$(BIN_FILE) $(HEX_FILE) $(INFO_FILE) $(CODE_FILE)$(BIN_FILE):$(ELF_FILE)$(OBJCOPY) -O binary $^ $@$(HEX_FILE):$(ELF_FILE)$(OBJCOPY) -O ihex $^ $@$(INFO_FILE):$(ELF_FILE)$(READELF) -a $^ > $@$(CODE_FILE):$(ELF_FILE)$(OBJDUMP) -S $^ > $@$(STARTUP_OBJ):$(STARTUP)$(CC) $(CFLAGS) $^ -c $(STARTUP)$(ELF_FILE):$(SRC) $(STARTUP_OBJ)$(CC) $(CFLAGS)  $^ -o $@install:#@st-flash write $(BIN_FILE) 0x8000000  jlink -NoGui -commanderscript loadbin_cmd.jlinkclean:  @rm -rvf *.o *.bin *.code *.elf *.hex *.info
在这里我们使用了一个LED灯闪烁来确认OS是否已经正常工作了,接下来我们需要讲述这个LED闪烁的程序是如何工作的。
我们已经将工程中加入了一个多线程并发操作系统Dolphin-OS,我们可以使用这个OS来完成多线程程度的并发执行,所谓并发就是在宏观上多个任务好像在同时运行一样。用户实际上也感受不到任务的延时与停顿。而在微观上讲多个任务其实是以分片时间段轮流执行的,也就是说OS会在每隔一小段时间就会切换一次任务并执行,但这个切换的过程非常快,通常每秒可切换几百或上千次,我们的OS是的调度频率为1000Hz,也就是说它可以在一秒内切换1000次任务。这在宏观上是完全感觉不到的,我们可以认为多个线程之间是同时执行的。也就是我们常常说的并发执行。
为了验证我们的OS工作正常,我们首先需要完成一个LED闪烁的功能,它是一个在OS下可以正常被调度和执行的线程。首先我们可以先完成一个LED驱动程序,这个LED驱动程序对外提供了LED的点亮、熄灭、闪烁等操作,具体源代码实现如下,我们在drivers文件夹中加入一个led的文件夹,在里面完成led.c和led.h两个驱动文件:
使用操作系统点亮LED灯   来自知乎李德强 四轴,AI,app,PCB 作者:15519743871 134 #include <led.h>void led_on(void){  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);}void led_off(void){  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);}void led_blink(void){  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_11);}
可以看到,实际上我们只是完成了几个LED的功能函数,使LED点亮、熄灭和闪烁,就是使用的HAL_GPIO_WritePin()函数和HAL_GPIO_TogglePin()函数来操作的GPIO脚,而我们这所以要把GPIO脚的操作封装成led_on()、led_off()、led_blink()函数,就是想让我们的代码尽可能的脱离对硬件的依赖,而方便移植到其它平台当中去。
接下来,我们还需要在moudles文件夹下创建一个led文件夹,在里面完成我们的LED控制线程:
使用操作系统点亮LED灯   来自知乎李德强 四轴,AI,app,PCB 作者:15519743871 6030 #include <led.h>#include <led_task.h>uint8_t val = 0x05;void* led_pthread(void* arg){   for (uint8_t i = 0;; i++)   {    if ((val >> (i % 8)) & 0x1)    {      led_on();    }    else    {      led_off();    }    sleep_ticks(125);  }  return NULL;}void led_task(void){  pcb_create(4, &led_pthread, NULL, 600);}

我们使用了OS中的pcb_create()函数为我们的LED功能创建了一个可调度的线程,这个线程的入口函数为led_phtread(),它的功能是每间隔1秒就使led双闪一次。
关于这个双闪功能,我们可以再介绍一下。如果我们想让一个LED灯进行双闪,通常的做法是上LED亮一次,然后熄灭,然后再亮一次,然后再熄灭,不过第二次熄灭时需要等待的时间稍微长一点,如此循环。而我们可以采用一种较为优雅的方式来完成,我们使用一个uint8_t val变量来存放一个值为0x05的数,它的二进制表示为:0000 0101
我们可以在每一个循环中使用位运算判断其某个比特位上是0还是1,如果是0则熄灭,是1则点亮。而每一次循环我们所判断的bit位向右移动一次,这样我们就完成了LED双闪的功能代码,这样我们即省略了每次等待的时间,又简化了功能代码。
最后,我们只需要在main()函数中调用我们的led_task()函数就可以让我们的LED进行双闪了。如下:
#include "led_task.h"#include <core.h>#include <sche.h>int _kernel_startup = 0;int main(void){  HAL_Init();  SystemClock_Config();  MX_GPIO_Init();    kernel_startup();  led_task();  _kernel_startup = 1;  while (1)  {  }}
我们在Makefile目录下执行make命令即可将源代码编译为我们STM32F103C8T6所能使用的程序dolphin-os.bin,最后使用JLink烧录工具将程序烧录到STM32的芯片Flash中即可。





您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

QQ|关于模吧|APP下载|广告报价|手机版|企业会员|商城入驻|联系我们|模吧 ( 黔ICP备2022002348号-1 )

© 2013-2020 Moz8.com 模吧,玩出精彩!