[LKS32 硬件] 【LKS32MC081评测】01.从零开始创建凌鸥基础工程

[复制链接]
 楼主| GrandLine 发表于 2022-6-12 10:09 | 显示全部楼层 |阅读模式
本帖最后由 GrandLine 于 2022-6-12 10:16 编辑

#申请原创#   @21小跑堂


有幸入围了凌鸥创芯电机开发板LKS32M081免费开发板评测活动,收到开发板就迫不及待的开始想玩转起来了……我也是头一回接触凌鸥创芯的MCU。这回申请的开发板主控是LKS08系列的芯片,它是针对电机驱动市场的高性能电机运动控制系列芯片;使用Cortex-M0作为MCU内核,最高工作频率可达到96MHz;集成了仪表级全差分可编程增益放大器和差分ADC,在不需要电压偏置的情况下就可以处理正负信号;集成了12位精度的同步双采样3Msps高速ADC;集成了全温范围可达到千分之五以内的电压基准源;同时还集成了凌鸥自主DSP指令集,可作为异构独立核心运行程序,三角函数、开方运算都可以在100ns内完成运算,给电机的算法控制提供了强有力的支撑。

LKS08系列的芯片工作温度范围在-40℃到+105℃,内部自带的RC时钟源在全温范围内可以达到±1%以内,此外芯片引脚还通过了6KV的接触式放电测试和15KV的非接触式空气静电放电测试,这些给芯片的稳定、可靠运行提供了保障。

LKS08系列的芯片带有丰富的模拟运放和比较器资源,可满足单电阻、双电阻、三电阻电流采样拓扑架构的不同需求;内部集成高压钳位网络,允许高压共模信号直接输入,可以轻松的实现MOSFET内阴直接电流采样的功能,节省了昂贵的电流采样电阻并降低了系统功耗。这样集成度高的MCU,尽最大可能的节省了BOM成本,使客户产品更具有竞争优势。

下面切入正题,本文将介绍基于LKS08系列MCU从搭建开发环境、创建新工程、编写工程代码这几步来讲解,在实操过程中将遇到的一些问题和注意事项,给大家分享一下:
1.准备工作
2.安装芯片PACK支持包
3.创建基础工程
4.编写工程代码:LED、KEY、基于UART0移植的Letter-shell
5.下载运行演示


准备工作
  • 软件开发环境
目前官网上仅提供了基于KEIL4和KEIL5的Device Pack,所以我们开发IDE环境是基于KEIL MDK软件,另外就是我常用的MobaXterm调试终端软件。

  • 硬件开发环境
LKS_EVB_MCU081_V2.0核心板和LKD_EVB_MVPOWPRE_V4.0电机驱动底板各1块,J-LINK调试下载工具、USB转TTL工具、因为LKS_EVB_MCU081_V2.0核心板上板载设计的是SWD下载接口,所以还带了一个自制的JTAG转SWD接口转接小板。
25.jpg

  • 开发资料准备
凌鸥的开发资料都可以通过官网下载到,相对于入门开发的小伙伴来说,资料提供得还是比较全的,我也整理了一下,如下所示:
数据手册: LKS32MC08x_DS.pdf (1.91 MB, 下载次数: 19)
用户手册: LKS32MC08x_UM.pdf (7.83 MB, 下载次数: 20)
勘误手册: LKS32MC08x_ER.pdf (383.05 KB, 下载次数: 26)
LKS_EVB_MCU081_V2.0核心板原理图: LKS_EVB_MCU081_083_088_V2.0.pdf (139.15 KB, 下载次数: 49)
LKD_EVB_MVPOWPRE_V4.0电机驱动底板原理图: LKS_EVB_MVPOWPRE_V4.0-20210906.pdf (164.97 KB, 下载次数: 57)
……


安装芯片PACK支持包
我使用的中KEIL5集成开发环境,所以在官方上下载了相对应的Device Pack文件(https://www.linkosemi.com/uploadfiles/Updatefile/nutstore/Keil_v5_device_pack/Linko.LKS08x_v1.0.5.rar),在解压后直接双击Linko.LKS08x_v1.0.5.pack进行安装,如下图所示;在安装完成后,我们就可以在芯片列表中找到凌鸥的芯片型号,创建工程进行开发了:
1.png 2.png


创建基础工程
STEP1.打开KEIL5 MDK软件,点击菜单栏Project->New uVision Project...
3.png

STEP2.在弹出的Create New Project窗口中选择工程存放的路径以及命名项目文件名:
4.png

STEP3.在弹出的Select Device for Target窗口中选择凌鸥芯片型号:LKS32MC081C8T8:
5.png

STEP4.在弹出的Manage Run-Time Environment窗口中不作任何操作,直接点击OK:
6.png

STEP5.此时一个基于LKS32MC081C8T8芯片的空项目工程就创建出来了,接下来我们还需要向工程中添加分组、添加程序以及配置参数等操作:
7.png

STEP6.点击工具栏上的Manage Project Items按钮:
8.png

STEP7.在弹出的Manage Project Items窗口中的Project Items选项卡中,将Project Targets重新命名为LKS32MC081C8T8、在Groups中添加分组、然后在对应的Group中再添加Files源程序:
9.png

STEP8.完整的工程目录文件如下所示:
10.png

需要注意的是我们一般源程序都是*.c/*.h类型的文件,但在添加官方库程序时有一个.o类型的文件lks32mc08x_nvr.o,这是KEIL编译后输出的文件,可以理解为看不到源码形式的程序文件,也可以直接编译;如果不添加这个文件会有Read_Trim的错误提示哦!!!

STEP9.点击工具栏上的Options for Target...按钮:
11.png

STEP10.在弹出的Options for Target窗口的Target选项卡中,将Code Generation选择使用Use default compiler version5,勾选Use MicroLIB:
12.png

STEP11.在Output选项卡中,可以设置Name of Executable,如果需要生成HEX烧录文件则需要勾选Create HEX File选项,将Browse Information勾选是为了程序实现过程中或者是调试过程中的便捷:
13.png

STEP12.在C/C++选项卡中,对包含的头文件路径Include Paths进行配置:
14.png 15.png

STEP13.在Debug选项卡中,选择相就的调试工具,并点击Settings进行后一级设置,如果提示未知的芯片型号,我们默认选择Cortex-M0即可,然后将程序烧录接口方式修改为SW模式,这样在SW Device中就可以成功检测到芯片了:
16.png 19.png 20.png

STEP14.在Utilities选项卡中,选择Use Target Driver for Flash Programming选项,默认勾选Use Debug Driver和Update Target before Debugging,然后点击Settings进行后一级设置,确认Programming Algorithm下载算法的选择是正确的,并勾选Reset and Run,这样在程序下载完成后,就可以自动复位运行了:
17.png 18.png

STEP15.到此整个项目工程就完全创建、配置完成了,接下来就可以开始编写程序代码了。


编写工程代码
打开官方的示例程序,我们使用到了LKS32MC08x_Periph_Driver中的库程序;示例中官方除了startup_lks32mc08x.s启动文件之外,并没有看到我们常见的system_lks32mc08x.c文件;通过查阅官方示例工程源程序,我们看到多出了一个hardware_init.c文件,在这个文件中实现了对MCU使用外设的初始化和startup文件中需要调用的SystemInit函数;但个人习惯,还是把这个hardware_init.c的文件名给改写成了system_lks32mc08x.c,只实现了SystemInit对系统时钟的配置,其余的外设初始化放到了其它的.c文件中;另外还有一个需要注意的文件是hardware_config.h文件,这个在底层库程序中都有包含,它主要是包含了底层库程序的一些头文件,以及共用的宏定义,所以这个文件一定要包含进来哈!!!最后一个就是interrupt.c文件,官方是将所有的中断函数实现集中在一个文件中的,这样写各有优缺点,我还是按照之前我的写法,在使用到的时候再具体实现,因为本身在startup_lks32mc08x.s文件中加了weak的修饰,我不用的时候完全不需要去写个空函数嘛。

  • LED初始化配置、LED翻转
  1. void LED_Init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStruct;

  4.     /* LED1 <-> P0.6 */
  5.     GPIO_StructInit(&GPIO_InitStruct);
  6.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_6;
  7.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  8.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  9.     GPIO_Init(GPIO0, &GPIO_InitStruct);

  10.     GPIO_WriteBit(GPIO0, GPIO_Pin_6, Bit_RESET);

  11.     /* LED2 <-> P0.7 */
  12.     GPIO_StructInit(&GPIO_InitStruct);
  13.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_7;
  14.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  15.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  16.     GPIO_Init(GPIO0, &GPIO_InitStruct);

  17.     GPIO_WriteBit(GPIO0, GPIO_Pin_7, Bit_RESET);

  18.     /* LED3 <-> P0.3 */
  19.     GPIO_StructInit(&GPIO_InitStruct);
  20.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_3;
  21.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  22.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  23.     GPIO_Init(GPIO0, &GPIO_InitStruct);

  24.     GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_SET);

  25.     TASK_Append(TASK_ID_LED, LED_Toggle, 250);
  26. }

  27. void LED_Toggle(void)
  28. {
  29.     if(!GPIO_ReadOutputDataBit(GPIO0, GPIO_Pin_3))
  30.     {
  31.         GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_SET);
  32.     }
  33.     else
  34.     {
  35.         GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_RESET);
  36.     }
  37. }

  • KEY初始化配置、KEY检测
  1. void KEY_Init(void)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStruct;

  4.     /* START <-> P2.11 */
  5.     GPIO_StructInit(&GPIO_InitStruct);
  6.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_11;
  7.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
  8.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  9.     GPIO_Init(GPIO2, &GPIO_InitStruct);

  10.     /* STOP  <-> P2.12 */
  11.     GPIO_StructInit(&GPIO_InitStruct);
  12.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_12;
  13.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
  14.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  15.     GPIO_Init(GPIO2, &GPIO_InitStruct);

  16.     TASK_Append(TASK_ID_KEY, KEY_Scan, 10);
  17. }

  18. void KEY_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, char *Name)
  19. {
  20.     if(*State == 0)
  21.     {
  22.         if(Value == Bit_RESET) *Count += 1;
  23.         else                   *Count  = 0;

  24.         if(*Count > 5)
  25.         {
  26.             *Count = 0; *State = 1;
  27.             printf("\r\n%s Pressed", Name);
  28.         }
  29.     }
  30.     else
  31.     {
  32.         if(Value != Bit_RESET) *Count += 1;
  33.         else                   *Count  = 0;

  34.         if(*Count > 5)
  35.         {
  36.             *Count = 0; *State = 0;
  37.             printf("\r\n%s Release", Name);
  38.         }
  39.     }
  40. }

  41. void KEY_Scan(void)
  42. {
  43.     static uint8_t KeyState[2] = {0, 0};
  44.     static uint8_t KeyCount[2] = {0, 0};

  45.     KEY_SubScan(&KeyState[0], &KeyCount[0], GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_11), "START");
  46.     KEY_SubScan(&KeyState[1], &KeyCount[1], GPIO_ReadInputDataBit(GPIO2, GPIO_Pin_12), "STOP ");
  47. }

  • UART0初始配置、Letter-shell移植
  1. void shellPortWrite(const char ch)
  2. {
  3.     UART0_BUFF = (uint8_t)ch;

  4.     while(!(UART0_IF & UART_IF_SendOver));

  5.     UART0_IF = UART_IF_SendOver;
  6. }

  7. void shellPortInit(void)
  8. {
  9.     GPIO_InitTypeDef GPIO_InitStruct;
  10.     UART_InitTypeDef UART_InitStruct;

  11.     /* UART0_RXD <-> P0.15 */
  12.     GPIO_StructInit(&GPIO_InitStruct);
  13.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_15;
  14.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
  15.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  16.     GPIO_Init(GPIO0, &GPIO_InitStruct);

  17.     /* UART0_TXD <-> P1.0  */
  18.     GPIO_StructInit(&GPIO_InitStruct);
  19.     GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_0;
  20.     GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
  21.     GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  22.     GPIO_Init(GPIO1, &GPIO_InitStruct);

  23.     GPIO_PinAFConfig(GPIO0, GPIO_PinSource_15, AF4_UART);
  24.     GPIO_PinAFConfig(GPIO1, GPIO_PinSource_0,  AF4_UART);

  25.     UART_StructInit(&UART_InitStruct);
  26.     UART_InitStruct.BaudRate   = 115200;
  27.     UART_InitStruct.WordLength = UART_WORDLENGTH_8b;
  28.     UART_InitStruct.StopBits   = UART_STOPBITS_1b;
  29.     UART_InitStruct.FirstSend  = UART_FIRSTSEND_LSB;
  30.     UART_InitStruct.ParityMode = UART_Parity_NO;
  31.     UART_InitStruct.IRQEna     = UART_IRQEna_RcvOver;
  32.     UART_Init(UART0, &UART_InitStruct);
  33.     UART0_IF = 0xFF;

  34.     NVIC_SetPriority(UART0_IRQn, 3);

  35.     NVIC_EnableIRQ(UART0_IRQn);

  36.     shell.write = shellPortWrite;
  37.     shellInit(&shell);
  38. }

  39. void UART0_IRQHandler(void)
  40. {
  41.     if(UART0_IF & UART_IF_RcvOver)
  42.     {
  43.         UART0_IF = UART_IF_RcvOver;
  44.         shellHandler(&shell, UART0_BUFF);
  45.     }
  46. }

  47. int fputc(int ch, FILE *f)
  48. {
  49.     UART0_BUFF = (uint8_t)ch;

  50.     while(!(UART0_IF & UART_IF_SendOver));

  51.     UART0_IF = UART_IF_SendOver;

  52.     return ch;
  53. }


下载运行演示
  • 在工程源代码中还添加了对SysTick的初始化,然后结合TASK任务进行时间片轮转调度;完成所有程序编写之后,我们就可以编译工程源代码啦:
21.png

  • 编译无误后,我们通过工具栏上的Download按钮将程序下载到芯片中:
22.png

  • 通过MobaXterm软件我们可以看到芯片启动之后的运行打印信息、板载的LED处于翻转显示的状态、按下抬起START和STOP按键也有对应的状态信息打印出来:
24.jpg 23.png


软件工程源代码
级别太低了,今天没有上传权限了,明天传上来


后续
对于MCU来说,我可以驾轻就熟的去编写代码,实现功能;但对于电机应用控制来说我还是个新手;后续通过边学边做的形式,来熟悉凌鸥MCU的外设资源,结合电机的实际调试来做一些分享。

 楼主| GrandLine 发表于 2022-6-12 11:23 | 显示全部楼层
1、官方双线UART0传输例程中,UART0是通过中断方式进行接收和发送的,在UART.c文件中重载实现的fputc函数,缺少UART_Flag标志位置位的操作,导致在使用printf函数时,打印输出不了,修改后如下:
  1. ///重定向c库函数printf到串口,重定向后可使用printf函数
  2. int fputc(int ch, FILE *f)
  3. {
  4.     UART_Flag = 1;
  5.     /* 发送一个字节数据到串口 */
  6.     UART_SendData(UART0, (uint8_t) ch);       
  7.     /* 等待发送完毕 */
  8.     while(UART_Flag);               

  9.     return (ch);
  10. }


2、官方提供的原理图是设计源文件形式的,建议增加一个PDF版本的,这样方便查看;在楼上准备资料中已经转换成PDF版本了,有需要的小伙伴可以下载。


 楼主| GrandLine 发表于 2022-6-13 12:54 | 显示全部楼层
软件工程源代码
Template.zip (157.57 KB, 下载次数: 57)
上下而求索 发表于 2022-6-13 13:21 | 显示全部楼层

模板级
 楼主| GrandLine 发表于 2022-6-14 08:41 | 显示全部楼层
hilahope 发表于 2022-6-20 15:46 | 显示全部楼层
这个是官网的例程吗?
cashrwood 发表于 2022-6-20 15:55 | 显示全部楼层
可以使用vs开发吗?  
gouguoccc 发表于 2022-6-20 20:01 来自手机 | 显示全部楼层
谢谢分享,学习了。
 楼主| GrandLine 发表于 2022-6-20 22:08 | 显示全部楼层
hilahope 发表于 2022-6-20 15:46
这个是官网的例程吗?

这个是基于官方底层驱动库,自己创建的模板工程……
 楼主| GrandLine 发表于 2022-6-20 22:09 | 显示全部楼层
cashrwood 发表于 2022-6-20 15:55
可以使用vs开发吗?

应该可以吧,我不太熟
 楼主| GrandLine 发表于 2022-6-20 22:10 | 显示全部楼层
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

35

帖子

7

粉丝
快速回复 在线客服 返回列表 返回顶部