打印
[LKS32 硬件]

【LKS32MC081评测】01.从零开始创建凌鸥基础工程

[复制链接]
1077|10
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
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接口转接小板。

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


安装芯片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进行安装,如下图所示;在安装完成后,我们就可以在芯片列表中找到凌鸥的芯片型号,创建工程进行开发了:


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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翻转
void LED_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

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

    GPIO_WriteBit(GPIO0, GPIO_Pin_6, Bit_RESET);

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

    GPIO_WriteBit(GPIO0, GPIO_Pin_7, Bit_RESET);

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

    GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_SET);

    TASK_Append(TASK_ID_LED, LED_Toggle, 250);
}

void LED_Toggle(void)
{
    if(!GPIO_ReadOutputDataBit(GPIO0, GPIO_Pin_3))
    {
        GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_SET);
    }
    else
    {
        GPIO_WriteBit(GPIO0, GPIO_Pin_3, Bit_RESET);
    }
}

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

    /* START <-> P2.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(GPIO2, &GPIO_InitStruct);

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

    TASK_Append(TASK_ID_KEY, KEY_Scan, 10);
}

void KEY_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, char *Name)
{
    if(*State == 0)
    {
        if(Value == Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 1;
            printf("\r\n%s Pressed", Name);
        }
    }
    else
    {
        if(Value != Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 0;
            printf("\r\n%s Release", Name);
        }
    }
}

void KEY_Scan(void)
{
    static uint8_t KeyState[2] = {0, 0};
    static uint8_t KeyCount[2] = {0, 0};

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

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

    while(!(UART0_IF & UART_IF_SendOver));

    UART0_IF = UART_IF_SendOver;
}

void shellPortInit(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);
    UART0_IF = 0xFF;

    NVIC_SetPriority(UART0_IRQn, 3);

    NVIC_EnableIRQ(UART0_IRQn);

    shell.write = shellPortWrite;
    shellInit(&shell);
}

void UART0_IRQHandler(void)
{
    if(UART0_IF & UART_IF_RcvOver)
    {
        UART0_IF = UART_IF_RcvOver;
        shellHandler(&shell, UART0_BUFF);
    }
}

int fputc(int ch, FILE *f)
{
    UART0_BUFF = (uint8_t)ch;

    while(!(UART0_IF & UART_IF_SendOver));

    UART0_IF = UART_IF_SendOver;

    return ch;
}


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

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

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


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


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

使用特权

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

    return (ch);
}


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


使用特权

评论回复
板凳
GrandLine|  楼主 | 2022-6-13 12:54 | 只看该作者
软件工程源代码
Template.zip (157.57 KB)

使用特权

评论回复
地板
上下而求索| | 2022-6-13 13:21 | 只看该作者

模板级

使用特权

评论回复
5
GrandLine|  楼主 | 2022-6-14 08:41 | 只看该作者

使用特权

评论回复
6
hilahope| | 2022-6-20 15:46 | 只看该作者
这个是官网的例程吗?

使用特权

评论回复
7
cashrwood| | 2022-6-20 15:55 | 只看该作者
可以使用vs开发吗?  

使用特权

评论回复
8
gouguoccc| | 2022-6-20 20:01 | 只看该作者
谢谢分享,学习了。

使用特权

评论回复
9
GrandLine|  楼主 | 2022-6-20 22:08 | 只看该作者
hilahope 发表于 2022-6-20 15:46
这个是官网的例程吗?

这个是基于官方底层驱动库,自己创建的模板工程……

使用特权

评论回复
10
GrandLine|  楼主 | 2022-6-20 22:09 | 只看该作者
cashrwood 发表于 2022-6-20 15:55
可以使用vs开发吗?

应该可以吧,我不太熟

使用特权

评论回复
11
GrandLine|  楼主 | 2022-6-20 22:10 | 只看该作者
gouguoccc 发表于 2022-6-20 20:01
谢谢分享,学习了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

35

帖子

3

粉丝