打印
[应用相关]

STM32CubeMX 真的不要太好用

[复制链接]
548|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
由于工作内容的变动,我已经很久没有正经的玩过单片机了,近期又要用它做个小玩意了,还是选 stm32 吧,外设库开发不要太方便,哈哈哈

先去 stm32 社区逛了逛,发现了一个新字眼 STM32CubeMX,简单看了下,大概明白是个 ST 公司新推出的一个配置工具,直接由图形界面简单配置下,生成初始化代码,并对外设做了进一步的抽象,让开发人员更只专注应用的开发,挺不错的样子,下来玩玩。

详细的入门教程我就不写了,网上太多,这里说下我是怎么用它快速的实现了一个虚拟串口终端和点灯吧,也算是入门教程吧,哈哈哈

我需要用的外设有 usb 接口和一个普通的 GPIO,需要使用的中间件库有 FreeRTOS 和 USB_DEVICE,要完成的功能是实现一个虚拟串口,然后适配上 FreeRTOS 的 CLI 制作一个虚拟串口终端,同时让一个 LED 闪烁,这里我选用经典的 STM32F103C8T6 来完成这些。


使用特权

评论回复
沙发
liuqiangdong|  楼主 | 2022-3-31 20:08 | 只看该作者
那让我们开始吧,

第一步,直接打开 STM32CubeMX,在主界面选到 ACCESS TO MCU SELECTOR 开始一个工程,这里需要注意的一点是此时 STM32CubeMX 会去联网检查一些更新,如果你的防火墙是开的话,很可能会检测失败,关掉防火墙就好。

使用特权

评论回复
板凳
liuqiangdong|  楼主 | 2022-3-31 20:10 | 只看该作者
第二步,在 MCU Filters 栏直接输入我们使用的型号,只需要输入 103C8 就找到啦,然后选中它,右边有它的介绍,而且还可以直接下载它的 Datasheet 也不要太方便了,然后点击 Start Project 按钮开始一个工程。

使用特权

评论回复
地板
liuqiangdong|  楼主 | 2022-3-31 20:11 | 只看该作者
第三步,把我们需要的外设和中间件库选上,有 USB 接口,GPIO 接口,FREERTOS 和 USB_DEVICE 的支持,最后如下图:

使用特权

评论回复
5
liuqiangdong|  楼主 | 2022-3-31 20:12 | 只看该作者
配置的时候有以下几点要注意的:

1、时钟的配置在 RCC 选项,根据实际情况是选内部 RC 还是外部晶振,外部晶振的话要占用两个引脚,我这里是 8 Mhz 外部晶振,配置如下:

使用特权

评论回复
6
liuqiangdong|  楼主 | 2022-3-31 20:13 | 只看该作者
2、关于 debug 口子的配置,默认它是认为我们不需要 debug 口的,所以如果不更改的话生成代码下载进去后 debug 口就关闭了,哈哈哈

如果你真的不小心 debug 口子被关闭了也不用担心,可以通过如下方法再次下载程序:

按住复位按钮要不要放,然后在 Keil 下点击下载按钮,等它提示错误并说检测到被锁掉 debug 口的片子,然后点击确定的同时,松开复位按钮就可以下载啦,如果不行则再多试几次,时序和时机很重要。

使用特权

评论回复
7
liuqiangdong|  楼主 | 2022-3-31 20:19 | 只看该作者
然后在 SYS 里面选择 debug 工具连接的类型,我这里是 SWD 的方式连接的,选择串行线,如下图:

使用特权

评论回复
8
liuqiangdong|  楼主 | 2022-3-31 20:20 | 只看该作者
3、由于 STM32CubeMX 使用的 HAL 库有一个 delay 接口默认是使用 SysTick 定时器来实现的,而由于这里我们选用了 FreeRTOS,它使用的心跳时钟在 Cortex 内核上的移植都是依赖于 SysTick 定时器,这里会有冲突,这一点在点击生成代码按钮时 STM32CubeMX 也会发出警告,这里我们为 HAL 库使用的定时器选过一个就可以了,如下图,这里我把它选到了 TIM1

使用特权

评论回复
9
liuqiangdong|  楼主 | 2022-3-31 20:22 | 只看该作者
4、由于后续我们还要在 FreeRTOS 上加入许多线程,我们把他的堆大小设置大一点,在 FREERTOS 的 Config parameters 里面的 TOTAL_HEAP_SIZE,这里比较任性,我配置了 1024 字节,哈哈哈

使用特权

评论回复
10
liuqiangdong|  楼主 | 2022-3-31 20:24 | 只看该作者
第四步,我们再来为 FreeRTOS 上加入一个 LED 闪烁的线程。这里我们选到 FREERTOS 的 Taks and Queues,然后点击 Add 按钮即可添加了,取名为 ledTask。

到这里配置工作就差不多了,接下来见证奇迹的时刻到了,点击 GENERATE CODE 然后整个工程就生成了,然后编译下载到板子上,插上电脑,叮咚,成功识别为串口,在 win10 上驱动都不用自己装,是不是很爽,哈哈哈

到这里我们的 LED 还是不能闪的,这需要我们加入几条代码,这个简单,找到我们在工具上创建的 ledTask 线程,在线程函数实体里面加入如下代码再编译下载后灯就开始闪了,简单吧。

使用特权

评论回复
11
liuqiangdong|  楼主 | 2022-3-31 20:27 | 只看该作者

使用特权

评论回复
12
liuqiangdong|  楼主 | 2022-3-31 20:28 | 只看该作者
我想我的介绍就到这里结束吧,这才简单嘛,哈哈哈,但等等,说好的虚拟终端呢,

那好吧,那我们先再说个注意点,接下来往上加代码的时候,往由工具生成的文件里面加代码一定要加在标注了 USER CODE BEGIN 和 USER CODE END 的注释里面,这样下次更新配置的时候你加的代码才不会消失,自己加入的文件则无所谓。具体实现我这里也说个大致思路和要注意的点,具体实现,请到我的 github 上去取吧,如果你能点颗星星我就更开心了,哈哈哈

串口终端我们都上 FreeRTOS 了当然有现成的实现了,这里我们主要做的工作就是把 FreeRTOS 提供的串口终端实现即 CLI ,对接上我们的虚拟串口。

使用特权

评论回复
13
liuqiangdong|  楼主 | 2022-3-31 20:30 | 只看该作者
第一步,下载 FreeRTOS 的 CLI 扩展库,这里 STM32CubeMX 默认是没有提供的,没关系,我们去 FreeRTOS 的官网下载,注意最好下载当前使用 FreeRTOS 版本对应版本的完整包,然后解压得到 CLI 扩展库加入到我们的工程,同时把它的 Demo 也加进来。

使用特权

评论回复
14
liuqiangdong|  楼主 | 2022-3-31 20:31 | 只看该作者
第二步,经阅读 CLI 的 Demo 后发现它依赖的如下四个接口我们实现一下就好了。我们要做的内容就是完善这四个接口,把虚拟串口下来的数据传进来,然后把输出传回去。

使用特权

评论回复
15
liuqiangdong|  楼主 | 2022-3-31 20:32 | 只看该作者
xComPortHandle xSerialPortInitMinimal(int baudRate, int queueLength);

int xSerialGetChar(xComPortHandle xPort, signed char *cRxedChar, int timeout);

int vSerialPutString(xComPortHandle xPort, signed char *pcWelcomeMessage, unsigned short len);

int xSerialPutChar(xComPortHandle xPort, signed char c, int timeout);

使用特权

评论回复
16
liuqiangdong|  楼主 | 2022-3-31 20:33 | 只看该作者
这里实际上设计是依赖实际的串口的,但没关系我们虚拟的也可以,只要把数据接收发送打通即可。

使用特权

评论回复
17
liuqiangdong|  楼主 | 2022-3-31 20:34 | 只看该作者
第三步,完善发送数据接口。发送接口比较简单,经阅读 ST 对虚拟串口的实现了解到往 USB 发送数据,使用 CDC_Transmit_FS 接口即可,所以实现如下:
int vSerialPutString(xComPortHandle xPort, signed char *pMessage, unsigned short len)
{
        xPort = xPort;

retry:

        if(0x00 != CDC_Transmit_FS(pMessage, len))
        {
                osDelay(10);
                goto retry;
        }

        return 0x00;
}

使用特权

评论回复
18
liuqiangdong|  楼主 | 2022-3-31 20:35 | 只看该作者
请放过不要吐槽这里的 goto,哈哈哈,这里一定要去检查 CDC_Transmit_FS 接口的返回值,刚开始我只检查了,没有动作,后面发现会漏字符,后面才把 retry 加上去的,然后就使用了 goto,我觉的这样很简洁也好理解,当然加上超时机制就更好了,这里勉强先这样吧,哈哈哈

使用特权

评论回复
19
liuqiangdong|  楼主 | 2022-3-31 20:38 | 只看该作者
第四步,对接数据接收。这里稍微有点麻烦,CDC 的数据是在 CDC_Receive_FS 以 callback 的方式传上来的,应该是使用中断接收,我们串口终端是一个线程通过 xSerialGetChar 接口来获取数据的,这里当然首先想到的就是使用 FreeRTOS 提供的 queue 功能了,但最后发现 queue 只能一个个入列,效果很不理想,会漏字符,如果只传递数据的指针过来的话,又要不停的 malloc 和 free 内存我想效率也不会太高,这里索性搬出来我经常使用的一个 fifo 实现,配合 FreeRTOS 任务通知功能,也算是达到预期吧,哈哈哈,最终它们的实现如下:

使用特权

评论回复
20
liuqiangdong|  楼主 | 2022-3-31 20:38 | 只看该作者
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
        /* USER CODE BEGIN 6 */
        extern TaskHandle_t xConsoleHandle;
        uint32_t count = 0x00;
        BaseType_t xHigherPriorityTaskWoken;
               
        USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
        USBD_CDC_ReceivePacket(&hUsbDeviceFS);
       
        count = kfifo_put(&cdcRxFifo, Buf, *Len);
       
        if(        count != *Len)
        {
                CDC_Transmit_FS((uint8_t*) "cdc rx buffer full!!!", strlen("cd rx buffer full!!!"));
        }
       
        if(NULL != xConsoleHandle)
        {
                vTaskNotifyGiveFromISR(xConsoleHandle, &xHigherPriorityTaskWoken);
       
                if(xHigherPriorityTaskWoken)
                {
                        taskYIELD();
                }
        }
       
        return (USBD_OK);
        /* USER CODE END 6 */
}

int xSerialGetChar(xComPortHandle xPort, signed char *cRxedChar, int timeout)
{
        static uint32_t i = 0x00;

        xPort = xPort;

        if(i == 0x00)
        {
                ulTaskNotifyTake(pdTRUE, timeout);

                i = kfifo_len(&cdcRxFifo);
        }

        if(0x01 == kfifo_get(&cdcRxFifo, cRxedChar, 0x01))
        {
                i--;
        }

        return pdPASS;
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

5

主题

97

帖子

0

粉丝