建立工程框架
本节介绍
📝本节您将学习如何搭建基于 Keil 的代码编辑调试环境,包括文件管理、串口调试、LED 调试等。
🏆本章⽬标
1️⃣工程的创建,并了解分层架构的概念。
2️⃣开发板板载外设 LED 和串口的配置与使用。
工程创建
准备工程文件
TI 的 MSPM0G3507-keil 工程在 M0-SDK 已经创建好了,因为涉及到很多文件和功能,如果我们自己创建配置的话太过繁琐,这里直接去复制 M0-SDK 下的空白工程 empty。
![]() |
---|
复制到我工程的文件夹后,我重新命名为 01_template_project
。
![]() |
---|
为了让工程更加方便的分享给其他用户,让其他人收到后直接编译不会报错,我们还得将一些核心文件复制到我们的工程下。
将 M0-SDK 下的 source
和 .metadata
文件夹复制到我们的工程下,同我们的工程同一个层级。
![]() |
---|
将 <M0-SDK路径>\tools\keil\
下的 syscfg.bat
文件复制到我们的工程下,同我们的工程同一个层级。
![]() |
---|
现在外部文件已经准备完毕,打开我们的 01_template_project
工程。
![]() |
---|
工程配置
在 keil 中是支持 sysconfig 这个图形化代码生成工具的,但是我们将工程从 M0-SDK 中复制了出来,导致工程中一些路径配置就不对了,需要我们重新配置。
打开我们工程的魔法棒配置,先配置 User
下的命令。该命令是当图形化代码生成工具生成代码时,能够修改我们工程中的文件。
![]() |
---|
更新命令如下:
cmd.exe /C "$P../../syscfg.bat '$P' empty.syscfg"
更新完成后,打开 C/C++(AC6) 选项,我们配置工程头文件的路径。
![]() |
---|
配置路径为新路径,并添加我们工程本身的根目录路径:
![]() |
---|
完成后保存,再打开 Linker 选项页,我们配置链接器。
![]() |
---|
将 Misc controls
中的路径,改为以下新路径:
../../source/ti/driverlib/lib/keil/m0p/mspm0g1x0x_g3x0x/driverlib.a
完成后保存,并编译工程。正常的话是没有报错的。
![]() |
---|
文件管理
分层架构介绍
为了不让代码变的乱七八糟,文件的管理使用的是三分层架构。
主要分为三层:最高层的应用层、中间层的协议层、底层的硬件层。
硬件层(hardware)
硬件层主要处理主控外设和外部模块的硬件程序,比如彩屏显示驱动、电机的PWM速度正反转控制、编码器数据获取、按键状态获取等等。
中间层(middle)
中间层主要是一些协议数据或开源库的处理,比如按键库、PID库等。还有把采集到的信号转换成具体的值,比如把ADC转换成电量。
应用层(app)
应用层主要是该项目的逻辑程序,比如该项目的UI、任务切换、具体的项目逻辑等。
分层管理的优点
功能清晰
:每层干啥一目了然,改代码不用满世界找。扩展性强
:加个新功能?直接在应用层插一块就行,下层不动。复用性高
:底层写好了,别的项目也能拿来用。
使用分层架构时,尽量做到每层只跟旁边的层打交道,互不越界。做着做着代码能力就提升了。
创建分层
在我们的工程下,新建三个文件夹,hardware
、middle
、app
。
这里可以给我们的工程减一下身
,删除掉 gcc
、iar
、ticlang
文件夹。
![]() | ![]() |
---|---|
在keil中,对我们的工程也建立三个虚拟文件夹,hardware
、middle
、app
。
![]() | ![]() |
---|---|
将三个文件夹的路径添加到工程头文件路径中。
![]() |
---|
调试功能配置
程序开发过程中,必须做的一件事是排查Bug和修复Bug。那如何排查Bug?通过软硬件调试的手段,定位到BUG点进行修复。
调试的方法有很多,这里就先准备三种调试功能。
三种调试功能
仿真器+keil的程序仿真功能,实时查看和修改内存、寄存器值,设置断点、单步执行等。这个不需要在代码进行配置,只需要选择一个支持仿真功能的仿真下载器即可。
通过编写代码配置串口(UART)接口,与电脑进行串行通信,将程序运行时的信息、错误代码、调试日志等通过串口发送到电脑上的串口调试助手软件,从而实时监控程序运行状态。
通过编写代码控制LED灯的闪烁频率、模式等,来表示系统的不同状态或错误代码。
配置工程为 80MHz
打开工程的图形化代码生成工具。首先配置系统寄存器,开启时钟树配置功能。
![]() |
---|
打开时钟树,配置 HFXT 为 40MHZ,并等待10个时钟周期的时钟起振时间;设置HFCLK的时钟来源为 XTAL。
![]() |
---|
配置 SYSPLL 如下:
![]() |
---|
因 ULPCLK 只能在40MHZ及以下,所以还要对 ULPCLK 进行分频。
![]() |
---|
完成后保存并更新代码到keil中。
配置调试LED
开发板上板载了一个用户LED,它接到了GPIOB22引脚。根据入门手册中的点亮LED灯章节配置就好。
![]() | ![]() |
---|---|
配置完成后,在我们工程下的 middle 文件夹下新建两个工程:mid_debug_led.c
和 mid_debug_led.h
。将.c文件添加到我们keil工程的 middle 虚拟文件夹下。
![]() | ![]() |
---|---|
往两个文件中添加代码:
#include "mid_debug_led.h"
static DEBUG_LED_STRUCT debug_led;
// 打开LED
void set_debug_led_on(void)
{
debug_led.state = LED_ON;
DL_GPIO_setPins(DEBUG_LED_PORT, DEBUG_LED_PIN_22_PIN);
}
// 关闭LED
void set_debug_led_off(void)
{
debug_led.state = LED_OFF;
DL_GPIO_clearPins(DEBUG_LED_PORT, DEBUG_LED_PIN_22_PIN);
}
// 切换LED状态并操作灯
void set_debug_led_toggle(void)
{
if( debug_led.state == LED_ON )
{
set_debug_led_off();
}
else if( debug_led.state == LED_OFF )
{
set_debug_led_on();
}
}
// 获取LED的状态
LED_STATE_ENUM get_debug_led_state(void)
{
return debug_led.state;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#ifndef _MID_DEBUG_LED_H_
#define _MID_DEBUG_LED_H_
#include "ti_msp_dl_config.h"
// 定义LED状态
typedef enum {
LED_OFF,
LED_ON
} LED_STATE_ENUM;
// 定义调试LED结构体
typedef struct {
LED_STATE_ENUM state; // LED当前状态
} DEBUG_LED_STRUCT;
void set_debug_led_on(void);
void set_debug_led_off(void);
void set_debug_led_toggle(void);
LED_STATE_ENUM get_debug_led_state(void);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
配置调试串口
在开发板上有集成了一块 CH340E,CH340E 是一款流行的 USB 转串口芯片,广泛用于各种需要串口通信的设备中。它提供了一种低成本、便捷的方式将 USB 接口转换成标准的串口(RS232/RS485/TTL),广泛应用于单片机开发、嵌入式系统、通信设备等多个领域。
CH340E 接入到了开发板主控的 PA10 与 PA11 引脚,我们可以通过一根数据线,接入到开发板的TYPE-C接口上,即可与开发板进行串口通信。也可以接到开发板下载排针口下方的TX、RX引脚。
![]() |
---|
关于串口外设的配置,只需要根据入门手册中的串口通信章节配置就好。
![]() |
---|
配置完成后,在我们工程下的 middle 文件夹下新建两个工程:mid_debug_uart.c
和 mid_debug_uart.h
。将.c文件添加到我们keil工程的 middle 虚拟文件夹下。
![]() | ![]() |
---|---|
往两个文件中添加代码:
#include "mid_debug_uart.h"
#include "string.h"
DEBUG_UART_STRUCT debug_uart;
// 初始化调试串口
void debug_uart_init(void)
{
NVIC_ClearPendingIRQ(UART_DEBUG_INST_INT_IRQN);
NVIC_EnableIRQ(UART_DEBUG_INST_INT_IRQN);
}
//发送单个字符
void debug_uart_send_char(char ch)
{
while( DL_UART_isBusy(UART_DEBUG_INST) == true );
DL_UART_Main_transmitData(UART_DEBUG_INST, ch);
}
//发送字符串
void debug_uart_send_string(char* str)
{
while(*str!=0&&str!=0)
{
debug_uart_send_char(*str++);
}
}
// 获取调试串口接收到的数据
char* get_debug_uart_receive_data(void)
{
return debug_uart.receive_buffer;
}
// 清除调试串口接收到的数据
void clear_debug_uart_receive_data(void)
{
int i=0;
debug_uart.receive_data_length = 0;
for( i = 0; i < REVEIVE_BUFFER_MAX; i++ )
{
debug_uart.receive_buffer[i] = 0;
}
}
// 限制接收长度
static int debug_uart_receive_limit_length(void)
{
if( debug_uart.receive_data_length >= REVEIVE_BUFFER_MAX )
{
debug_uart.receive_data_length = 0;
return 0;
}
return 1;
}
//调试串口的中断服务函数
void UART_DEBUG_INST_IRQHandler(void)
{
if( DL_UART_getPendingInterrupt(UART_DEBUG_INST) == DL_UART_IIDX_RX )
{
debug_uart.receive_buffer[ debug_uart.receive_data_length++ ] = DL_UART_Main_receiveData(UART_DEBUG_INST);
debug_uart_receive_limit_length();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#ifndef _MID_DEBUG_UART_H_
#define _MID_DEBUG_UART_H_
#include "ti_msp_dl_config.h"
#define REVEIVE_BUFFER_MAX 127
// 定义调试串口结构体
typedef struct {
char receive_buffer[ REVEIVE_BUFFER_MAX ];
int receive_data_length;
}DEBUG_UART_STRUCT;
void debug_uart_init(void);
void debug_uart_send_char(char ch);
void debug_uart_send_string(char* str);
char* get_debug_uart_receive_data(void);
void clear_debug_uart_receive_data(void);
#endif
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
调试功能验证
在工程的 empty.c 中编写以下代码:
#include "ti_msp_dl_config.h"
#include "mid_debug_led.h"
#include "mid_debug_uart.h"
#include "string.h"
int main(void)
{
SYSCFG_DL_init();
debug_uart_init();
while (1)
{
debug_uart_send_string("run 01_template_project demo\r\n");
if( strstr(get_debug_uart_receive_data(), "toggle led") != NULL)
{
set_debug_led_toggle();
clear_debug_uart_receive_data();
}
delay_cycles(CPUCLK_FREQ / 1000 * 200);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
下载器我使用的是 J-LINK,接线如下:
J-LINK | 开发板 |
---|---|
5V | 5V |
CLK | CLK |
SWD | DIO |
GND | GND |
TXD | RX |
RXD | TX |
![]() |
---|
工程编译下载到开发板中。查看现象:
电脑串口助手会接收到开发板发送过来的串口数据,并且通过电脑的串口助手发送字符串 "toggle led"给开发板时,会让开发板上的LED灯状态翻转。
![]() |
---|
下载后程序不正常怎么办?
将工程的优化等级调整为 O0
,即不优化。