“NBDK-L4:LiteOS实验教程”的版本间的差异
(→工程简介) |
(→los_led_entry.c) |
||
(未显示同一用户的10个中间版本) | |||
第16行: | 第16行: | ||
在华为LiteOS源码及说明的链接中,有给大家说明如何移植华为LiteOS。 | 在华为LiteOS源码及说明的链接中,有给大家说明如何移植华为LiteOS。 | ||
− | + | 因为LiteOS是持续更新的,所以移植起来不是特别方便(目录架构总在变),我们建议大家可以先略过这一步骤,直接使用我们移植好的工程去进行需求开发。 | |
3.如何使用LiteOS开发我们的功能 | 3.如何使用LiteOS开发我们的功能 | ||
第31行: | 第31行: | ||
=== 工程目录简介 === | === 工程目录简介 === | ||
− | + | 大家打开任意一个LiteOS例程,都会看到如下的4个目录(Drivers、LiteOS、MDK-ARM、Src)及clean.bat文件。 | |
− | + | 其中'''<big>clean.bat</big>'''是用于清除工程编译生成的中间文件。例如我们想拷贝一个编译过的工程,工程有200M左右大小,我们点击clean.bat清除一下编译生成的中间文件,则工程大概会缩小到100M左右,此时工程只剩下了库文件、用户文件,以及编译生成的hex文件。[[文件:NBDK-LOS-TAB-MPath.png|边框|居中|无框|901x901像素]]从上图可以看到,四个主目录下分别包含的一些文件,这边给大家简单的介绍一下这边文件大概的功能。 | |
− | + | '''<big>Drivers:</big>''' | |
− | + | STM32驱动文件目录,也就是大家常说的hal库,里面包含了hal(硬件抽象层)相关的文件。 | |
− | + | 主要就是有RCC时钟、Flash内存,以及大家常用的外设(例如uart、spi、adc等等)的一些库文件。 | |
− | === | + | '''<big>LiteOS:</big>''' |
+ | |||
+ | 华为LiteOS操作系统目录,里面包含了两个目录:arch、kernel。 | ||
+ | |||
+ | 这两个目录就是我们移植到STM32cube工程中的LiteOS相关文件。 | ||
+ | |||
+ | '''<big>MDK-ARM:</big>''' | ||
+ | |||
+ | 工程目录,主要是两个工程文件“.uvoptx”以及".uvprojx"(keil打开的是这个)。剩下的文件比较重要的是Output目录下编译生成的“.hex”文件。 | ||
+ | |||
+ | '''<big>Src:</big>''' | ||
+ | |||
+ | 用户文件,用户自己开发的一些驱动文件(外设驱动等等),以及main文件所在的目录。 | ||
+ | |||
+ | === 实验01-TFT显示屏 === | ||
+ | LiteOS的第一个实验,我们给大家带来的是LCD图形化显示实验。至于我们为什么要首先讲解LCD相关的例程,而不是最简单的LED控制实验,主要目的有三个: | ||
+ | |||
+ | 1.大家了解了LCD例程之后,对于后面的其他实验,可以使用LCD显示一些调试信息,这样便利于大家的开发。 | ||
+ | |||
+ | 2.我们会借由此例程,给大家讲解LiteOS例程在Keil5中的目录结构,以及我们开发者需要关注的重要文件及重要功能函数。 | ||
+ | |||
+ | 3.借由此例程,给大家展示keil5 options中的配置。 | ||
+ | |||
+ | === 工程文件说明 === | ||
+ | 这一章节我们给大家介绍一下,每个工程的Group目录以及其下包含的文件的功能。 | ||
+ | |||
+ | [[文件:NBDK-LOS-KEIL-PRO.png|边框|无框|566x566像素]] | ||
+ | {| class="wikitable" | ||
+ | |+KEIL工程文件简介 | ||
+ | !Group | ||
+ | !file | ||
+ | !说明 | ||
+ | |- | ||
+ | | rowspan="5" |'''LiteOS/arch''' | ||
+ | | colspan="2" |'''arch目录取名来源于architecture(建筑结构),所包含的是硬件配置文件''' | ||
+ | |- | ||
+ | |los_dispatch_keil.c | ||
+ | |keil中los的系统调度文件 | ||
+ | |- | ||
+ | |los_hw.c | ||
+ | |hardware硬件配置文件 | ||
+ | |- | ||
+ | |los_hw_tick.c | ||
+ | |hardware tick硬件系统tick文件 | ||
+ | |- | ||
+ | |los_hwi.c | ||
+ | |hardware interrupt硬件中断文件 | ||
+ | |- | ||
+ | | rowspan="18" |'''LiteOS/kernel''' | ||
+ | | colspan="2" |'''los内核''' | ||
+ | |- | ||
+ | |cmsis_liteos.c | ||
+ | |los提供的CMSIS接口 | ||
+ | |- | ||
+ | |los_config.c | ||
+ | |los配置文件,用于初始化并启动los | ||
+ | |- | ||
+ | |los_priqueue.c | ||
+ | |priority queue,los队列优先级处理 | ||
+ | |- | ||
+ | |los_swtmr.c | ||
+ | |software timer,los软件定时器 | ||
+ | |- | ||
+ | |los_sys.c | ||
+ | |系统tick | ||
+ | |- | ||
+ | |los_task.c | ||
+ | |用于创建和处理任务 | ||
+ | |- | ||
+ | |los_tick.c | ||
+ | |仅包含了tick中断函数 | ||
+ | |- | ||
+ | |los_timeslice.c | ||
+ | |时间片初始化和设置 | ||
+ | |- | ||
+ | |los_event.c | ||
+ | |事件 | ||
+ | |- | ||
+ | |los_mux.c | ||
+ | |互斥锁 | ||
+ | |- | ||
+ | |los_queue.c | ||
+ | |队列 | ||
+ | |- | ||
+ | |los_sem.c | ||
+ | |信号量 | ||
+ | |- | ||
+ | |los_membox.c | ||
+ | |内存池初始化、分配、清除 | ||
+ | |- | ||
+ | |los_memory.c | ||
+ | |内存节点申请、清理、优化处理 | ||
+ | |- | ||
+ | |los_memstat.c | ||
+ | |内存状态 | ||
+ | |- | ||
+ | |los_muitipledlinkhead.c | ||
+ | |多内存处理 | ||
+ | |- | ||
+ | |los_misc.c | ||
+ | |包含了两个功能函数,字节对齐、当前任务休眠 | ||
+ | |- | ||
+ | |'''Driver/STM32L4xx_hal_driver''' | ||
+ | | colspan="2" |'''和硬件相关的HAL抽象层文件,主要是电源、flash、外设。HAL不清楚如何使用的,大家可以先看下基础实验部分。''' | ||
+ | |- | ||
+ | | rowspan="2" |'''Driver/CMSIS''' | ||
+ | | colspan="2" | '''Cortex-M 处理器系列的与供应商无关的硬件抽象层''' | ||
+ | |- | ||
+ | |system_stm32l4xx.c | ||
+ | |STM32L4系列硬件抽象层文件 | ||
+ | |- | ||
+ | | rowspan="2" |'''startup''' | ||
+ | | colspan="2" |'''系统启动文件''' | ||
+ | |- | ||
+ | |los_startup_keil.s | ||
+ | |los在keil中的启动项文件 | ||
+ | |- | ||
+ | |'''Application/Display''' | ||
+ | | colspan="2" |'''TFT彩屏驱动,以及GUI图形界面驱动文件''' | ||
+ | |- | ||
+ | | | ||
+ | |gui.c | ||
+ | |GUI图形界面文件 | ||
+ | |- | ||
+ | | | ||
+ | |gui_dispstr.c | ||
+ | |GUI字符串打印 | ||
+ | |- | ||
+ | | | ||
+ | |gui_dispval.c | ||
+ | |GUI值打印 | ||
+ | |- | ||
+ | | | ||
+ | |gui_drawBitmap.c | ||
+ | |GUI图形打印 | ||
+ | |- | ||
+ | | | ||
+ | |gui_os.c | ||
+ | |GUI操作系统任务 | ||
+ | |- | ||
+ | | | ||
+ | |lcd.c | ||
+ | |TFT驱动文件 | ||
+ | |- | ||
+ | | | ||
+ | |bmp_keyio.c | ||
+ | |按键图形界面 | ||
+ | |- | ||
+ | | | ||
+ | |bmp_logo.c | ||
+ | |谷雨logo图形界面 | ||
+ | |- | ||
+ | | | ||
+ | |Font8x16.c | ||
+ | |描述8*16大小的常见英文字符 | ||
+ | |- | ||
+ | | | ||
+ | |FontMethod.c | ||
+ | |定义英文字体的通用函数文件 | ||
+ | |- | ||
+ | | | ||
+ | |FontZH16x16_simsun.c | ||
+ | |描述16*16大小的中文字符 | ||
+ | |- | ||
+ | | | ||
+ | |FontZHMethod.c | ||
+ | |定义中文字体的通用函数文件 | ||
+ | |- | ||
+ | | | ||
+ | |lcddrv_tft_130 | ||
+ | |TFT屏驱动文件,1.3型号 | ||
+ | |- | ||
+ | | | ||
+ | |lcddrv_tft_144 | ||
+ | |TFT屏驱动文件,1.44型号 | ||
+ | |- | ||
+ | | | ||
+ | |lcd_gpio.c | ||
+ | |TFT屏BL、CD、CS引脚控制 | ||
+ | |- | ||
+ | | | ||
+ | |lcd_hw_cfg.c | ||
+ | |TFT屏硬件接口定义 | ||
+ | |- | ||
+ | | | ||
+ | |lcd_spi.c | ||
+ | |TFT屏SPI接口控制 | ||
+ | |- | ||
+ | |'''Application/bsp''' | ||
+ | | colspan="2" |'''Board Support Package 板级支持包,结合STM32L4平台的外设驱动''' | ||
+ | |- | ||
+ | | | ||
+ | |los_bsp_adapter.c | ||
+ | |LOS和STM32的适配文件 | ||
+ | |- | ||
+ | | | ||
+ | |los_bsp_lcd.c | ||
+ | |LCD驱动文件,结合STM32硬件 | ||
+ | |- | ||
+ | |'''Application/task''' | ||
+ | | colspan="2" |'''用户任务,用户个人创建的task''' | ||
+ | |- | ||
+ | |'''Application/user''' | ||
+ | | colspan="2" |'''用户文件,main函数、系统时钟、系统外设宏定义''' | ||
+ | |- | ||
+ | | | ||
+ | |stm32l4xx_it.c | ||
+ | |STM32中断函数文件 | ||
+ | |- | ||
+ | | | ||
+ | |sys_init.c | ||
+ | |STM32系统时钟以及外设时钟定义文件 | ||
+ | |- | ||
+ | | | ||
+ | |main.c | ||
+ | |用于初始化los并运行,并且初始化和定义用户任务 | ||
+ | |} | ||
+ | |||
+ | === 实验验证 === | ||
+ | 编译并下载此例程,由于涉及的库文件较多,编译将持续1min以上,请大家耐心等待。 | ||
+ | |||
+ | 下载完成后,可以看到TFT显示屏打印“LiteOS”、"01_los_lcd"字样。 | ||
+ | |||
+ | === 源码详解 === | ||
+ | 源码详解部分,第一个这个LCD部分,我们会按照基础实验部分的源码详解方式说明,后续的其他实验,我们仅讲解相对此例程新增的功能部分,所以请大家一定仔细阅读此例程说明。 | ||
+ | |||
+ | ==== stm32l4xx_hal_conf.h ==== | ||
+ | 此文件位于“实验01-TFT显示屏\Src\User”路径中,主要用途是选择使能此例程使用到的HAL库文件。 | ||
+ | |||
+ | 使用STM32,大家默认需要打开的几个宏定义,芯片使能、flash、电源、时钟、NVIC,这个也很好理解,就是让芯片工作的必要几点。 | ||
+ | |||
+ | 此例程我们给大家展示的是TFT显示屏,所以我们需要请打开驱动TFT工作的SPI、GPIO以及DMA三个宏。其他的外设例程也需要使能对应的宏,这个大家有使用的疑问,可以查找一下基础实验中的相应的外设例程。<syntaxhighlight lang="c++" line="1" start="103"> | ||
+ | // 使能的宏 | ||
+ | #define HAL_MODULE_ENABLED // 芯片 | ||
+ | #define HAL_FLASH_MODULE_ENABLED // Flash | ||
+ | #define HAL_PWR_MODULE_ENABLED // 电源 | ||
+ | #define HAL_RCC_MODULE_ENABLED // 时钟 | ||
+ | #define HAL_CORTEX_MODULE_ENABLED // NVIC | ||
+ | |||
+ | #define HAL_GPIO_MODULE_ENABLED // GPIO | ||
+ | #define HAL_DMA_MODULE_ENABLED // DMA | ||
+ | #define HAL_SPI_MODULE_ENABLED // SPI | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== sys_init.c ==== | ||
+ | 时钟初始化函数,用于配置我们模块运行的系统时钟、AHB高性能总线时钟、APB外设总线时钟以及单个外设的时钟。 | ||
+ | |||
+ | 主要包含了三个部分的初始化配置。 | ||
+ | # 内部或者外部振荡器选择,也就是选择时钟信号的来源,是内部振荡,还是外部晶振。 | ||
+ | # 时钟配置,选择系统、AHB总线及APB总线的时钟来源。 | ||
+ | # 外设时钟配置,选择外设时钟来源。 | ||
+ | 为了给大家比较全面的展示各个时钟,我们振荡器选择HSI(内部16MHz高频)、HSE(外部8MHz高频)以及LSE(外部32.768KHz低频)三个。选择HSE作为PLL(锁相回路)时钟源,配置PLLCLK为80MHz。配置系统时钟SYSCLK、AHB高性能总线、APB外设总线(APB1及APB2)为80MHz。另外我们还分别配置了ADC、UART以及I2C的外设时钟。 | ||
+ | |||
+ | 基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。<syntaxhighlight lang="c++" line="1" start="41"> | ||
+ | //****************************************************************** | ||
+ | // fn : SystemClock_Config | ||
+ | // | ||
+ | // brief : 系统时钟配置函数 | ||
+ | // | ||
+ | // param : none | ||
+ | // | ||
+ | // return : none | ||
+ | void SystemClock_Config(void) | ||
+ | { | ||
+ | RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体 | ||
+ | RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体 | ||
+ | RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体 | ||
+ | |||
+ | // 配置LSE驱动器功能为低驱动能力 | ||
+ | __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW); | ||
+ | |||
+ | // 初始化CPU,AHB和APB总线时钟 | ||
+ | RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE | ||
+ | |RCC_OSCILLATORTYPE_LSE; // 设置需要配置的振荡器为HSI、HSE、LSE | ||
+ | // 配置HSE | ||
+ | RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz) | ||
+ | // 配置LSE | ||
+ | RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动) | ||
+ | // 配置HSI | ||
+ | RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟 | ||
+ | RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz | ||
+ | // 配置PLL | ||
+ | RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL | ||
+ | RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz | ||
+ | RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz | ||
+ | RCC_OscInitStruct.PLL.PLLN = 20; // 配置PLL VCO输入倍增为20,8MHz*20 = 160MHz | ||
+ | RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160/7 = 22.857143MHz | ||
+ | RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160/2 = 80MHz | ||
+ | RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160/2 = 80MHz | ||
+ | // RCC时钟配置,出错则进入错误处理函数 | ||
+ | if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | |||
+ | // 初始化CPU,AHB和APB总线时钟 | ||
+ | RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK | ||
+ | |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2 | ||
+ | RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,80MHz | ||
+ | RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80/1 = 80MHz | ||
+ | RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80/1 = 80MHz | ||
+ | RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80/1 = 80MHz | ||
+ | // RCC时钟配置,出错则进入错误处理函数 | ||
+ | if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // HCLK=80MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4) | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | |||
+ | // 初始化外设时钟 | ||
+ | PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2 | ||
+ | |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1 | ||
+ | |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC | ||
+ | PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz | ||
+ | PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz | ||
+ | PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz | ||
+ | PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz | ||
+ | PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz | ||
+ | PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义 | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1 | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8 | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,64/2 = 32MHz | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz | ||
+ | PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz | ||
+ | // 外设时钟配置,出错则进入错误处理函数 | ||
+ | if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | |||
+ | // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz | ||
+ | if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) | ||
+ | { | ||
+ | _Error_Handler(__FILE__, __LINE__); | ||
+ | } | ||
+ | |||
+ | // 配置系统定时器中断时间,配置为HCLK的千分频 | ||
+ | HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); | ||
+ | |||
+ | // 配置系统定时器,配置为HCLK | ||
+ | HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); | ||
+ | |||
+ | // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0) | ||
+ | HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); | ||
+ | } | ||
+ | |||
+ | |||
+ | //****************************************************************** | ||
+ | // fn : _Error_Handler | ||
+ | // | ||
+ | // brief : 错误处理程序 | ||
+ | // | ||
+ | // param : none | ||
+ | // | ||
+ | // return : none | ||
+ | void _Error_Handler(char * file, int line) | ||
+ | { | ||
+ | // 暂时未处理任何事情,用户可用来添加自己的功能来报告HAL错误返回状态 | ||
+ | while(1) | ||
+ | { | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== main.c ==== | ||
+ | main.c文件中,我们只放置了3个函数,以后的所有例程也只有这3个函数,下面我们将简要说明各个函数的功能。 | ||
+ | |||
+ | 硬件初始化函数,顾名思义,就是我们初始化硬件驱动的函数,在这个实验中,我们初始化了LCD的驱动文件。<syntaxhighlight lang="c++" line="1" start="27"> | ||
+ | void HardWare_Init(void) | ||
+ | { | ||
+ | // 初始化LCD | ||
+ | BSP_EvbLcdInit(); | ||
+ | } | ||
+ | </syntaxhighlight>LiteOS任务初始化函数,我们会在这个函数中初始化我们用户新建的task任务,在这个TFT实验中,我们没有独立的任务,仅仅初始化了GUI图形化界面。<syntaxhighlight lang="c++" line="1" start="41"> | ||
+ | void LosTask_Init(void) | ||
+ | { | ||
+ | // 初始化图形接口 | ||
+ | GUI_Init(); | ||
+ | GUI_DispString("LiteOS"); | ||
+ | GUI_DispNextLine(); | ||
+ | GUI_DispString("01_los_lcd"); | ||
+ | } | ||
+ | </syntaxhighlight>main()函数中,我们首先初始化HAL和系统时钟,接着我们初始化了LiteOS内核,使能LiteOS系统tick中断,然后我们分别初始化了我们所使用的硬件驱动和los任务,最后我们开启运行los。<syntaxhighlight lang="c++" line="1" start="58"> | ||
+ | int main(void) | ||
+ | { | ||
+ | UINT32 uwRet; | ||
+ | |||
+ | // 初始化硬件 | ||
+ | HAL_Init(); | ||
+ | SystemClock_Config(); | ||
+ | |||
+ | // 初始化LOS内核 | ||
+ | uwRet = LOS_KernelInit(); | ||
+ | if (uwRet != LOS_OK) | ||
+ | { | ||
+ | return LOS_NOK; | ||
+ | } | ||
+ | |||
+ | // 使能LOS系统tick中断 | ||
+ | uwRet = LOS_EnableTick(); | ||
+ | if (uwRet != LOS_OK) | ||
+ | { | ||
+ | return LOS_NOK; | ||
+ | } | ||
+ | |||
+ | // 初始化开发板硬件 | ||
+ | HardWare_Init(); | ||
+ | |||
+ | // 初始化用户任务 | ||
+ | LosTask_Init(); | ||
+ | |||
+ | // 运行LOS | ||
+ | (void)LOS_Start(); | ||
+ | for(;;); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === los_bsp_lcd.c及gui.c等 === | ||
+ | los_bsp_lcd.c等同于基础实验TFT显示屏中的gyu_spi.c文件,gui.c等与基础实验TFT显示屏中的相同。大家可以参考基础实验,这边不再赘述。 | ||
+ | |||
+ | === gui_os.c === | ||
+ | gui_os.c文件,此文件将与具体的操作系统有关,在基础实验的Display和GUI中是没有的,所以这边给大家讲解一下。 | ||
+ | |||
+ | 在不同的操作系统中,开发者要根据操作系统要求完成对GUI_Lock,GUI_Unlock,GUIOS_Init函数进行修改。开发者只要完成信号量Semaphore或临界区即可。 | ||
+ | |||
+ | 永久阻塞模式申请信号量,用于阻塞有关显示屏GUI打印的信号量。<syntaxhighlight lang="c++" line="1" start="32"> | ||
+ | void GUI_Lock(void) | ||
+ | { | ||
+ | /*永久阻塞模式申请信号量*/ | ||
+ | LOS_SemPend(g_usDispSemID, LOS_WAIT_FOREVER); | ||
+ | } | ||
+ | </syntaxhighlight>释放信号量,用于释放有关显示屏GUI打印的信号量。<syntaxhighlight lang="c++" line="1" start="46"> | ||
+ | void GUI_Unlock(void) | ||
+ | { | ||
+ | /*释放信号量*/ | ||
+ | LOS_SemPost(g_usDispSemID); | ||
+ | } | ||
+ | </syntaxhighlight>创建信号量,用于创建一个有关显示屏GUI打印的信号量。<syntaxhighlight lang="c++" line="1" start="59"> | ||
+ | void GUIOS_Init(void) | ||
+ | { | ||
+ | /*创建信号量*/ | ||
+ | LOS_SemCreate(1,&g_usDispSemID); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == 实验02-led点灯 == | ||
+ | led点灯实验,我们在实验01的基础上,新增了使用LiteOS去创建一个独立的led task任务进程。 | ||
+ | |||
+ | 通过这个实验,大家需要掌握的是LiteOS Task任务的创建和使用。 | ||
=== 实验验证 === | === 实验验证 === | ||
=== 源码详解 === | === 源码详解 === | ||
+ | |||
+ | === main.c === | ||
+ | 在硬件初始化函数中,我们新增了LED硬件初始化部分。<syntaxhighlight lang="c++" line="1" start="27"> | ||
+ | void HardWare_Init(void) | ||
+ | { | ||
+ | // 初始化LCD | ||
+ | BSP_EvbLcdInit(); | ||
+ | |||
+ | // 初始化LED | ||
+ | BSP_EvbLedInit(); | ||
+ | } | ||
+ | </syntaxhighlight>在LiteOS 独立任务初始化函数中,我们新增加了LED独立任务。<syntaxhighlight lang="c++" line="1" start="44"> | ||
+ | void LosTask_Init(void) | ||
+ | { | ||
+ | // 初始化图形接口 | ||
+ | GUI_Init(); | ||
+ | GUI_DispString("LiteOS"); | ||
+ | GUI_DispNextLine(); | ||
+ | GUI_DispString("Led Test"); | ||
+ | |||
+ | // 初始化LED任务 | ||
+ | LOS_BoardLedEntry(); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === los_bsp_led.c === | ||
+ | 初始化LED硬件,文件中函数功能等同于LED基础实验中的gyu_led.c,不清楚的大家可以查看基础实验中的说明。 | ||
+ | |||
+ | === los_led_entry.c === | ||
+ | 此文件为本实验的重点内容,也就是我们使用LiteOS创建的独立LED控制任务。 | ||
+ | |||
+ | 基于los的LED独立任务创建函数,在创建一个task任务时,我们需要做如下步骤(结合具体的代码,不过单纯的框架步骤): | ||
+ | |||
+ | 1.使用TSK_INIT_PARAM_S结构体定义一个创建task任务的参数结构 | ||
+ | |||
+ | 2.清空任务参数内容 | ||
+ | |||
+ | 3.定义此任务的入口功能(用户任务处理的功能函数) | ||
+ | |||
+ | 4.分配任务栈的大小,默认给LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE大小 | ||
+ | |||
+ | 5.给当前任务取一个名称,此任务我们取名“LED-Demo” | ||
+ | |||
+ | 6.分配优先级,任务一共有32个优先级(0-31),最高优先级为0,最低优先级为31 | ||
+ | |||
+ | 7.锁任务调度,防止高优先级任务调度,影响任务正常创建 | ||
+ | |||
+ | 8.利用刚刚的任务参数创建新的任务,创建一个LED任务 | ||
+ | |||
+ | 9.解锁任务,让任务按照优先级进行调度<syntaxhighlight lang="c++" line="1" start="43"> | ||
+ | void LOS_BoardLedEntry(void) | ||
+ | { | ||
+ | UINT32 uwRet; | ||
+ | |||
+ | // 定义用于创建任务的参数的结构 | ||
+ | TSK_INIT_PARAM_S stTaskInitParam; | ||
+ | |||
+ | // 锁任务调度 | ||
+ | LOS_TaskLock(); | ||
+ | |||
+ | (VOID)memset((void *)(&stTaskInitParam), 0, sizeof(TSK_INIT_PARAM_S)); // 任务参数清空 | ||
+ | stTaskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)LOS_BoardLedTskfunc; // 定义此任务的入口功能 | ||
+ | stTaskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE; // 任务栈大小0x500 | ||
+ | stTaskInitParam.pcName = "LED-Demo"; // 任务名称 | ||
+ | stTaskInitParam.usTaskPrio = 10; // 任务优先级 | ||
+ | |||
+ | // 创建此任务,由于任务锁,不会马上执行 | ||
+ | uwRet = LOS_TaskCreate(&g_uwLedTaskID, &stTaskInitParam); | ||
+ | if (uwRet != LOS_OK) | ||
+ | { | ||
+ | GUI_DispStringAtCL("led task create failed",10,32,GUI_Context.colorInfo.bkColor); // 打印创建lcd task失败 | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // 解锁任务调度,此时会发生任务调度,执行就绪列表中最高优先级任务 | ||
+ | LOS_TaskUnlock(); | ||
+ | |||
+ | GUI_DispStringAtCL("led task create success",10,32,GUI_Context.colorInfo.bkColor); // 打印创建lcd task成功 | ||
+ | return; | ||
+ | } | ||
+ | </syntaxhighlight>led任务的功能入口函数,在这个函数中,我们利用任务延时函数LOS_TaskDelay(),让LED灯周期点亮和熄灭。<syntaxhighlight lang="c++" line="1" start="82"> | ||
+ | static LITE_OS_SEC_TEXT VOID LOS_BoardLedTskfunc(VOID) | ||
+ | { | ||
+ | while (1) | ||
+ | { | ||
+ | // 点亮LED,LCD打印"LED=ON",任务延时500ms | ||
+ | BSP_EvbLedControl(LED_ON); | ||
+ | GUI_DispStringAtCL("LED=ON",10,64,GUI_Context.colorInfo.bkColor); | ||
+ | (void)LOS_TaskDelay(500); | ||
+ | |||
+ | // 熄灭LED,LCD打印"LED=OFF",任务延时500ms | ||
+ | BSP_EvbLedControl(LED_OFF); | ||
+ | GUI_DispStringAtCL("LED=OFF",10,64,GUI_Context.colorInfo.bkColor); | ||
+ | (void)LOS_TaskDelay(500); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> |
2019年4月8日 (一) 14:18的最新版本
目录
1 教程介绍
经过NBDK-L4基础实验部分的学习,大家应该已经能够熟练的使用STM32Cube生成工程,并且掌握工程各部分文件的功能,完成个人需求的开发。在开发的过程中,我们不难发现,所有的功能都需要在main()函数中去配置工作,这样的工作方式很明显不能适用于较复杂的功能开发,所以我们需要引入OS操作系统,以此来方便我们的开发。本章节我们将给大家带来华为LiteOS操作系统的使用和讲解。
对于LiteOS的学习和使用,我们主要分为三个步骤:
1.了解LiteOS
在华为的IOT开发者专区中,有专门对于LiteOS的讲解,大家可以通过如下的链接前往查看
华为IOT开发者专区:https://developer.huaweicloud.com/iot
华为LiteOS源码及说明:https://github.com/LiteOS/LiteOS
2.如何在Cube生成的工程中移植LiteOS
在华为LiteOS源码及说明的链接中,有给大家说明如何移植华为LiteOS。
因为LiteOS是持续更新的,所以移植起来不是特别方便(目录架构总在变),我们建议大家可以先略过这一步骤,直接使用我们移植好的工程去进行需求开发。
3.如何使用LiteOS开发我们的功能
这一部分是我们LiteOS实验的主体部分,请大家跟随我们的实验一起学习。
Huawei LiteOS是华为面向物联网领域开发的一个基于实时内核的轻量级操作系统。本项目属于华为物联网操作系统Huawei LiteOS源码,现有基础内核支持任务管理、内存管理、时间管理、通信机制、中断管理、队列管理、事件管理、定时器等操作系统基础组件,更好地支持低功耗场景,支持tickless机制,支持定时器对齐。
同时提供端云协同能力,集成了LwM2M、CoAP、mbedtls、LwIP全套IoT互联协议栈,且在LwM2M的基础上,提供了AgentTiny模块,用户只需关注自身的应用,而不必关注LwM2M实现细节,直接使用AgentTiny封装的接口即可简单快速实现与云平台安全可靠的连接。 Huawei LiteOS自开源社区发布以来,围绕NB-IoT物联网市场从技术、生态、解决方案、商用支持等多维度使能合作伙伴,构建开源的物联网生态,目前已经聚合了30+ MCU和解决方案合作伙伴,共同推出一批开源开发套件和行业解决方案,帮助众多行业客户快速的推出物联网终端和服务,客户涵盖抄表、停车、路灯、环保、共享单车、物流等众多行业,为开发者提供 “一站式” 完整软件平台,有效降低开发门槛、缩短开发周期。 |
1.1 工程简介
1.2 工程目录简介
大家打开任意一个LiteOS例程,都会看到如下的4个目录(Drivers、LiteOS、MDK-ARM、Src)及clean.bat文件。
其中clean.bat是用于清除工程编译生成的中间文件。例如我们想拷贝一个编译过的工程,工程有200M左右大小,我们点击clean.bat清除一下编译生成的中间文件,则工程大概会缩小到100M左右,此时工程只剩下了库文件、用户文件,以及编译生成的hex文件。
从上图可以看到,四个主目录下分别包含的一些文件,这边给大家简单的介绍一下这边文件大概的功能。
Drivers:
STM32驱动文件目录,也就是大家常说的hal库,里面包含了hal(硬件抽象层)相关的文件。
主要就是有RCC时钟、Flash内存,以及大家常用的外设(例如uart、spi、adc等等)的一些库文件。
LiteOS:
华为LiteOS操作系统目录,里面包含了两个目录:arch、kernel。
这两个目录就是我们移植到STM32cube工程中的LiteOS相关文件。
MDK-ARM:
工程目录,主要是两个工程文件“.uvoptx”以及".uvprojx"(keil打开的是这个)。剩下的文件比较重要的是Output目录下编译生成的“.hex”文件。
Src:
用户文件,用户自己开发的一些驱动文件(外设驱动等等),以及main文件所在的目录。
1.3 实验01-TFT显示屏
LiteOS的第一个实验,我们给大家带来的是LCD图形化显示实验。至于我们为什么要首先讲解LCD相关的例程,而不是最简单的LED控制实验,主要目的有三个:
1.大家了解了LCD例程之后,对于后面的其他实验,可以使用LCD显示一些调试信息,这样便利于大家的开发。
2.我们会借由此例程,给大家讲解LiteOS例程在Keil5中的目录结构,以及我们开发者需要关注的重要文件及重要功能函数。
3.借由此例程,给大家展示keil5 options中的配置。
1.4 工程文件说明
这一章节我们给大家介绍一下,每个工程的Group目录以及其下包含的文件的功能。
Group | file | 说明 |
---|---|---|
LiteOS/arch | arch目录取名来源于architecture(建筑结构),所包含的是硬件配置文件 | |
los_dispatch_keil.c | keil中los的系统调度文件 | |
los_hw.c | hardware硬件配置文件 | |
los_hw_tick.c | hardware tick硬件系统tick文件 | |
los_hwi.c | hardware interrupt硬件中断文件 | |
LiteOS/kernel | los内核 | |
cmsis_liteos.c | los提供的CMSIS接口 | |
los_config.c | los配置文件,用于初始化并启动los | |
los_priqueue.c | priority queue,los队列优先级处理 | |
los_swtmr.c | software timer,los软件定时器 | |
los_sys.c | 系统tick | |
los_task.c | 用于创建和处理任务 | |
los_tick.c | 仅包含了tick中断函数 | |
los_timeslice.c | 时间片初始化和设置 | |
los_event.c | 事件 | |
los_mux.c | 互斥锁 | |
los_queue.c | 队列 | |
los_sem.c | 信号量 | |
los_membox.c | 内存池初始化、分配、清除 | |
los_memory.c | 内存节点申请、清理、优化处理 | |
los_memstat.c | 内存状态 | |
los_muitipledlinkhead.c | 多内存处理 | |
los_misc.c | 包含了两个功能函数,字节对齐、当前任务休眠 | |
Driver/STM32L4xx_hal_driver | 和硬件相关的HAL抽象层文件,主要是电源、flash、外设。HAL不清楚如何使用的,大家可以先看下基础实验部分。 | |
Driver/CMSIS | Cortex-M 处理器系列的与供应商无关的硬件抽象层 | |
system_stm32l4xx.c | STM32L4系列硬件抽象层文件 | |
startup | 系统启动文件 | |
los_startup_keil.s | los在keil中的启动项文件 | |
Application/Display | TFT彩屏驱动,以及GUI图形界面驱动文件 | |
gui.c | GUI图形界面文件 | |
gui_dispstr.c | GUI字符串打印 | |
gui_dispval.c | GUI值打印 | |
gui_drawBitmap.c | GUI图形打印 | |
gui_os.c | GUI操作系统任务 | |
lcd.c | TFT驱动文件 | |
bmp_keyio.c | 按键图形界面 | |
bmp_logo.c | 谷雨logo图形界面 | |
Font8x16.c | 描述8*16大小的常见英文字符 | |
FontMethod.c | 定义英文字体的通用函数文件 | |
FontZH16x16_simsun.c | 描述16*16大小的中文字符 | |
FontZHMethod.c | 定义中文字体的通用函数文件 | |
lcddrv_tft_130 | TFT屏驱动文件,1.3型号 | |
lcddrv_tft_144 | TFT屏驱动文件,1.44型号 | |
lcd_gpio.c | TFT屏BL、CD、CS引脚控制 | |
lcd_hw_cfg.c | TFT屏硬件接口定义 | |
lcd_spi.c | TFT屏SPI接口控制 | |
Application/bsp | Board Support Package 板级支持包,结合STM32L4平台的外设驱动 | |
los_bsp_adapter.c | LOS和STM32的适配文件 | |
los_bsp_lcd.c | LCD驱动文件,结合STM32硬件 | |
Application/task | 用户任务,用户个人创建的task | |
Application/user | 用户文件,main函数、系统时钟、系统外设宏定义 | |
stm32l4xx_it.c | STM32中断函数文件 | |
sys_init.c | STM32系统时钟以及外设时钟定义文件 | |
main.c | 用于初始化los并运行,并且初始化和定义用户任务 |
1.5 实验验证
编译并下载此例程,由于涉及的库文件较多,编译将持续1min以上,请大家耐心等待。
下载完成后,可以看到TFT显示屏打印“LiteOS”、"01_los_lcd"字样。
1.6 源码详解
源码详解部分,第一个这个LCD部分,我们会按照基础实验部分的源码详解方式说明,后续的其他实验,我们仅讲解相对此例程新增的功能部分,所以请大家一定仔细阅读此例程说明。
1.6.1 stm32l4xx_hal_conf.h
此文件位于“实验01-TFT显示屏\Src\User”路径中,主要用途是选择使能此例程使用到的HAL库文件。
使用STM32,大家默认需要打开的几个宏定义,芯片使能、flash、电源、时钟、NVIC,这个也很好理解,就是让芯片工作的必要几点。
此例程我们给大家展示的是TFT显示屏,所以我们需要请打开驱动TFT工作的SPI、GPIO以及DMA三个宏。其他的外设例程也需要使能对应的宏,这个大家有使用的疑问,可以查找一下基础实验中的相应的外设例程。
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
111 #define HAL_DMA_MODULE_ENABLED // DMA
112 #define HAL_SPI_MODULE_ENABLED // SPI
1.6.2 sys_init.c
时钟初始化函数,用于配置我们模块运行的系统时钟、AHB高性能总线时钟、APB外设总线时钟以及单个外设的时钟。
主要包含了三个部分的初始化配置。
- 内部或者外部振荡器选择,也就是选择时钟信号的来源,是内部振荡,还是外部晶振。
- 时钟配置,选择系统、AHB总线及APB总线的时钟来源。
- 外设时钟配置,选择外设时钟来源。
为了给大家比较全面的展示各个时钟,我们振荡器选择HSI(内部16MHz高频)、HSE(外部8MHz高频)以及LSE(外部32.768KHz低频)三个。选择HSE作为PLL(锁相回路)时钟源,配置PLLCLK为80MHz。配置系统时钟SYSCLK、AHB高性能总线、APB外设总线(APB1及APB2)为80MHz。另外我们还分别配置了ADC、UART以及I2C的外设时钟。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
41 //******************************************************************
42 // fn : SystemClock_Config
43 //
44 // brief : 系统时钟配置函数
45 //
46 // param : none
47 //
48 // return : none
49 void SystemClock_Config(void)
50 {
51 RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体
52 RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体
53 RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体
54
55 // 配置LSE驱动器功能为低驱动能力
56 __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
57
58 // 初始化CPU,AHB和APB总线时钟
59 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
60 |RCC_OSCILLATORTYPE_LSE; // 设置需要配置的振荡器为HSI、HSE、LSE
61 // 配置HSE
62 RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz)
63 // 配置LSE
64 RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动)
65 // 配置HSI
66 RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟
67 RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz
68 // 配置PLL
69 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
70 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz
71 RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz
72 RCC_OscInitStruct.PLL.PLLN = 20; // 配置PLL VCO输入倍增为20,8MHz*20 = 160MHz
73 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160/7 = 22.857143MHz
74 RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160/2 = 80MHz
75 RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160/2 = 80MHz
76 // RCC时钟配置,出错则进入错误处理函数
77 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
78 {
79 _Error_Handler(__FILE__, __LINE__);
80 }
81
82 // 初始化CPU,AHB和APB总线时钟
83 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
84 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2
85 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,80MHz
86 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80/1 = 80MHz
87 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80/1 = 80MHz
88 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80/1 = 80MHz
89 // RCC时钟配置,出错则进入错误处理函数
90 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // HCLK=80MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4)
91 {
92 _Error_Handler(__FILE__, __LINE__);
93 }
94
95 // 初始化外设时钟
96 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
97 |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1
98 |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC
99 PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz
100 PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz
101 PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz
102 PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz
103 PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz
104 PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义
105 PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz
106 PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1
107 PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8
108 PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz
109 PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,64/2 = 32MHz
110 PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz
111 PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz
112 // 外设时钟配置,出错则进入错误处理函数
113 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
114 {
115 _Error_Handler(__FILE__, __LINE__);
116 }
117
118 // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz
119 if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
120 {
121 _Error_Handler(__FILE__, __LINE__);
122 }
123
124 // 配置系统定时器中断时间,配置为HCLK的千分频
125 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
126
127 // 配置系统定时器,配置为HCLK
128 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
129
130 // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0)
131 HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
132 }
133
134
135 //******************************************************************
136 // fn : _Error_Handler
137 //
138 // brief : 错误处理程序
139 //
140 // param : none
141 //
142 // return : none
143 void _Error_Handler(char * file, int line)
144 {
145 // 暂时未处理任何事情,用户可用来添加自己的功能来报告HAL错误返回状态
146 while(1)
147 {
148 }
149 }
1.6.3 main.c
main.c文件中,我们只放置了3个函数,以后的所有例程也只有这3个函数,下面我们将简要说明各个函数的功能。
硬件初始化函数,顾名思义,就是我们初始化硬件驱动的函数,在这个实验中,我们初始化了LCD的驱动文件。
27 void HardWare_Init(void)
28 {
29 // 初始化LCD
30 BSP_EvbLcdInit();
31 }
LiteOS任务初始化函数,我们会在这个函数中初始化我们用户新建的task任务,在这个TFT实验中,我们没有独立的任务,仅仅初始化了GUI图形化界面。
41 void LosTask_Init(void)
42 {
43 // 初始化图形接口
44 GUI_Init();
45 GUI_DispString("LiteOS");
46 GUI_DispNextLine();
47 GUI_DispString("01_los_lcd");
48 }
main()函数中,我们首先初始化HAL和系统时钟,接着我们初始化了LiteOS内核,使能LiteOS系统tick中断,然后我们分别初始化了我们所使用的硬件驱动和los任务,最后我们开启运行los。
58 int main(void)
59 {
60 UINT32 uwRet;
61
62 // 初始化硬件
63 HAL_Init();
64 SystemClock_Config();
65
66 // 初始化LOS内核
67 uwRet = LOS_KernelInit();
68 if (uwRet != LOS_OK)
69 {
70 return LOS_NOK;
71 }
72
73 // 使能LOS系统tick中断
74 uwRet = LOS_EnableTick();
75 if (uwRet != LOS_OK)
76 {
77 return LOS_NOK;
78 }
79
80 // 初始化开发板硬件
81 HardWare_Init();
82
83 // 初始化用户任务
84 LosTask_Init();
85
86 // 运行LOS
87 (void)LOS_Start();
88 for(;;);
89 }
1.7 los_bsp_lcd.c及gui.c等
los_bsp_lcd.c等同于基础实验TFT显示屏中的gyu_spi.c文件,gui.c等与基础实验TFT显示屏中的相同。大家可以参考基础实验,这边不再赘述。
1.8 gui_os.c
gui_os.c文件,此文件将与具体的操作系统有关,在基础实验的Display和GUI中是没有的,所以这边给大家讲解一下。
在不同的操作系统中,开发者要根据操作系统要求完成对GUI_Lock,GUI_Unlock,GUIOS_Init函数进行修改。开发者只要完成信号量Semaphore或临界区即可。
永久阻塞模式申请信号量,用于阻塞有关显示屏GUI打印的信号量。
32 void GUI_Lock(void)
33 {
34 /*永久阻塞模式申请信号量*/
35 LOS_SemPend(g_usDispSemID, LOS_WAIT_FOREVER);
36 }
释放信号量,用于释放有关显示屏GUI打印的信号量。
46 void GUI_Unlock(void)
47 {
48 /*释放信号量*/
49 LOS_SemPost(g_usDispSemID);
50 }
创建信号量,用于创建一个有关显示屏GUI打印的信号量。
59 void GUIOS_Init(void)
60 {
61 /*创建信号量*/
62 LOS_SemCreate(1,&g_usDispSemID);
63 }
2 实验02-led点灯
led点灯实验,我们在实验01的基础上,新增了使用LiteOS去创建一个独立的led task任务进程。
通过这个实验,大家需要掌握的是LiteOS Task任务的创建和使用。
2.1 实验验证
2.2 源码详解
2.3 main.c
在硬件初始化函数中,我们新增了LED硬件初始化部分。
27 void HardWare_Init(void)
28 {
29 // 初始化LCD
30 BSP_EvbLcdInit();
31
32 // 初始化LED
33 BSP_EvbLedInit();
34 }
在LiteOS 独立任务初始化函数中,我们新增加了LED独立任务。
44 void LosTask_Init(void)
45 {
46 // 初始化图形接口
47 GUI_Init();
48 GUI_DispString("LiteOS");
49 GUI_DispNextLine();
50 GUI_DispString("Led Test");
51
52 // 初始化LED任务
53 LOS_BoardLedEntry();
54 }
2.4 los_bsp_led.c
初始化LED硬件,文件中函数功能等同于LED基础实验中的gyu_led.c,不清楚的大家可以查看基础实验中的说明。
2.5 los_led_entry.c
此文件为本实验的重点内容,也就是我们使用LiteOS创建的独立LED控制任务。
基于los的LED独立任务创建函数,在创建一个task任务时,我们需要做如下步骤(结合具体的代码,不过单纯的框架步骤):
1.使用TSK_INIT_PARAM_S结构体定义一个创建task任务的参数结构
2.清空任务参数内容
3.定义此任务的入口功能(用户任务处理的功能函数)
4.分配任务栈的大小,默认给LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE大小
5.给当前任务取一个名称,此任务我们取名“LED-Demo”
6.分配优先级,任务一共有32个优先级(0-31),最高优先级为0,最低优先级为31
7.锁任务调度,防止高优先级任务调度,影响任务正常创建
8.利用刚刚的任务参数创建新的任务,创建一个LED任务
9.解锁任务,让任务按照优先级进行调度
43 void LOS_BoardLedEntry(void)
44 {
45 UINT32 uwRet;
46
47 // 定义用于创建任务的参数的结构
48 TSK_INIT_PARAM_S stTaskInitParam;
49
50 // 锁任务调度
51 LOS_TaskLock();
52
53 (VOID)memset((void *)(&stTaskInitParam), 0, sizeof(TSK_INIT_PARAM_S)); // 任务参数清空
54 stTaskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)LOS_BoardLedTskfunc; // 定义此任务的入口功能
55 stTaskInitParam.uwStackSize = LOSCFG_BASE_CORE_TSK_IDLE_STACK_SIZE; // 任务栈大小0x500
56 stTaskInitParam.pcName = "LED-Demo"; // 任务名称
57 stTaskInitParam.usTaskPrio = 10; // 任务优先级
58
59 // 创建此任务,由于任务锁,不会马上执行
60 uwRet = LOS_TaskCreate(&g_uwLedTaskID, &stTaskInitParam);
61 if (uwRet != LOS_OK)
62 {
63 GUI_DispStringAtCL("led task create failed",10,32,GUI_Context.colorInfo.bkColor); // 打印创建lcd task失败
64 return;
65 }
66
67 // 解锁任务调度,此时会发生任务调度,执行就绪列表中最高优先级任务
68 LOS_TaskUnlock();
69
70 GUI_DispStringAtCL("led task create success",10,32,GUI_Context.colorInfo.bkColor); // 打印创建lcd task成功
71 return;
72 }
led任务的功能入口函数,在这个函数中,我们利用任务延时函数LOS_TaskDelay(),让LED灯周期点亮和熄灭。
82 static LITE_OS_SEC_TEXT VOID LOS_BoardLedTskfunc(VOID)
83 {
84 while (1)
85 {
86 // 点亮LED,LCD打印"LED=ON",任务延时500ms
87 BSP_EvbLedControl(LED_ON);
88 GUI_DispStringAtCL("LED=ON",10,64,GUI_Context.colorInfo.bkColor);
89 (void)LOS_TaskDelay(500);
90
91 // 熄灭LED,LCD打印"LED=OFF",任务延时500ms
92 BSP_EvbLedControl(LED_OFF);
93 GUI_DispStringAtCL("LED=OFF",10,64,GUI_Context.colorInfo.bkColor);
94 (void)LOS_TaskDelay(500);
95 }
96 }