文章目录
前言
上一篇我将GD32F4裸机移植LWIP的过程写出来,这一篇主要在上一篇的代码基础上加入freertos系统。
一、获取上一篇的代码
二、加入FreeRTOS操作系统
1.在代码中添加.c和.h文件,并在c/c++中添加工程路径。
2.添加FreeRTOSConfig.h文件,该文件从正点原子的freertos工程中找出,并将头文件修改一下。
3.修改gd32f4xx_it.c文件。
屏蔽void SVC_Handler(void)、void PendSV_Handler(void)中断
修改void SysTick_Handler(void);
代码如下:
extern void xPortSysTickHandler(void);
/**
* @brief systick中断服务函数,使用OS时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
// HAL_IncTick();
delay_decrement();
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) /* OS开始跑了,才执行正常的调度处理 */
{
xPortSysTickHandler();
}
}
4.添加sys_arch.c文件。
添加该文件之后,编译会报错,这个是因为我们没有打开操作系统接口,这时候,需要在lwippopts.h打开有操作系统的接口。
5.修改ethernetif.c文件
代码如下(修改部分):
/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void
low_level_init(struct netif *netif)
{
netif->hwaddr_len = ETHARP_HWADDR_LEN; /* 设置MAC地址长度,为6个字节 */
/* 初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复 */
netif->hwaddr[0] = g_lwipdev.mac[0];
netif->hwaddr[1] = g_lwipdev.mac[1];
netif->hwaddr[2] = g_lwipdev.mac[2];
netif->hwaddr[3] = g_lwipdev.mac[3];
netif->hwaddr[4] = g_lwipdev.mac[4];
netif->hwaddr[5] = g_lwipdev.mac[5];
netif->mtu = 1500; /* 最大允许传输单元,允许该网卡广播和ARP功能 */
/* 创建一个信号量 */
g_rx_semaphore = xSemaphoreCreateBinary();
/* 创建处理ETH_MAC的任务 */
sys_thread_new("eth_thread",
ethernetif_input, /* 任务入口函数 */
netif, /* 任务入口函数参数 */
NETIF_IN_TASK_STACK_SIZE,/* 任务栈大小 */
NETIF_IN_TASK_PRIORITY); /* 任务的优先级 */
/* 网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播 */
/* 使能、 ARP 使能等等重要控制位 */
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP; /* 广播 ARP协议 链接检测 */
/* initialize MAC address in ethernet MAC */
enet_mac_address_set(ENET_MAC_ADDRESS0, netif->hwaddr);
/* initialize descriptors list: chain/ring mode */
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
enet_ptp_enhanced_descriptors_chain_init(ENET_DMA_TX);
enet_ptp_enhanced_descriptors_chain_init(ENET_DMA_RX);
#else
enet_descriptors_chain_init(ENET_DMA_TX);
enet_descriptors_chain_init(ENET_DMA_RX);
// enet_descriptors_ring_init(ENET_DMA_TX);
// enet_descriptors_ring_init(ENET_DMA_RX);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
/* enable ethernet Rx interrrupt */
int i;
for(i=0; i<ENET_RXBUF_NUM; i++){
enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
}
#ifdef CHECKSUM_BY_HARDWARE
/* enable the TCP, UDP and ICMP checksum insertion for the Tx frames */
for(i=0; i < ENET_TXBUF_NUM; i++){
enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
}
#endif /* CHECKSUM_BY_HARDWARE */
/* enable MAC and DMA transmission and reception */
enet_enable();
}
/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
static xSemaphoreHandle s_tx_semaphore = NULL;
struct pbuf *q;
uint8_t *buffer ;
uint16_t framelength = 0;
ErrStatus reval = ERROR;
SYS_ARCH_DECL_PROTECT(sr);
if (s_tx_semaphore == NULL){
vSemaphoreCreateBinary (s_tx_semaphore);
}
if (xSemaphoreTake(s_tx_semaphore, portMAX_DELAY)){
SYS_ARCH_PROTECT(sr);
while((uint32_t)RESET != (dma_current_txdesc->status & ENET_TDES0_DAV)){
}
buffer = (uint8_t *)(enet_desc_information_get(dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
for(q = p; q != NULL; q = q->next){
memcpy((uint8_t *)&buffer[framelength], q->payload, q->len);
framelength = framelength + q->len;
}
/* transmit descriptors to give to DMA */
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
reval = ENET_NOCOPY_PTPFRAME_TRANSMIT_ENHANCED_MODE(framelength, NULL);
#else
reval = ENET_NOCOPY_FRAME_TRANSMIT(framelength);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
SYS_ARCH_UNPROTECT(sr);
/* give semaphore and exit */
xSemaphoreGive(s_tx_semaphore);
}
if(SUCCESS == reval){
return ERR_OK;
}else{
while(1){
}
}
}
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf *
low_level_input(struct netif *netif)
{
struct pbuf *p= NULL, *q;
uint32_t l =0;
u16_t len;
uint8_t *buffer;
/* obtain the size of the packet and put it into the "len" variable. */
len = enet_desc_information_get(dma_current_rxdesc, RXDESC_FRAME_LENGTH);
buffer = (uint8_t *)(enet_desc_information_get(dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));
if (len > 0){
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
}
if (p != NULL){
for(q = p; q != NULL; q = q->next){
memcpy((uint8_t *)q->payload, (u8_t*)&buffer[l], q->len);
l = l + q->len;
}
}
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_RECEIVE_ENHANCED_MODE(NULL);
#else
ENET_NOCOPY_FRAME_RECEIVE();
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
return p;
}
6.修改以太网中断函数
extern void lwip_pkt_handle(void); //在lwip_comm.c里面定义
/*!
\brief this function handles ethernet interrupt request
\param[in] none
\param[out] none
\retval none
*/
void ENET_IRQHandler(void)
{
if(SET == enet_interrupt_flag_get(ENET_DMA_INT_FLAG_RS))
{
lwip_pkt_handle(); /* 处理以太网数据,即将数据提交给LWIP */
}
/* clear the enet DMA Rx interrupt pending bits */
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_RS_CLR);
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_NI_CLR);
}
该处使用的url网络请求的数据。
7.修改lwip_comm.c与main.c文件
这个过程比较简单,按照错误去修改就行,对照正点原子代码去修改。
8.编译下载
完成上述步骤之后,编译无错误无告警。下载到板子后,发现串口打印以下错误。
经过耐心的查找,发现是中断分组出现了问题,因此在初始化中加入中断分组的初始化。
但是又出现了新的问题,如下图,
找到port.c的791行,然后定位是这个configMAX_SYSCALL_INTERRUPT_PRIORITY定义的问题,在FreeRTOSConfig.c文件中修改下面定义。
//#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 /* 中断最低优先级 */
//#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 /* FreeRTOS可管理的最高中断优先级 */
//#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
//#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
//#define configMAX_API_CALL_INTERRUPT_PRIORITY configMAX_SYSCALL_INTERRUPT_PRIORITY
/* the lowest interrupt priority that can be used in a call to a "set priority" function */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. Do not call
interrupt safe freertos api functions from any interrupt that has a higher
priority than this! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 2
/* interrupt priorities used by the kernel port layer itself */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 32 //( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
9.ping包测试
总结
移植代码并不难,最重要的是接下来的学习,如何熟练的使用LWIP才是最难的。