【stm32f1xx HAL库 freertos 移植letter-shell和log完整记录】

简介

本文使用stm32f103进行测试,移植 letter-shell 3.12版本

letter-shell 项目地址: https://github.com/NevermindZZT/letter-shell

使用cubemx创建工程

在这里插入图片描述
选择自己的芯片,我这里使用的stm32f103CBT6
在这里插入图片描述

配置RCC,使用外部晶振

在这里插入图片描述

配置SYS

我使用st-link下载程序,调试接口选择SW,由于要使用freertos,系统滴答时钟会默认作为freertos的时基,这里选择滴答时钟会有警告,建议换成除滴答时钟外的其他定时器,我这里选的TIM1.
在这里插入图片描述

配置串口

模式选择异步模式,开启串口中断,其他参数可以不变,有需要可自行修改。
在这里插入图片描述

配置freertos

在这里插入图片描述

设置时钟树

在这里插入图片描述

设置工程目录及保存方式

保存为keil 5工程,后续也可使用vscode编写程序
在这里插入图片描述
在这里插入图片描述

移植 letter-shell

将 letter-shell 源码下载到本地并解压,将项目中src目录和demo/stm32freertos中的文件放到stm32工程目录中,
在这里插入图片描述

对代码进行修改,主要修改shell_port.c。实现读写函数,如果使用中断则可不实现读函数,使用阻塞方式读,可能会造成卡死以及数据丢失。没有使用锁,在使用锁的情况下,使用串口中断读数据会造成程序卡死。

/***shell_port.c****/
#include "FreeRTOS.h"
#include "task.h"
#include "shell.h"
#include "stm32f1xx_hal.h"
#include "usart.h"
#include "shell_port.h"

Shell shell;
char shellBuffer[512];

/**
 * @brief 用户shell写
 * 
 * @param data 数据
 * @param len 数据长度
 * 
 * @return short 实际写入的数据长度
 */
short userShellWrite(char *data, unsigned short len)
{
    HAL_UART_Transmit(&huart1, (uint8_t *)data, len, 0x1FF);
    return len;
}

 * @brief 用户shell初始化
 * 
 */
void userShellInit(void)
{
    shell.write = userShellWrite;
    shellInit(&shell, shellBuffer, 512);
}

修改配置文件shell_cfg.h,具体参数可参考原项目

/**
 * @file shell_cfg.h
 * @author Letter (nevermindzzt@gmail.com)
 * @brief shell config
 * @version 3.0.0
 * @date 2019-12-31
 * 
 * @copyright (c) 2019 Letter
 * 
 */

#ifndef __SHELL_CFG_H__
#define __SHELL_CFG_H__

#include "stm32f1xx_hal.h"
#include "FreeRTOS.h"
//#include "portable.h"

/**
 * @brief 是否使用默认shell任务while循环,使能宏`SHELL_USING_TASK`后此宏有意义
 *        使能此宏,则`shellTask()`函数会一直循环读取输入,一般使用操作系统建立shell
 *        任务时使能此宏,关闭此宏的情况下,一般适用于无操作系统,在主循环中调用`shellTask()`
 */
#define     SHELL_TASK_WHILE            1

/**
 * @brief 是否使用命令导出方式
 *        使能此宏后,可以使用`SHELL_EXPORT_CMD()`等导出命令
 *        定义shell命令,关闭此宏的情况下,需要使用命令表的方式
 */
#define     SHELL_USING_CMD_EXPORT      1

/**
 * @brief 是否使用shell伴生对象
 *        一些扩展的组件(文件系统支持,日志工具等)需要使用伴生对象
 */
#define     SHELL_USING_COMPANION       0

/**
 * @brief 支持shell尾行模式
 */
#define     SHELL_SUPPORT_END_LINE      1

/**
 * @brief 是否在输出命令列表中列出用户
 */
#define     SHELL_HELP_LIST_USER        0

/**
 * @brief 是否在输出命令列表中列出变量
 */
#define     SHELL_HELP_LIST_VAR         0

/**
 * @brief 是否在输出命令列表中列出按键
 */
#define     SHELL_HELP_LIST_KEY         0

/**
 * @brief 是否在输出命令列表中展示命令权限
 */
#define     SHELL_HELP_SHOW_PERMISSION  1

/**
 * @brief 使用LF作为命令行回车触发
 *        可以和SHELL_ENTER_CR同时开启
 */
#define     SHELL_ENTER_LF              1

/**
 * @brief 使用CR作为命令行回车触发
 *        可以和SHELL_ENTER_LF同时开启
 */
#define     SHELL_ENTER_CR              1

/**
 * @brief 使用CRLF作为命令行回车触发
 *        不可以和SHELL_ENTER_LF或SHELL_ENTER_CR同时开启
 */
#define     SHELL_ENTER_CRLF            0

/**
 * @brief 使用执行未导出函数的功能
 *        启用后,可以通过`exec [addr] [args]`直接执行对应地址的函数
 * @attention 如果地址错误,可能会直接引起程序崩溃
 */
#define     SHELL_EXEC_UNDEF_FUNC       0

/**
 * @brief shell命令参数最大数量
 *        包含命令名在内,超过16个参数并且使用了参数自动转换的情况下,需要修改源码
 */
#define     SHELL_PARAMETER_MAX_NUMBER  8

/**
 * @brief 历史命令记录数量
 */
#define     SHELL_HISTORY_MAX_NUMBER    5

/**
 * @brief 双击间隔(ms)
 *        使能宏`SHELL_LONG_HELP`后此宏生效,定义双击tab补全help的时间间隔
 */
#define     SHELL_DOUBLE_CLICK_TIME     200

/**
 * @brief 快速帮助
 *        作用于双击tab的场景,当使能此宏时,双击tab不会对命令进行help补全,而是直接显示对应命令的帮助信息
 */
#define     SHELL_QUICK_HELP            1

/**
 * @brief 保存命令返回值
 *        开启后会默认定义一个`RETVAL`变量,会保存上一次命令执行的返回值,可以在随后的命令中进行调用
 *        如果命令的`SHELL_CMD_DISABLE_RETURN`标志被设置,则该命令不会更新`RETVAL`
 */
#define     SHELL_KEEP_RETURN_VALUE     0

/**
 * @brief 管理的最大shell数量
 */
#define     SHELL_MAX_NUMBER            5

/**
 * @brief shell格式化输出的缓冲大小
 *        为0时不使用shell格式化输出
 */
#define     SHELL_PRINT_BUFFER          128

/**
 * @brief shell格式化输入的缓冲大小
 *        为0时不使用shell格式化输入
 * @note shell格式化输入会阻塞shellTask, 仅适用于在有操作系统的情况下使用
 */
#define     SHELL_SCAN_BUFFER          0

/**
 * @brief 获取系统时间(ms)
 *        定义此宏为获取系统Tick,如`HAL_GetTick()`
 * @note 此宏不定义时无法使用双击tab补全命令help,无法使用shell超时锁定
 */
#define     SHELL_GET_TICK()            HAL_GetTick()

/**
 * @brief 使用锁
 * @note 使用shell锁时,需要对加锁和解锁进行实现
 */
#define     SHELL_USING_LOCK            0

/**
 * @brief shell内存分配
 *        shell本身不需要此接口,若使用shell伴生对象,需要进行定义
 */
#define     SHELL_MALLOC(size)          0

/**
 * @brief shell内存释放
 *        shell本身不需要此接口,若使用shell伴生对象,需要进行定义
 */
#define     SHELL_FREE(obj)             0

/**
 * @brief 是否显示shell信息
 */
#define     SHELL_SHOW_INFO             1

/**
 * @brief 是否在登录后清除命令行
 */
#define     SHELL_CLS_WHEN_LOGIN        1

/**
 * @brief shell默认用户
 */
#define     SHELL_DEFAULT_USER          "BMS_BQ"

/**
 * @brief shell默认用户密码
 *        若默认用户不需要密码,设为""
 */
#define     SHELL_DEFAULT_USER_PASSWORD ""

/**
 * @brief shell自动锁定超时
 *        shell当前用户密码有效的时候生效,超时后会自动重新锁定shell
 *        设置为0时关闭自动锁定功能,时间单位为`SHELL_GET_TICK()`单位
 * @note 使用超时锁定必须保证`SHELL_GET_TICK()`有效
 */
#define     SHELL_LOCK_TIMEOUT          0 * 60 * 1000

#endif

修改freertos,使用freertos创建shell任务,使用cubemx创建两个任务其中一个为letter-shell,另一个为默认的测试任务。letter-shell任务如下,任务函数使用 As weak,创建一个弱函数,会自动在程序中寻找相同的函数名。
在这里插入图片描述
freertos部分程序如下,添加串口中断,在初始化阶段加入letter-shell初始化和串口接收中断开启函数。

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "shell_port.h"
#include "usart.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t my_uart1_redata;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

/* USER CODE END Variables */
osThreadId myshellHandle;
osThreadId myTask02Handle;

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void shellTask(void const * argument);
void StartTask02(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );

/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
  *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
  *ppxIdleTaskStackBuffer = &xIdleStack[0];
  *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
  /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
  userShellInit();
	HAL_UART_Receive_IT(&huart1,(uint8_t *)&my_uart1_redata,1);
  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* definition and creation of myshell */
  osThreadDef(myshell, shellTask, osPriorityNormal, 0, 256);
  myshellHandle = osThreadCreate(osThread(myshell), (void*) &shell);

  /* definition and creation of myTask02 */
  osThreadDef(myTask02, StartTask02, osPriorityIdle, 0, 128);
  myTask02Handle = osThreadCreate(osThread(myTask02), NULL);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

}

/* USER CODE BEGIN Header_shellTask */
/**
  * @brief  Function implementing the myshell thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_shellTask */
__weak void shellTask(void const * argument)
{
  /* USER CODE BEGIN shellTask */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END shellTask */
}

/* USER CODE BEGIN Header_StartTask02 */
/**
* @brief Function implementing the myTask02 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask02 */
void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartTask02 */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/// shell 测试函数
void shell_test()
{
  uint8_t hello1[] = "Hello, blocking\n";
  HAL_UART_Transmit(&huart1, hello1, sizeof(hello1), 500);
	
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC), 
	test, shell_test, shell test);

//串口收到数据回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
    if(huart->Instance == USART1)//判断串口号
    {
        //发送
		//HAL_UART_Transmit(&huart1,&my_uart1_redata,1,100);
		shellHandler(&shell, my_uart1_redata);
        
        //开启一次中断
        HAL_UART_Receive_IT(&huart1,(uint8_t *)&my_uart1_redata,1);
    }
}
/* USER CODE END Application */

修改 shell.c 部分中的 shellTask 函数,加入osDelay 延时函数,防止程序卡死,由于没有实现shell->read函数,故将部分代码删除,cubemx生成的函数默认使用const修饰,这里也进行修改,添加const,shell.h中的函数名也需要做相同修改。

void shellTask(void const *param)
{
    Shell *shell = (Shell *)param;
    char data;
#if SHELL_TASK_WHILE == 1
    while(1)
    {
#endif
		osDelay(10);

#if SHELL_TASK_WHILE == 1
    }
#endif
}

编译程序进行测试,终端软件使用 MobaXterm
在这里插入图片描述

添加log

log文件在 extensions\log 中,同样添加到工程中,在 shell_port.c 中添加log的支持

#include "log.h"

//使用尾行模式
void uartLogWrite(char *buffer, short len);
// level 显示的日志等级,高于level的等级不显示,具体的日志等级可以看log.h文件
Log uartLog = {
.write = uartLogWrite,
.active = LOG_ENABLE,
.level = LOG_DEBUG
};

void uartLogWrite(char *buffer, short len)
{
if (uartLog.shell)
{
	shellWriteEndLine(uartLog.shell, buffer, len);
}
}

/**
 * @brief 用户shell初始化
 * 
 */
 //在shell_port.c 的shell初始化中添加
void userShellInit(void)
{
    shell.write = userShellWrite;
    shellInit(&shell, shellBuffer, 512);
    //添加log初始化
	logRegister(&uartLog, &shell);
}

在需要添加日志的文件中添加log.h文件,在freertos.c文件中添加测试函数

void StartTask02(void const * argument)
{
  /* USER CODE BEGIN StartTask02 */
  /* Infinite loop */
  for(;;)
  {
	logInfo("logInfo");
    osDelay(1000);
  }
  /* USER CODE END StartTask02 */
}
// log测试函数

void LOG_Test()
{
  uint8_t i[4] = {0, 1, 2,3};
  logPrintln("logPrintln = %d", 2);
  logError("logError");
  logWarning("logWarning");
  logInfo("logInfo");
  
  logAssert(1>2, logDebug("logDebug"));
  logHexDumpAll(i, 4);
}
SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE (SHELL_TYPE_CMD_MAIN), LOG_Test, LOG_Test, "LOG_Test");

测试结果如下,可以支持尾行模式
在这里插入图片描述
log 其他用法

#undef  LOG_ENABLE
#define LOG_ENABLE  1  //控制当前文件日志是否显示

#undef LOG_TAG
#define LOG_TAG           "freertos.c" //打印时可以设置是哪个文件打印的日志
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值