前文:【易开嵌入式】rt-thread+stm32f407+nandflash,实现RL-FLASHFS文件系统移植
版权声明:本版面文章皆为原创、或参考其他技术网站、博客后自己动手做实验所得,转载请注明出处。
鸣谢:感谢eric硬汉
商务合作:lz_kwok@163.com
易开嵌入式工作室
本文重点讲述,使用stm32最新hal库,基于rt-thread操作系统,移植RL-TCPnet的流程。
参考【前文】描述,编写kconfig和sconsript脚本,将源码源码加入rtt的工程中:
数据收发采用中断模式,需要实现以下几个接口:
void init_ethernet () //初始化以太网控制器。
void send_frame (OS_FRAME *frame)//发送数据包给以太网控制器。
void int_enable_eth () //使能以太网控制器中断。
void int_disable_eth () //关闭以太网控制器中断。
HAL_ETH_RxCpltCallback//中断函数,主要用于数据包的接收
使用hal库,实现方法如下:
首先是init_ethernet函数
/* Register the EMAC device */
int init_ethernet(void)
{
rt_err_t state = RT_EOK;
/* Prepare receive and send buffers */
Rx_Buff = (rt_uint8_t *)rt_calloc(ETH_RXBUFNB, ETH_MAX_PACKET_SIZE);
if (Rx_Buff == RT_NULL)
{
LOG_E("No memory");
state = -RT_ENOMEM;
goto __exit;
}
Tx_Buff = (rt_uint8_t *)rt_calloc(ETH_TXBUFNB, ETH_MAX_PACKET_SIZE);
if (Tx_Buff == RT_NULL)
{
LOG_E("No memory");
state = -RT_ENOMEM;
goto __exit;
}
DMARxDscrTab = (ETH_DMADescTypeDef *)rt_calloc(ETH_RXBUFNB, sizeof(ETH_DMADescTypeDef));
if (DMARxDscrTab == RT_NULL)
{
LOG_E("No memory");
state = -RT_ENOMEM;
goto __exit;
}
DMATxDscrTab = (ETH_DMADescTypeDef *)rt_calloc(ETH_TXBUFNB, sizeof(ETH_DMADescTypeDef));
if (DMATxDscrTab == RT_NULL)
{
LOG_E("No memory");
state = -RT_ENOMEM;
goto __exit;
}
#ifdef RT_USING_LWIP
stm32_eth_device.ETH_Speed = ETH_SPEED_100M;
stm32_eth_device.ETH_Mode = ETH_MODE_FULLDUPLEX;
/* OUI 00-80-E1 STMICROELECTRONICS. */
stm32_eth_device.dev_addr[0] = 0x00;
stm32_eth_device.dev_addr[1] = 0x80;
stm32_eth_device.dev_addr[2] = 0xE1;
/* generate MAC addr from 96bit unique ID (only for test). */
stm32_eth_device.dev_addr[3] = *(rt_uint8_t *)(UID_BASE + 4);
stm32_eth_device.dev_addr[4] = *(rt_uint8_t *)(UID_BASE + 2);
stm32_eth_device.dev_addr[5] = *(rt_uint8_t *)(UID_BASE + 0);
stm32_eth_device.parent.parent.init = rt_stm32_eth_init;
stm32_eth_device.parent.parent.open = rt_stm32_eth_open;
stm32_eth_device.parent.parent.close = rt_stm32_eth_close;
stm32_eth_device.parent.parent.read = rt_stm32_eth_read;
stm32_eth_device.parent.parent.write = rt_stm32_eth_write;
stm32_eth_device.parent.parent.control = rt_stm32_eth_control;
stm32_eth_device.parent.parent.user_data = RT_NULL;
stm32_eth_device.parent.eth_rx = rt_stm32_eth_rx;
stm32_eth_device.parent.eth_tx = rt_stm32_eth_tx;
/* register eth device */
state = eth_device_init(&(stm32_eth_device.parent), "eth0");
if (RT_EOK == state)
{
LOG_D("emac device init success");
}
else
{
LOG_E("emac device init faild: %d", state);
state = -RT_ERROR;
goto __exit;
}
#endif
rt_err_t result;
// result = rt_mb_init(&tcpnet_rx_thread_mb, "trxmb",
// &tcpnet_rx_thread_mb_pool[0], sizeof(tcpnet_rx_thread_mb_pool)/4,
// RT_IPC_FLAG_FIFO);
// RT_ASSERT(result == RT_EOK);
#ifdef RT_USING_RL_TCPnet
state = rt_stm32_eth_init();
if(state != RT_EOK){
return state;
}
#endif
result = rt_event_init(&event, "event", RT_IPC_FLAG_FIFO);
if (result != RT_EOK)
{
rt_kprintf("init event failed.\n");
state = -RT_ERROR;
}
// result = rt_thread_init(&tcpnet_rx_thread, "trx", tcpnet_rx_thread_entry, RT_NULL,
// &tcpnet_rx_thread_stack[0], sizeof(tcpnet_rx_thread_stack),
// RT_THREAD_PRIORITY_MAX - 5, 16);
// RT_ASSERT(result == RT_EOK);
// result = rt_thread_startup(&tcpnet_rx_thread);
/* start phy monitor */
rt_thread_t tid;
tid = rt_thread_create("phy",
phy_monitor_thread_entry,
RT_NULL,
1024,
RT_THREAD_PRIORITY_MAX - 2,
2);
if (tid != RT_NULL)
{
rt_thread_startup(tid);
}
else
{
state = -RT_ERROR;
}
rt_thread_init(&main_TCPnet,
"thread_main_TCPnet",
thread_main_TCPnet,
RT_NULL,
&main_TCPnet_stack[0],
sizeof(main_TCPnet_stack),
THREAD_PRIORITY - 1, THREAD_TIMESLICE);
rt_thread_startup(&main_TCPnet);
rt_thread_init(&TCP_time,
"thread_TCP_timeTick",
thread_TCP_timeTick,
RT_NULL,
&TCP_time_stack[0],
sizeof(TCP_time_stack),
THREAD_PRIORITY, THREAD_TIMESLICE);
rt_thread_startup(&TCP_time);
__exit:
if (state != RT_EOK)
{
if (Rx_Buff)
{
rt_free(Rx_Buff);
}
if (Tx_Buff)
{
rt_free(Tx_Buff);
}
if (DMARxDscrTab)
{
rt_free(DMARxDscrTab);
}
if (DMATxDscrTab)
{
rt_free(DMATxDscrTab);
}
}
return state;
}
接着是send_frame函数
void send_frame (OS_FRAME *frame)
{
HAL_StatusTypeDef state;
rt_uint32_t i;
__IO ETH_DMADescTypeDef *DmaTxDesc;
rt_uint8_t *buffer = (rt_uint8_t *)(EthHandle.TxDesc->Buffer1Addr);
rt_uint32_t framelength = 0;
rt_uint32_t bufferoffset = 0;
rt_uint32_t byteslefttocopy = 0;
rt_uint32_t payloadoffset = 0;
DmaTxDesc = EthHandle.TxDesc;
bufferoffset = 0;
// LOG_D("%s\r\n",__func__);
/* copy frame from pbufs to driver buffers */
for (;;)
{
if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET){
LOG_D("buffer not valid");
goto error;
}
/* Get bytes in current lwIP buffer */
byteslefttocopy = frame->length;
payloadoffset = 0;
/* Check if the length of data to copy is bigger than Tx buffer size*/
while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE)
{
/* Copy data to Tx buffer*/
memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)frame->data + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset));
/* Point to next descriptor */
DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr);
/* Check if the buffer is available */
if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET)
{
LOG_E("dma tx desc buffer is not valid");
goto error;
}
buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset);
framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy the remaining bytes */
memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)frame->data + payloadoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
framelength = framelength + byteslefttocopy;
break;
}
#ifdef ETH_TX_DUMP
dump_hex(buffer, p->tot_len);
#endif
/* Prepare transmit descriptors to give to DMA */
/* TODO Optimize data send speed*/
LOG_D("%s lenth :%d", __func__,framelength);
for(i=0;i<1000;i++);
/* wait for unlocked */
while (EthHandle.Lock == HAL_LOCKED){
LOG_I("%s waitting",__func__);
};
state = HAL_ETH_TransmitFrame(&EthHandle, framelength);
if (state != HAL_OK)
{
LOG_E("eth transmit frame faild: %d", state);
}
rt_event_send(&event, EVENT_MAIN);
error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET)
{
/* Clear TUS ETHERNET DMA flag */
EthHandle.Instance->DMASR = ETH_DMASR_TUS;
/* Resume DMA transmission*/
EthHandle.Instance->DMATPDR = 0;
}
}
然后是中断接受函数HAL_ETH_RxCpltCallback
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
#ifdef RT_USING_LWIP
rt_err_t result;
result = eth_device_ready(&(stm32_eth_device.parent));
if (result != RT_EOK)
LOG_I("RxCpltCallback err = %d", result);
#endif
#ifdef RT_USING_RL_TCPnet
OS_FRAME *frame;
while(ETH_GetRxPktSize(heth->RxDesc))
{
// rt_mb_send(&tcpnet_rx_thread_mb, 0);
HAL_StatusTypeDef state;
uint16_t len = 0;
uint8_t *buffer;
__IO ETH_DMADescTypeDef *dmarxdesc;
uint32_t bufferoffset = 0;
uint32_t payloadoffset = 0;
uint32_t byteslefttocopy = 0;
volatile uint32_t i = 0;
/* Get received frame */
state = HAL_ETH_GetReceivedFrame_IT(&EthHandle);
if (state != HAL_OK)
{
LOG_D("receive frame faild");
}
/* Obtain the size of the packet and put it into the "len" variable. */
len = EthHandle.RxFrameInfos.length;
buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer;
LOG_D("receive frame len : %d", len);
if (len > 0)
{
/* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
frame = alloc_mem (len | 0x80000000);
}
#ifdef ETH_RX_DUMP
dump_hex(buffer, p->tot_len);
#endif
// for(i=0;i<5000;i++);
if (frame != NULL)
{
dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
bufferoffset = 0;
for (;;)
{
byteslefttocopy = frame->length;
payloadoffset = 0;
/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE)
{
/* Copy data to pbuf */
memcpy((uint8_t *)((uint8_t *)frame->data + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset));
/* Point to next descriptor */
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
buffer = (uint8_t *)(dmarxdesc->Buffer1Addr);
byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset);
payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset);
bufferoffset = 0;
}
/* Copy remaining data in pbuf */
memcpy((uint8_t *)((uint8_t *)frame->data + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy);
bufferoffset = bufferoffset + byteslefttocopy;
put_in_queue (frame);
break;
}
}else{
free_mem(frame);
return ;
}
/* Release descriptors to DMA */
/* Point to first descriptor */
dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc;
/* Set Own bit in Rx descriptors: gives the buffers back to DMA */
for (i = 0; i < EthHandle.RxFrameInfos.SegCount; i++)
{
dmarxdesc->Status |= ETH_DMARXDESC_OWN;
dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr);
}
/* Clear Segment_Count */
EthHandle.RxFrameInfos.SegCount = 0;
/* When Rx Buffer unavailable flag is set: clear it and resume reception */
if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET)
{
/* Clear RBUS ETHERNET DMA flag */
EthHandle.Instance->DMASR = ETH_DMASR_RBUS;
/* Resume DMA reception */
EthHandle.Instance->DMARPDR = 0;
}
rt_event_send(&event, EVENT_MAIN);
}
#endif
}
在init_ethernet函数中,创建了两个线程,用于运行RL-TCPnet的时基和协议栈(基于rtt的事件驱动)。
移植完成后,下载到开发板中测试ftpserver功能,效果如下:
可以看到,速度可以达到2MB/s左右。
至此,移植完成!!!