1、创建一个GD32E230C8T6的模板工程,通过官网给的标准库中的模板直接移植过来GD32E23x资源下载当前软件库版本为:GD32E23x_Firmware_Library_V1.2.0
2、库文件如下:
3、删除无用的文件只保留必要的文件:
Examples:工程中自带的示例文件,用来做参考直接删除
IAR:此文件夹是用IAR开发时候才用到,本次使用MDK5这个文件夹直接删除
IAR_Project:也是用IAR开发时候才用到,这里直接删除
Utilities:是第三方功能用到的文件,直接删除
4、留下的文件如下:
5、在LetterShell的Gitee上下载源码Letter_Shell_GitHub本次使用的是letter shell 3.x
6、打开MDK工程先把GD32E230的工程和串口配置配置好,由于删除了一部分库里带的文件,编译时候会报错,根据错误提示将不需要的配置从MDK中移除
7、移除后的工程中不需要的代码页删除,只保留基本的框架
8、开始系统时钟和串口的配置
void systick_config(void)
{
/* setup systick timer for 1000Hz interrupts */
if(SysTick_Config(SystemCoreClock / 1000U)){ //将系统滴答定时器设置为1Khz,就是1ms触发一次滴答定时器
/* capture error */
while(1){
}
}
/* configure the systick handler priority */
NVIC_SetPriority(SysTick_IRQn, 0x00U);//使能系统中断功能(1ms进入一次滴答定时器中断)
}
9、SystemCoreClock是CPU时钟的配置需要根据开发板来定,本次使用的是内部高速时钟经过倍频使用,在system_gd32e23x.c文件中有关于系统时钟配置的宏定义
/* select a system clock by uncommenting the following line */
//#define __SYSTEM_CLOCK_8M_HXTAL (__HXTAL) //外部高速时钟
//#define __SYSTEM_CLOCK_8M_IRC8M (__IRC8M) //内部高速时钟
//#define __SYSTEM_CLOCK_72M_PLL_HXTAL (uint32_t)(72000000) //经过外部高速时钟配置后Core时钟会倍频为72Mhz
//#define __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2 (uint32_t)(72000000) //经过内部高速时钟配置后Core时钟会倍频为72Mhz
10、根据上边的宏定义system_gd32e23x.c文件中会自动通过下边的默认配置进行系统时钟的配置
#ifdef __SYSTEM_CLOCK_8M_HXTAL
uint32_t SystemCoreClock = __SYSTEM_CLOCK_8M_HXTAL;
static void system_clock_8m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_HXTAL)
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_HXTAL;
static void system_clock_72m_hxtal(void);
#elif defined (__SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2) //本次采用的是内部高速时钟经过pll倍频作为系统时钟
uint32_t SystemCoreClock = __SYSTEM_CLOCK_72M_PLL_IRC8M_DIV2;
static void system_clock_72m_irc8m(void);//系统时钟配置调用的函数
#else
uint32_t SystemCoreClock = __SYSTEM_CLOCK_8M_IRC8M;
static void system_clock_8m_irc8m(void);
#endif /* __SYSTEM_CLOCK_8M_HXTAL */
11、系统时钟树参考官方手册
12、经过pll倍频时候倍频系数在被调用的函数中也可以看到
static void system_clock_72m_irc8m(void)
{
uint32_t timeout = 0U;
uint32_t stab_flag = 0U;
/* enable IRC8M */
RCU_CTL0 |= RCU_CTL0_IRC8MEN;
/* wait until IRC8M is stable or the startup time is longer than IRC8M_STARTUP_TIMEOUT */
do{
timeout++;
stab_flag = (RCU_CTL0 & RCU_CTL0_IRC8MSTB);
}
while((0U == stab_flag) && (IRC8M_STARTUP_TIMEOUT != timeout));
/* if fail */
if(0U == (RCU_CTL0 & RCU_CTL0_IRC8MSTB)){
while(1){
}
}
FMC_WS = (FMC_WS & (~FMC_WS_WSCNT)) | WS_WSCNT_2;
/* AHB = SYSCLK */
RCU_CFG0 |= RCU_AHB_CKSYS_DIV1;
/* APB2 = AHB */
RCU_CFG0 |= RCU_APB2_CKAHB_DIV1;
/* APB1 = AHB */
RCU_CFG0 |= RCU_APB1_CKAHB_DIV1;
/* PLL = (IRC8M/2) * 18 = 72 MHz */ //根据默认配置,内部高速时钟通过2分频再经过18倍频将系统时钟设置为72Mhz
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PLLMF);
RCU_CFG0 |= (RCU_PLLSRC_IRC8M_DIV2 | RCU_PLL_MUL18);
/* enable PLL */
RCU_CTL0 |= RCU_CTL0_PLLEN;
/* wait until PLL is stable */
while(0U == (RCU_CTL0 & RCU_CTL0_PLLSTB)){
}
/* select PLL as system clock */
RCU_CFG0 &= ~RCU_CFG0_SCS;
RCU_CFG0 |= RCU_CKSYSSRC_PLL;
/* wait until PLL is selected as system clock */
while(RCU_SCSS_PLL != (RCU_CFG0 & RCU_CFG0_SCSS)){
}
}
13、在工程目录下创建bsp文件夹并创建如下文件,将文件添加到MDK工程中(记得在MDK中添加BSP文件夹的路径)
14、先配置串口驱动
/**
* USART0 TX PA9
* USART0 RX PA10
*/
void bsp_letter_shell_driver_init(void)
{
//使能串口用的相关时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
//使能对应的串口中断,优先级设置为0最高
nvic_irq_enable(USART0_IRQn, 0);
/*设置引脚的复用功能*/
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
/*设置引脚的输出模式和输出类型*/
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
/*配置串口的基本参数*/
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_word_length_set(USART0, USART_WL_8BIT);
/*使能串口的收发功能*/
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
/*使能串口配置和串口接收完成中断*/
usart_enable(USART0);
usart_interrupt_enable(USART0, USART_INT_RBNE);//使能读数据缓冲区非空中断标志位
}
15、重定义串口中断函数
void USART0_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)){
char data = 0;
data = usart_data_receive(USART0);//接收一个字符
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
}
}
16、至此串口驱动部分就编写完成,开始配置letter shell部分的源码,下载解压后如下:
17、由于只使用shell的基础命令,因此删除不需要的其他文件
18、lettershell的移植可以之际参考Readme.md文件,讲解的非常详细
创建一个shell对象
定义收发函数,本次使用的是裸机程序,因此只用设置发函数就行,收函数不用定义,直接在串口中断函数中调用shellHandler()函数即可
19、最终添加的代码如下
#include "bsp_uart.h"
#include "shell.h"
Shell shell;//创建一个Shell对象
char Shell_Buf[128] = {0};//给shell设置一个缓存区
signed short userShellWrite(char *data, unsigned short len)
{
/*通过Shell发送一串数据*/
for(int32_t i=0; i< len; i++)
{
usart_data_transmit(USART0, (uint8_t) *data++);
while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
}
/*返回写如的数据长度(字节)*/
return len;
}
/**
* USART0 TX PA9
* USART0 RX PA10
*/
void bsp_letter_shell_driver_init(void)
{
//使能串口用的相关时钟
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_USART0);
//使能对应的串口中断,优先级设置为0最高
nvic_irq_enable(USART0_IRQn, 0);
/*设置引脚的复用功能*/
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_9);
gpio_af_set(GPIOA, GPIO_AF_1, GPIO_PIN_10);
/*设置引脚的输出模式和输出类型*/
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
/*配置串口的基本参数*/
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_word_length_set(USART0, USART_WL_8BIT);
/*使能串口的收发功能*/
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
/*使能串口配置和串口接收完成中断*/
usart_enable(USART0);
usart_interrupt_enable(USART0, USART_INT_RBNE);//使能读数据缓冲区非空中断标志位
/*初始化Letter Shell 由于本次是裸机程序不用设置读函数*/
shell.write = userShellWrite;
shellInit(&shell,Shell_Buf,sizeof(Shell_Buf));
}
void USART0_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE)){
char data = 0;
data = usart_data_receive(USART0);
/*中断中直接调用shellHandler()*/
shellHandler(&shell,data);
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE);
}
}
20、在main函数中初始化串口配置
int main(void)
{
/* configure systick */
systick_config();
bsp_letter_shell_driver_init();
while(1){
;
}
}
21、修改Letter SHell的配置文件shell_cfg.h
#ifndef SHELL_TASK_WHILE
/**
* @brief 是否使用默认shell任务while循环
* 使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
* 任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
*/
#define SHELL_TASK_WHILE 0 //本次使用裸机就用不到任务,关闭这个宏定义
#endif /** SHELL_TASK_WHILE */
22、到此Letter Shell移植完成,修改用户名为自己的名字
#ifndef SHELL_DEFAULT_USER
/**
* @brief shell默认用户
*/
#define SHELL_DEFAULT_USER "YangJunbo"
#endif /** SHELL_DEFAULT_USER */
23、设置用户密码为
#ifndef SHELL_DEFAULT_USER_PASSWORD
/**
* @brief shell默认用户密码
* 若默认用户不需要密码,设为""
*/
#define SHELL_DEFAULT_USER_PASSWORD "123456"
#endif /** SHELL_DEFAULT_USER_PASSWORD */
24、将程序下载后打开串口中断,按下回车键,提示输入密码
25、输入help回车就会看到已经有的命令了
26、添加自定义的命令
/*letter shell cmd test*/
void letter_shell_test(char ch)
{
switch(ch)
{
case 0:
{
userShellWrite((char *)"recive shell value 0\r\n",sizeof("recive shell value 0\r\n"));
break;
}
case 1:
{
userShellWrite((char *)"recive shell value 1\r\n",sizeof("recive shell value 1\r\n"));
break;
}
default:
{
userShellWrite((char *)"recive shell value xxx\r\n",sizeof("recive shell value xxx\r\n"));
}
}
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_DISABLE_RETURN,shell_test, letter_shell_test, "shell test code");//设置命令没有返回值,类型是执行一个函数,命令带参数
27、最后的执行结果如下所示,基本的letter shell命令就移植完成了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。