配置CLion用于TI MSPM0开发【丝滑的嵌入式开发】

专为TI MSPM0G3507配置的CLion开发模板,集成FreeRTOS实时操作系统和图形化配置工具。

写在前面

相信打过电赛的同学一定饱受TI MSPM0G3507的折磨,Keil这种上世纪风格的IDE(不只是UI),可以说几乎没有体验感,而TI自家的CCS,和STM32CubeIDE可以说是半斤八两,毕竟这俩都是从Eclipse魔改过来的,和keil相比也没多多少智能化设计,体验感上也只是从依托换到另依托。

CLion作为JetBrains家族的IDE,专精C/C++开发,可以说是非常好的选择,近几年也对STM32开发做了很多适配,也收获了很多好评,强大的代码补全、界面风格、各种插件、流畅性等众多优点吸引了一大批用户,本文为TI MSPM0G3507配置了一套CLion开发的工程模板。

核心功能

  • 完整的CLion工程配置
  • 预移植FreeRTOS
  • 集成TI SysConfig图形化外设配置
  • OpenOCD程序下载与调试
说明一下,目前只适配了XDS110调试器,openOCD在原生daplink上下载MSPM0经常锁死,所以目前XDS110是最稳妥的方案。

🛠 硬件要求

开发硬件

  • MSPM0G3507开发板
  • XDS110调试器

软件环境

🚀 快速开始

克隆仓库

git clone --recursive git clone https://gitee.com/xulijun_2003/MSPM0.git

仓库链接也放这里:
github:https://github.com/Xulijun180/Clion_MSPM0_Template
gitee:https://gitee.com/xulijun_2003/MSPM0

环境配置

下载arm-none-eabi-gcc:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
解压到一个文件夹,并把安装目录下的bin文件夹添加到环境变量:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
然后重启使得环境变量生效之后可以在命令行里用以下语句测试:
arm-none-eabi-gcc -v
如果有信息输出,那就是装好了。

openOCD配置

OpenOCD是用于对芯片进行下载仿真的工具,是一个开源软件包,但是注意,MSPM0G3507需要下载特定的open OCD版本,请参考这里,下载好解压到一个目录就行,后面会在Clion中链接这个目录

在Clion中链接openOCD的方法:
在这里插入图片描述

打开File-Settings-Build,Execution,Deployment选项卡,在Toolchains下面添加一个MinGW环境:C编译器和C++编译器填入相应的地址,配置完成后如图所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

进入Cmeke设置确认一下,选择工具链为配置好的STM32工具链

SYSCONFIG配置

  • 个人感觉sysconfig是TI开发工具链里体验感还算可以的软件,研究了一下在Clion中如何集成。
  • 首先,打开sysconfig安装目录,找到这个sysconfig_gui.bat,并记住这个文件所在的目录。

  • 打开Clion->设置->工具->外部工具,点击+号,添加一个外部工具,名称随便起,我这里就叫sysconfig,程序路径填写刚才记下的目录,注意这里的实参填$FileDir$\empty.syscfg,工作目录也如图填就可以,根据你安装的sysconfig目录自己做修改。设置完后点击确定即可。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 然后,在Clion中打开工程,在文件目录中找到empty.syscfg,右键该文件,选择使用外部工具sysconfig。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 正常的话就可以看到当前工程的sysconfig正常打开了,对工程的配置跟keil和CCS没有区别。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 配置完后点击保存,回到工程就可以看到文件已经更新。

编译及下载

前面的步骤都完成后,用Clion打开仓库提供的例程
在IDE底部的Cmake选项卡中如果没有提示错误,说明没有配置问题了
在这里插入图片描述
点击这个按钮可以更新工程Cmake。

顶部的这三个按钮分别是编译下载调试
在这里插入图片描述

点击编译可以看到编译输出:
在这里插入图片描述
可以看到输出了用于烧录的hex、bin以及elf文件

烧录程序以及在线调试

Clion烧录程序之前通用需要进行一些设置。
点击编译按钮旁边的配置栏下拉,选Edit Configurations,打开配置窗口:
在这里插入图片描述
在这个位置填入配置文件的路径,配置文件在工程目录里有提供,或者你也可以自己新建一个,内容如下:

source [find interface/xds110.cfg]
transport select swd
source [find target/ti_mspm0.cfg]
adapter speed 10000

设置好配置文件之后,就可以点击下载或者调试按钮进行下载和在线调试了。
Clion支持全功能的断点调试,还可以在代码里直接观察变量的值,体验感还是非常顺畅的
在这里插入图片描述

另外,添加相应的svd文件后,还可以在窗口内直接观察寄存器的值,svd文件我也放在工程目录里了
在这里插入图片描述

另外Clion对FreeRTOS的调试功能也有支持,可以在调试页面可以查看任务信息等

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

总结

  • 其实总体和Clion开发STM32没有太大的区别,只是集成了sysconfig,另外对FreeRTOS的调试功能做了适配,可以方便的查看任务信息等。

如果遇到任何CMake相关的报错,一般是由于缓存没有更新引起的,可以在CLion中选Tools-CMake-Reset Cache and Reload Project即可解决。

printf重定向问题

在Keil里面为了使用printf函数我们需要重定向fputc函数:

int fputc(int ch, FILE *stream)
{
   
    while (DL_UART_isBusy(UART0_INST) == true);

    DL_UART_Main_transmitData(UART0_INST, ch);

    return ch;
}

其中的FILE定义在stdio.h头文件中,所以需要在项目中包含这个头文件,但是经过测试发现,Keil里面包含的是MDK\ARM\ARMCC\include这个目录下的stdio.h,而在Clion中是不会链接到这个文件的。因此如果在Clion中也按之前的方法进行重定向,会发现printf没有任何输出。

在Clion中链接的是GNU-Tools-ARM-Embedded\arm-none-eabi\include里面的stdio.h,如果仍然想使用printf函数功能,则需要进行如下操作:

新建一个retarget.h文件内容如下:

#ifndef _RETARGET_H__

#define _RETARGET_H__

#include "ti_msp_dl_config.h"

#include <sys/stat.h>

#include <stdio.h>

void RetargetInit(UART_Regs *huart);

int _isatty(int fd);

int _write(int fd, char *ptr, int len);

int _close(int fd);

int _lseek(int fd, int ptr, int dir);

int _read(int fd, char *ptr, int len);

int _fstat(int fd, struct stat *st);

#endif //#ifndef _RETARGET_H__

再新建一个retarget.c文件内容如下:

#include <_ansi.h>

#include <_syslist.h>

#include <errno.h>

#include <sys/time.h>

#include <sys/times.h>

#include <retarget.h>

#include <stdint.h>

#if !defined(OS_USE_SEMIHOSTING)

#define STDIN_FILENO  0

#define STDOUT_FILENO 1

#define STDERR_FILENO 2


UART_Regs* gHuart;


void RetargetInit(UART_Regs* huart)
{
    gHuart = huart;
    /* Disable I/O buffering for STDOUT stream, so that

 * chars are sent out as soon as they are printed. */
    setvbuf(stdout, NULL, _IONBF, 0);
}


int _isatty(int fd)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
        return 1;
    errno = EBADF;
    return 0;
}


int _write(int fd, char* ptr, int len)
{
    if (fd == STDOUT_FILENO || fd == STDERR_FILENO)
    {
        for (int i = 0; i < len; i++)
        {
            // 阻塞发送一个字节
            DL_UART_Main_transmitData(gHuart, (uint8_t)ptr[i]);
            // 如果硬件需要等待发送完成,这里可能要加等待
            while (DL_UART_isBusy(gHuart) == true);
        }
        return len;
    }
    errno = EBADF;
    return -1;
}


int _close(int fd)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
        return 0;
    errno = EBADF;
    return -1;
}


int _lseek(int fd, int ptr, int dir)
{
    (void)fd;
    (void)ptr;
    (void)dir;
    
    errno = EBADF;
    return -1;
}


int _read(int fd, char* ptr, int len)

{
    if (fd == STDIN_FILENO)
    {
        DL_UART_Main_receiveData(gHuart);
        return 1;
    }
    errno = EBADF;
    return -1;
}

int _fstat(int fd, struct stat* st)
{
    if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)
    {
        st->st_mode = S_IFCHR;
        return 0;
    }
    errno = EBADF;
    return 0;
}

#endif //#if !defined(OS_USE_SEMIHOSTING)

再新建一个syscall.c如下:

/**
*****************************************************************************
**
**  File        : syscalls.c
**
**  Author		: Auto-generated by System workbench for STM32
**
**  Abstract    : System Workbench Minimal System calls file
**
** 		          For more information about which c-functions
**                need which of these lowlevel functions
**                please consult the Newlib libc-manual
**
**  Target      : STMicroelectronics STM32
**
**  Distribution: The file is distributed ��as is,�� without any warranty
**                of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>&copy; COPYRIGHT(c) 2019 STMicroelectronics</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
**   1. Redistributions of source code must retain the above copyright notice,
**      this list of conditions and the following disclaimer.
**   2. Redistributions in binary form must reproduce the above copyright notice,
**      this list of conditions and the following disclaimer in the documentation
**      and/or other materials provided with the distribution.
**   3. Neither the name of STMicroelectronics nor the names of its contributors
**      may be used to endorse or promote products derived from this software
**      without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/

/* Includes */
#include <sys/stat.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/times.h>


/* Variables */
//#undef errno
extern int errno;
extern int __io_putchar(int ch) __attribute__((weak));
extern int __io_getchar(void) __attribute__((weak));

register char * stack_ptr asm("sp");

char *__env[1] = { 0 };
char **environ = __env;


/* Functions */
void initialise_monitor_handles()
{
}

int _getpid(void)
{
	return 1;
}

int _kill(int pid, int sig)
{
	errno = EINVAL;
	return -1;
}

void _exit (int status)
{
	_kill(status, -1);
	while (1) {}		/* Make sure we hang here */
}

__attribute__((weak)) int _read(int file, char *ptr, int len)
{
	int DataIdx;

	for (DataIdx = 0; DataIdx < len; DataIdx++)
	{
		*ptr++ = __io_getchar();
	}

return len;
}

__attribute__((weak)) int _write(int file, char *ptr, int len)
{
	int DataIdx;

	for (DataIdx = 0; DataIdx < len; DataIdx++)
	{
		__io_putchar(*ptr++);
	}
	return len;
}















int _unlink(char *name)
{
	errno = ENOENT;
	return -1;
}

int _times(struct tms *buf)
{
	return -1;
}

int _stat(char *file, struct stat *st)
{
	st->st_mode = S_IFCHR;
	return 0;
}

int _link(char *old, char *new)
{
	errno = EMLINK;
	return -1;
}

int _fork(void)
{
	errno = EAGAIN;
	return -1;
}

int _execve(char *name, char **argv, char **env)
{
	errno = ENOMEM;
	return -1;
}

在main函数的初始化代码中添加对头文件的引用并注册重定向的串口号:

#include "retarget.h"

RetargetInit(UART_0_INST);
...

然后就可以正常使用printf了

后续我会将这几个文件直接到仓库里,可以打开即用

注意

  • 区别于此前网上流传的Clion开发STM32环境,本模板为个人移植,没有官方支持,可能存在未知的问题,请谨慎使用,当然也欢迎大家提issue或者PR指出并且一起完善这套模板
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐