转载自:https://blog.csdn.net/fishsmarkdown/article/details/53576704
FreeRTOS 与 LWIP 开发笔记
最近使用STM32 cube 固件库进行开发,使用的软件开发工具为SW4STM32和STM32CubeMX ,发现使用官方的cube 新库有一个最大的好处就是能使用STM32CubeMX进行工程初始化的配置工作,非常直观的图形化操作,特别是各种系统时钟的配置。同时,还能直接生成支持各种主流开发工具的工程,而且各种外设的初始化代码也能帮你生成,不过,虽然帮忙省了很多事,但是毕竟是个辅助软件工具,生成的工程文件还是或多或少的有一些瑕疵,这就要在开始时候对各种外设认真的配置,目前主要发现有以下几点需要注意的地方:
生成的初始化代码散落各处,需要对于后期修改不好查找,这也是Cube库的比较繁琐的地方,因为它有专门的硬件初始化函数,所以要么慢慢去适应,要么对相关代码按照自己的方式组织好。
对于生成的SW4STM32工程直接编译会发现报错,原因为
__weak=__attribute__((weak))
和__packed=__attribute__((__packed__))
这两个编译定义出错,正确修改应该给这两条定义加上单引号,如下图所示:
FreeRTOS 配置注意事项
在使用STM32Cube库文件会用到系统嘀嗒作为HAL_Delay()函数的定时器,所以在使用STM32CubeMX配置有FreeRTOS的工程时应该给FreeRTOS选择一个Timer定时器作为其系统主定时器,这样HAL_Delay()函数和RTOS中的延时函数不相冲突。
工程的FreeRTOS配置文件在inc
目录下的 FreeRTOSConfig.h
中设置,一些设置使用默认配置就行,以下讲解一些对工程有主要影响的配置:
configMINIMAL_STACK_SIZE
一个任务最小堆栈大小,默认为128。由于FreeRTOS 中定义单位为4个byte,所以相当于128 * 4个字节大小。configTOTAL_HEAP_SIZE
这个很关键,用于设置系统总的堆栈大小,单位为字节。需要注意的是系统占用的堆栈大小应该比所有任务占用的堆栈和要大一点点,因为系统自身任务调度也需要占用一些空间。这个数值小了创建任务失败,或者创建任务成功但是任务不运行。configLIBARY_LOWEST_INTERRUPUT_PRIORITY
这个是系统任务最低的中断函数等级,这个值就是系统时钟的中断优先级,这个对应STM32的系统中断优先值。configLIBARY_MAX_SYSCALL_INTERRUPT_PRIORITY
和上一个配置对应,系统任务最高的中断函数等级。这两个配置会影响一些需要用到信号量和消息队列的中断函数,要是有带有中断安全的系统函数必须将这些中断的优先级设置在这两个值之间,比如,系统默认设置为configLIBARY_LOWEST_INTERRUPT_PRIORITY = 15 configLIBARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 5
,当中断函数需要使用信号量或者消息队列等系统函数时,这些中断必须设置为5 ~ 15的中断等级。如以下代码片段:xSemaphoreGiveFromISR( rs485->recv_sema, &xHigherPriorityTaskWoken ); if( xHigherPriorityTaskWoken == pdTRUE ) { portYIELD_FROM_ISR (xHigherPriorityTaskWoken); //portEND_SWITCHING_ISR(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
LWIP 配置及注意事项
使用STM32CubeMX 配置 LWIP参数时 TCP_QUEUE_OOSEQ
参数必须配置为Disable
,如下图所示,其他可以使用默认配置。
工程默认将 LWIP 配置文件放在inc
文件夹中的 lwipopts.h
文件中,其实 LWIP 真正的配置在其源代码中的 opt.h
文件中,这个 lwipopts.h
文件是被后者包含了,让使用者去自定义,其实如果一个参数在 lwipopts.h
中没有配置在 opt.h
文档中已经有各种参数的默认配置,具体可以查看 opt.h
源代码。这些配置中需要重点注意以下信息:
TCP_QUEUE_OOSEQ
这个上面提到了,必须关了。LWIP_UDP
开启TCP/IP协议的UDP功能。TCPIP_THREAD_STACKSIZE
这个很重要,用于配置tcp_input任务的堆栈大小,这个默认128就可以了,关于tcp_input 下面会讲。TCPIP_THREAD_PRIO
这就tcp_input任务的优先级设置了。LWIP_TIMEVAL_PRIVATE
如若使用GCC 自带 timeval 结构体定义,这个必须关了,也就是定义为零,不然编译会报错。LWIP_BROADCAST_PING
这个要使用 ping 功能时必须开启,定义为 1。TCPIP_MBOX_SIZE
tcp_input 任务中需要用到,一个消息邮件中邮箱大小。LWIP_ICMP
要使用 ping 这个功能必须开启,定义为 1, 因为 ping 就是使用的这个协议。LWIP_SOCKET
这个就很明显了,一看就知道这个 嵌套字编程(socket)功能开启使能。LWIP_COMPAT_MUTEX
在任务中使用信号量必须开启这个。
LWIP 的移植就是要实现 ethernetif.c
文件中的函数,其实 STM32CubeMX 已经帮我们做好了一些通用函数的实现,我们要做的就是根据自己的实际情况对驱动程序进行适当的修改,比如,PHY 芯片不一样一些底层控制寄存器地址就会不一样,芯片复位引脚不一样,等等,这就根据实际情况修改就行,特别需要注意配置PHY芯片的中断优先级别设置的太高,要是使用了STM32芯片的 MCO 引脚输出时钟给PHY芯片还需要配置 MCO的时钟输出频率。
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(ETH_IRQn, 9, 0);
HAL_NVIC_EnableIRQ(ETH_IRQn);
- 1
- 2
- 3
在 low_level_init
函数中开启网卡功能后可以读取网口PHY芯片的寄存器值用于判断是否配置成功,具体可以查看PHY 芯片的 Datasheet 。
/* create a binary semaphore used for informing ethernetif of frame reception */
osSemaphoreDef(SEM);
s_xSemaphore = osSemaphoreCreate(osSemaphore(SEM) , 1 );
/* create the task that handles the ETH_MAC */
osThreadDef(EthIf, ethernetif_input, osPriorityRealtime, 0, INTERFACE_THREAD_STACK_SIZE);
osThreadCreate (osThread(EthIf), netif);
/* Enable MAC and DMA transmission and reception */
HAL_ETH_Start(&heth);
/* USER CODE BEGIN PHY_PRE_CONFIG */
HAL_ETH_ReadPHYRegister(&heth,0x02,®value);
printf("reg #2 :%ld\r\n",regvalue);
HAL_ETH_ReadPHYRegister(&heth,0x03,®value);
printf("reg #3 :%ld\r\n",regvalue); // 读取芯片ID验证驱动
/* 配置网口PHY芯片中断 */
HAL_ETH_ReadPHYRegister(&heth, PHY_MICR, ®value);
regvalue |= (PHY_MICR_INT_EN | PHY_MICR_INT_OE);
/* Enable Interrupts */
HAL_ETH_WritePHYRegister(&heth, PHY_MICR, regvalue );
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
其实 网卡要能工作并接收数据主要是在low_level_init
函数中创建了ethernetif_input
的任务,如上代码所示,这个任务将接收到的数据发送给 lwip/tcpip.c
文档中 tcpip_init
函数建立的tcpip_thread
系统任务中,所以要看工作是否正常(可以用ping,看能否ping通),只要这两个任务(ethernetif_input 和 tcpip_thread)能正常运行就没问题了 。