你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【福利三:逢7发帖赢大礼】基于STM32G070板子的uart shell移植设计

[复制链接]
STMWoodData 发布时间:2026-7-10 00:00

这个板子是好久之前画好的,焊接之后一直放着吃灰了。趁着参加活动拿出来用用。板子如下:image.png

image.png

使用的是STM32G070KBT6型号MCU。该MCU是高性能Arm® Cortex®-M0+ 32位内核,工作频率可达64 MHz。有128 KB Flash程序存储器以及36 KB SRAM,DMA、丰富的系统功能、增强型I/O和外设。提供了多个标准通信接口,包含2个I2C、2个SPI/1个I2S和4个USART、1个具有多达19通道的12位ADC (2.5 MSps)、一个低功耗RTC、一个高级控制PWM定时器、5个通用16位定时器、2个基本定时器、2个看门狗定时器和SysTick定时器。

板载有:4个用户按键,复位按键,LED指示灯,红外IR接收头,0.96寸OLED。USB转串口CH340, SPI FLASH W25Q32和TF卡座。另外扩展有1路SPI,I2C,UART通信接口,以及4个IO接口。

板子电路图如下:

18d91f437f3d138b6dcefd69a1898df2.png

这个板子是准备用来做一个简单的工具的,可以测试IR红外接收头,用于分析红外遥控编码解析。可以用于串口协议通信,串口shell控制,YModem协议测试传输文件,flash和TF卡的fatfs文件系统数据存储,I2C接口传感器测试,SPI接口控制,IO各种可控频率脉冲输出等等。

下面我先基于这个板子分享UART串口shell功能。

首先打开STM32CubeMX软件,新建初始化工程,通过界面初始化各个外设。这里2个串口都选择串口初始化参数115200波特率,使用中断方式发送接收数据。

cc4be91c9db2397408362866f442de83.png

48ddb195925d56f0f776744c64f5d279.png

image.png

image.png

image.png

系统时钟配置最高64MHz。

9e2fc56a36d45d0c615e7af8f4f267b9.png

最后生成KEIL初始化代码工程。

image.png

接下来就是串口框架代码编写了。就是串口的printf重定向,以及shell移植功能。

printf串口输出重定向有2个方法。第一个是勾选target的MicroLib,然后实现一个int fputc(int ch,FILE *f)串口输出函数。

66a6e48bccf3f5c1059d2fa284771261.png

第二个方法就是不勾选MicroLib,然后在pack里面勾选compiler的STDOUT User,再实现一个int stdout_putchar (int ch)串口输出函数。

2c3a3fd369a7f927ab69df8a81244621.png

我的串口部分代码如下,使用中断发送接收,通过FIFO缓存数据,这样通信不容易掉数据。

#include<RTE_Components.h>
#undef __USE_EVENT_RECORDER__
#if defined(RTE_Compiler_EventRecorder) || defined(RTE_CMSIS_View_EventRecorder)
#   define __USE_EVENT_RECORDER__  1
#endif
#if __USE_EVENT_RECORDER__
#   include <EventRecorder.h>
#   include "EventRecorderConf.h"
#endif
typedef struct fifo_buffer_t
{
    volatile uint32_t    read_i;
    volatile uint32_t    write_i;
    uint8_t              buff[UART_FIFO_BUFF_LEN];
}fifo_buffer_t;

typedef struct shell_uart_buffer_t
{
    fifo_buffer_t       tx;
    fifo_buffer_t       rx;
    volatile uint32_t   tx_cpl  :   1;

    void                *userdata;

}shell_uart_buffer_t;

extern shell_uart_buffer_t  g_shell_uart[];

typedef struct stm32_uart_para_t
{
    UART_HandleTypeDef * _uart;
    uint8_t  uart_recv_buff[4];
    uint8_t  uart_send_buff[128];
}stm32_uart_para_t;
stm32_uart_para_t    stm32_uart_p[UART_DEV_NUM];


shell_uart_buffer_t  g_shell_uart[UART_DEV_NUM];

static void uart_get_data_send(uint8_t index)
{
    uint32_t    i;
    //
    for(i=0;i<128;i++)
    {
        if(g_shell_uart[index].tx.read_i != g_shell_uart[index].tx.write_i)
        {
            ((stm32_uart_para_t *)g_shell_uart[index].userdata)->uart_send_buff[i] = g_shell_uart[index].tx.buff[g_shell_uart[index].tx.read_i++];
            g_shell_uart[index].tx.read_i &= (UART_FIFO_BUFF_LEN-1); //256Byte
        }else break;
    }
    if(i)
    {
        g_shell_uart[index].tx_cpl = 1;
        HAL_UART_Transmit_IT(((stm32_uart_para_t *)g_shell_uart[index].userdata)->_uart,
                             ((stm32_uart_para_t *)g_shell_uart[index].userdata)->uart_send_buff,i);
    }
}

void uart_flush_data(uint8_t index)
{
    if(g_shell_uart[index].tx_cpl == 0)
    {
        uart_get_data_send(index);
    }
}


void uart_send_char(uint8_t index, uint8_t ch)
{
    while(((g_shell_uart[index].tx.write_i+1) & (UART_FIFO_BUFF_LEN-1)) == g_shell_uart[index].tx.read_i)
    {
        //fifo buffer full
        if((g_shell_uart[index].tx_cpl == 0))
        {
            uart_get_data_send(index);
        }
    }

    g_shell_uart[index].tx.buff[g_shell_uart[index].tx.write_i++] = ch;
    g_shell_uart[index].tx.write_i &= (UART_FIFO_BUFF_LEN-1);//256Byte

    uart_flush_data(index);
}

uint8_t uart_recv_char(uint8_t index, uint8_t *ch)
{
    if(g_shell_uart[index].rx.read_i != g_shell_uart[index].rx.write_i)
    {
        *ch = g_shell_uart[index].rx.buff[g_shell_uart[index].rx.read_i++];
        g_shell_uart[index].rx.read_i &= (UART_FIFO_BUFF_LEN-1); //256Byte
        return 0;
    }
    return 1;
}

串口中断回调函数处理:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    for(int i=0;i<UART_DEV_NUM;i++)
    {
        if(huart->Instance == ((stm32_uart_para_t *)g_shell_uart[i].userdata)->_uart->Instance)   //shell
        {
            if(((g_shell_uart[i].rx.write_i+1) & (UART_FIFO_BUFF_LEN-1)) != g_shell_uart[i].rx.read_i)
            {
                g_shell_uart[i].rx.buff[g_shell_uart[i].rx.write_i++] = ((stm32_uart_para_t *)g_shell_uart[i].userdata)->uart_recv_buff[0];
                g_shell_uart[i].rx.write_i &= (UART_FIFO_BUFF_LEN-1);//256Byte
            }
            HAL_UART_Receive_IT(huart,((stm32_uart_para_t *)g_shell_uart[i].userdata)->uart_recv_buff,1);//
        }
    }
}

//
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    for(int i=0;i<UART_DEV_NUM;i++)
    {
        if(huart->Instance == ((stm32_uart_para_t *)g_shell_uart[i].userdata)->_uart->Instance)   //shell
        {
            g_shell_uart[i].tx_cpl = 0;
            uart_get_data_send(i);
        }
    }
}

//
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    for(int i=0;i<UART_DEV_NUM;i++)
    {
        if(huart->Instance == ((stm32_uart_para_t *)g_shell_uart[i].userdata)->_uart->Instance)   //shell
        {
            HAL_UART_Receive_IT(huart,((stm32_uart_para_t *)g_shell_uart[i].userdata)->uart_recv_buff,1);//
        }
    }
}

再就是串口shell移植处理,这里我已经实现了nr_micro_shell和letter shell移植。还有一个我自己在AI帮助下写的shell。

这里以我自己写的shell为例,只要实现一个串口输出字符串函数和读取串口接收的字符函数即可。然后主循环调用void shell_usart_init(void)初始化和void shell_usart_loop(void)shell解析循环。


uint8_t USART_PutChar(uint8_t ch)
{
    uart_send_char(UART_SHELL_DEV_INDEX, ch);
    return 0;
}

uint8_t USART_GetChar(uint8_t *ch)
{
    return uart_recv_char(UART_SHELL_DEV_INDEX, ch);
}


int USART_PutStr(const char* str, uint32_t len)
{
    char *s= (char*)str;
    int sl = len;
    while(len --)   USART_PutChar(*s++);
    return sl;
}

int USART_GetStr(const char* str, uint32_t len)
{
    uint8_t *s= (uint8_t *)str;
    uint32_t rlen = 0;

    for(uint32_t i=0;i<len;i++)
    {
        if(0 == uart_recv_char(UART_SHELL_DEV_INDEX, &s[rlen]))
        {
            rlen++;
        }
    }
    return rlen;
}


void shell_usart_init(void)
{
    stm32_uart_p[0]._uart = &huart1;
    g_shell_uart[0].tx.read_i = 0;
    g_shell_uart[0].tx.write_i = 0;
    g_shell_uart[0].rx.read_i = 0;
    g_shell_uart[0].rx.write_i = 0;
    g_shell_uart[0].tx_cpl = 0;
    g_shell_uart[0].userdata = &stm32_uart_p[0];

    stm32_uart_p[1]._uart = &huart3;
    g_shell_uart[1].tx.read_i = 0;
    g_shell_uart[1].tx.write_i = 0;
    g_shell_uart[1].rx.read_i = 0;
    g_shell_uart[1].rx.write_i = 0;
    g_shell_uart[1].tx_cpl = 0;
    g_shell_uart[1].userdata = &stm32_uart_p[1];

#if __USE_EVENT_RECORDER__
    EventRecorderInitialize(0, 1);
#endif


#ifdef UART_SHELL
#if UART_SHELL == 1
    userShellInit(); //LETTER_SHELL
#elif UART_SHELL == 2
    shell_init();   //NR_MICRO_SHELL
#elif UART_SHELL == 3
    // 配置IO操作
    shell_io_ops_t io_ops = {
        .send    = USART_PutStr,
    };
    lsy_shell_init(&io_ops);   //LSY_SHELL
#else

#endif
#endif
    /* 启动非阻塞式读取接收数据 */
    for(int i=0;i<UART_DEV_NUM;i++)
    {
        HAL_UART_Receive_IT(((stm32_uart_para_t *)g_shell_uart[i].userdata)->_uart,
                            ((stm32_uart_para_t *)g_shell_uart[i].userdata)->uart_recv_buff,1);//
    }
}


void shell_usart_loop(void)
{
    uint8_t ch;

    if(0 == uart_recv_char(UART_SHELL_DEV_INDEX, &ch))
    {
#ifdef UART_SHELL
        #if UART_SHELL == 1
            shellHandler(&shell, ch); //letter shell
        #elif UART_SHELL == 2
            shell(ch);
        #elif UART_SHELL == 3
            lsy_shell_input(ch);
        #else
            USART_PutChar(ch);
        #endif
#endif
    }

    //将串口FIFO内数据继续发送
    for(int i=0;i<UART_DEV_NUM;i++)
    {
        uart_flush_data(i);
    }
}

122653d215502217f0b574e7679c8b3d.png

我写的这个shell支持特殊控制按键功能,历史命令记录功能和用户命令权限控制功能。

命令编写例子:

int cmd_led_control(int argc, char **argv)
{
    uint32_t pinstate = 1;

    if (argc != 2) {
        printf("Usage: led <on|off>\r\n");
        return SHELL_ERR_PARAM;
    }

    if(!strcmp(argv[1], "on"))
    {
        pinstate = 0;

    }else if(!strcmp(argv[1],"off"))
    {
        pinstate = 1;

    }else 
    {
        printf("cmd para ERROR!\n");
        return SHELL_ERR_PARAM;
    }
    printf("led %s\r\n",pinstate ? "off":"on");
    return SHELL_OK;
}
LSY_SHELL_CMD_EXPORT(led, cmd_led_control, "Control LED: led <on|off>");

最后shell命令执行效果如下:

d6f0ae99a220ce19e0d0cba137434a30.png

收藏 评论1 发布时间:2026-7-10 00:00

举报

1个回答
STMWoodData 回答时间:半小时前
stm32_shell_uart.zip (29.43 KB, 下载次数: 0)

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版