有幸参加了凌鸥创兴车规级LKS32AT085开发板的测评活动,收到板子以来工作一下忙得抽不得空,到现在才开始……
车规级芯片顾名思义就是可以应用到汽车中的芯片,不同于消费品和工业品,其芯片的研发、生产制造的技术难度远远大于消费类电子产品;车规级芯片在导入汽车前装零部件产品的设计时必须相当的谨慎,在上市之前需要经过严格的测试、试产和认证,产品开发周期长、性能要求严苛。随着国内新能源汽车的大步发展,使得车规级芯片的市场前景相当看好。凌鸥经过3年的探索、研发和验证,推出了车规级芯片LKS32AT085主控MCU,并且通过了AEC-Q100认证。具备功能安全认证的设计,也方便了客户进行ISO26262等的安全认证。具体的芯片特性和功能可以参考文中的附件文档,凌鸥描述得还是相当详细和严谨的!
当前车规级的芯片已不仅仅应用在汽车的前装产品,对于汽车后装产品性能要求也是越来越高,甚至有客户直接要求主控MCU必须要达到车规级别的,很重视产生的稳定、安全等诸多因素;同样对于软件安全、稳定也提出了更高的要求,有些后装客户要求软件需要拿到ClassB的认证证书。所以本次测评主要围绕基于凌鸥LKS32AT085开发板平台来自编/参考大厂在凌鸥MCU上实现ClassB的部分功能测试,同时也是一边学习,一起分享的过程。
首先我们需要基于凌鸥LKS_EVB_MCU085_V2.0核心板和LKS_EVB_MVPOWPRE_V5.0底板来创建一个基础工程,主要实现功能如下: 1. 熟悉GPIO的基础操作,控制LED流水灯显示效果,实现按键状态采集; 2. 熟悉UART的基础功能,通过重载fputc函数实现printf的打印功能; 3. 使用MultiButton开源软件实现对按键状态和按键事件的管理; 4. 使用MultiTimer开源软件实现系统应用任务的管理和调度; 5. 使用SysTick定时器作为时基,为MultiTimer开源软件提供调度时钟基数;同时SysTick也是作为后面ClassB运行自检的重要组成部分。
准备资料 更多的资料可以到凌鸥官网下载:https://www.linkosemi.com/
硬件准备 凌鸥LKS_EVB_MCU085_V2.0核心板和LKS_EVB_MVPOWPRE_V5.0底板、J-LINK调试下载器和SWD转接小板、USB转TTL工具
软件准备 KEIL MDK集成开发环境、凌鸥LKS32芯片PACK包、串口终端调试软件
创建工程 创建新工程->输入工程名称->选择芯片型号->配置管理Run-Time环境
添加程序源文件
配置工程
编写代码 - /*******************************************************************************
- * [url=home.php?mod=space&uid=247401]@brief[/url]
- * @param
- * @retval
- * [url=home.php?mod=space&uid=93590]@Attention[/url]
- *******************************************************************************/
- uint8_t read_pin_level(uint8_t pin_id)
- {
- if(pin_id == 0)
- {
- return GPIO_ReadInputDataBit(GPIO1, GPIO_Pin_10);
- }
- else
- {
- return GPIO_ReadInputDataBit(GPIO1, GPIO_Pin_11);
- }
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void KEY_Handler(void *btn)
- {
- Button *key = btn;
- switch(key->event)
- {
- case PRESS_DOWN:
- printf("\r\nKEY%d : PRESS_DOWN", key->button_id);
- break;
- case PRESS_UP:
- printf("\r\nKEY%d : PRESS_UP", key->button_id);
- break;
- case SINGLE_CLICK:
- printf("\r\nKEY%d : SINGLE_CLICK", key->button_id);
- break;
- case DOUBLE_CLICK:
- printf("\r\nKEY%d : DOUBLE_CLICK", key->button_id);
- break;
- case LONG_PRESS_START:
- printf("\r\nKEY%d : LONG_PRESS_START", key->button_id);
- break;
- case LONG_PRESS_HOLD:
- printf("\r\nKEY%d : LONG_PRESS_HOLD", key->button_id);
- break;
- default:
- break;
- }
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void KEY_RepeatHandler(void *btn)
- {
- Button *key = btn;
- printf("\r\nKEY%d : PRESS_REPEAT", key->button_id);
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void KEY_MultiTimerCallback(MultiTimer *timer, void *userData)
- {
- button_ticks();
- MultiTimerStart(&KEY_MultiTimer, 5, KEY_MultiTimerCallback, "KEY");
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void KEY_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- /* START <-> P1.10 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO1, &GPIO_InitStruct);
- /* STOP <-> P1.11 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO1, &GPIO_InitStruct);
- button_init( &btnSTART, read_pin_level, Bit_RESET, 0);
- button_attach(&btnSTART, SINGLE_CLICK, KEY_Handler);
- button_start( &btnSTART);
- button_init( &btnSTOP, read_pin_level, Bit_RESET, 1);
- button_attach(&btnSTOP, SINGLE_CLICK, KEY_Handler);
- button_start( &btnSTOP);
- MultiTimerStart(&KEY_MultiTimer, 5, KEY_MultiTimerCallback, "KEY");
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void LED_MultiTimerCallback(MultiTimer *timer, void *userData)
- {
- static uint8_t LED_State = 0;
- switch(LED_State)
- {
- case 0:
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_SET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_SET);
- break;
- case 1:
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_SET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_SET);
- break;
- case 2:
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_RESET);
- break;
- case 3:
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_SET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_SET);
- break;
- default:
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_RESET);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_SET);
- break;
- }
- LED_State = (LED_State + 1) % 4;
- MultiTimerStart(&LED_MultiTimer, 250, LED_MultiTimerCallback, "LED");
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void LED_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- /* LED1 <-> P3.9 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO3, &GPIO_InitStruct);
- GPIO_WriteBit(GPIO3, GPIO_Pin_9, Bit_RESET);
- /* LED2 <-> P2.8 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO2, &GPIO_InitStruct);
- GPIO_WriteBit(GPIO2, GPIO_Pin_8, Bit_RESET);
- /* LED3 <-> P2.9 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO2, &GPIO_InitStruct);
- GPIO_WriteBit(GPIO2, GPIO_Pin_9, Bit_SET);
- MultiTimerStart(&LED_MultiTimer, 250, LED_MultiTimerCallback, "LED");
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void MCU_InitUART(void)
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- UART_InitTypeDef UART_InitStruct;
- /* UART0_RXD <-> P0.15 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO0, &GPIO_InitStruct);
- /* UART0_TXD <-> P1.0 */
- GPIO_StructInit(&GPIO_InitStruct);
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(GPIO1, &GPIO_InitStruct);
- GPIO_PinAFConfig(GPIO0, GPIO_PinSource_15, AF4_UART);
- GPIO_PinAFConfig(GPIO1, GPIO_PinSource_0, AF4_UART);
- UART_StructInit(&UART_InitStruct);
- UART_InitStruct.BaudRate = 115200;
- UART_InitStruct.WordLength = UART_WORDLENGTH_8b;
- UART_InitStruct.StopBits = UART_STOPBITS_1b;
- UART_InitStruct.FirstSend = UART_FIRSTSEND_LSB;
- UART_InitStruct.ParityMode = UART_Parity_NO;
- UART_InitStruct.IRQEna = UART_IRQEna_RcvOver;
- UART_Init(UART0, &UART_InitStruct);
- UART_ClearIRQFlag(UART0, UART_IF_SendOver | UART_IF_RcvOver | UART_IF_SendBufEmpty |
- UART_IF_StopError | UART_IF_CheckError);
- NVIC_SetPriority(UART0_IRQn, 1);
- NVIC_EnableIRQ(UART0_IRQn);
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- int fputc(int ch, FILE *f)
- {
- UART_SendData(UART0, (uint8_t)ch);
- while((UART0->STT & BIT0) == 0x0);
- return ch;
- }
- /*******************************************************************************
- 函数名称: void UART0_IRQHandler(void)
- 功能描述: UART0中断处理函数
- 输入参数: 无
- 输出参数: 无
- 返 回 值: 无
- 其它说明:
- 修改日期 版本号 修改人 修改内容
- -------------------------------------------------------------------------------
- 2020/8/5 V1.0 Howlet Li 创建
- ******************************************************************************/
- void UART0_IRQHandler(void)
- {
- if(UART0_IF & UART_IF_RcvOver)
- {
- printf("%c", UART_ReadData(UART0));
- UART_ClearIRQFlag(UART0, UART_IF_RcvOver);
- }
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- void SysTick_Init(void)
- {
- if(SysTick_Config(96000000 / 1000) != 0)
- {
- while(1);
- }
- NVIC_SetPriority(SysTick_IRQn, 0);
- }
- /*******************************************************************************
- 函数名称: void SysTick_Handler(void)
- 功能描述: 系统滴答定时中断
- 输入参数: 无
- 输出参数: 无
- 返 回 值: 无
- 其它说明:
- 修改日期 版本号 修改人 修改内容
- -------------------------------------------------------------------------------
- 2020/8/5 V1.0 Howlet Li 创建
- ******************************************************************************/
- void SysTick_Handler(void)
- {
- SysTick_Tick++;
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- uint64_t PlatformTicksFunction(void)
- {
- return SysTick_Tick;
- }
- /*******************************************************************************
- * @brief
- * @param
- * @retval
- * @attention
- *******************************************************************************/
- int main(void)
- {
- MultiTimerInstall(PlatformTicksFunction);
- MCU_Init();
- KEY_Init();
- LED_Init();
- printf("\r\nLKS_EVB_MCU085_V2.0 %s %s\r\n", __DATE__, __TIME__);
- while(1)
- {
- MultiTimerYield();
- }
- }
运行结果 编译->下载->运行(串口终端软件显示打印消息,按键按下后能够正确识别)
运行演示
程序附件
总结 1. 创建工程时,在弹出的Manage Run-Time Environment窗口时,需要勾选CMSIS中的CORE,这是因为LKS32AT085是集成32位Cortex-M0内核的车规级MCU,在官方的BSP软件包中只提供了基于MCU外设IP的底层驱动库程序,如果不勾选此选项,在编译工程时会提示如下信息:
2. 在使用SysTick时,在core_cm0.h文件中提供了SysTick_Config函数来配置SysTick定时器,但在通过调用SysTick_Config函数初始化配置SysTick时,却提示黄色警告;在编译工程时,却提示如下信息: 解决方法是在我们总包含的config.h头文件中进行如下宏定义即可: - /*******************************************************************************
- * [url=home.php?mod=space&uid=288409]@file[/url] config.h
- * [url=home.php?mod=space&uid=187600]@author[/url] King
- * [url=home.php?mod=space&uid=895143]@version[/url] V1.00
- * [url=home.php?mod=space&uid=212281]@date[/url] 15-Sep-2022
- * @brief ......
- *******************************************************************************/
- /* Define to prevent recursive inclusion -------------------------------------*/
- #ifndef __CONFIG_H__
- #define __CONFIG_H__
- /* Define --------------------------------------------------------------------*/
- #define __Vendor_SysTickConfig 0
- /* Includes ------------------------------------------------------------------*/
- #include <stdio.h>
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- /* Includes ------------------------------------------------------------------*/
- #include "lks32mc08x_lib.h"
- /* Includes ------------------------------------------------------------------*/
- #include "MultiButton.h"
- #include "MultiTimers.h"
- /* Includes ------------------------------------------------------------------*/
- #include "KEY.h"
- #include "LED.h"
- #include "MCU.h"
- #endif
- /*********************************END OF FILE**********************************/
3. 我们使用UART0进行调试信息打印输出的功能,UART0配置工作在收发的全双工模式,接收使用中断的方式,将接收到的数据再原样的打印出来,这样来测试UART0的收发功能;在中断函数中我们可以使用UART_GetIRQFlag和UART_ClearIRQFlag这两个库函数来判断获取和清除相应状态来处理应用;但我的的TX并非开启中断,在发送数据后需要通过查询相应的状态位来判断数据位是否已经发送完成,这样的判断并没有找到对应的库函数,而是需要我们通过程序直接操作寄存器来实现,所以我想说的是库函数还有需要再完善的部分。
|