一、串口的初始化
//系统初始化
byte osal_init_system( void )
{
……
osalInitTasks();//系统任务初始化
……
}
//任务初始化
void osalInitTasks( void )
{
……
#if defined( MT_TASK )//如果定义了MT_TASK就初始化MT任务,所以要使用uart必须编译MT_TASK
MT_TaskInit( taskID++ );
#endif
……
}
//MT_TASK初始化
void MT_TaskInit( byte task_id )
{
MT_TaskID = task_id;//分配任务ID
……
SPIMgr_Init();//SPI(UART)管理初始化
} /* MT_TaskInit() */
//SPI(UART)管理初始化
void SPIMgr_Init ()
{
halUARTCfg_t uartConfig;
/* Initialize APP ID */
App_TaskID = 0;//应用层TaskID初始化为0,在后面应用层如果想使用uart需要注册该TaskID
/* UART Configuration */
uartConfig.configured = TRUE;
uartConfig.baudRate = SPI_MGR_DEFAULT_BAUDRATE;
uartConfig.flowControl = SPI_MGR_DEFAULT_OVERFLOW;
uartConfig.flowControlThreshold = SPI_MGR_DEFAULT_THRESHOLD;
uartConfig.rx.maxBufSize = SPI_MGR_DEFAULT_MAX_RX_BUFF;
uartConfig.tx.maxBufSize = SPI_MGR_DEFAULT_MAX_TX_BUFF;
uartConfig.idleTimeout = SPI_MGR_DEFAULT_IDLE_TIMEOUT;
uartConfig.intEnable = TRUE;
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uartConfig.callBackFunc = SPIMgr_ProcessZToolData; //回调函数
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
uartConfig.callBackFunc = SPIMgr_ProcessZAppData; //回调函数
#else
uartConfig.callBackFunc = NULL;
#endif
/* Start UART */
#if defined (SPI_MGR_DEFAULT_PORT)
HalUARTOpen (SPI_MGR_DEFAULT_PORT, &uartConfig); //打开默认串口
#else
……
}
到这里串口的初始化基本就完成了,还有个打开串口,先不下去了,下面的水是越来越浑,过几天总结底层的…
二、串口的事件处理
//系统主循环
void osal_start_system( void )
{
……
Hal_ProcessPoll(); // 串口与定时器轮询函数
……
}
// 串口与定时器轮询函数
void Hal_ProcessPoll ()
{
HalTimerTick();
#if (defined HAL_UART) && (HAL_UART == TRUE)
HalUARTPoll();
#endif
}
//串口轮询函数
void HalUARTPoll( void )
{
……//这里会根据是DMA还是普通串口有两种不同的轮询方式,这里说普通串口
#if HAL_UART_ISR
#if HAL_UART_DMA
if ( cfg->flag & UART_CFG_DMA )
{
pollDMA( cfg );
}
else
#endif
{
pollISR( cfg );
}
#elif HAL_UART_DMA
pollDMA( cfg );
#endif
……
}
//普通串口轮询函数
static void pollISR( uartCfg_t *cfg )
{
……
轮询完事再次回到void HalUARTPoll函数中,看是否触发事件,如果触发了HAL_UART_RX_FULL、HAL_UART_RX_ABOUT_FULL、HAL_UART_RX_TIMEOUT三个中任意一个就调用回调函数处理。
if ( evt && cfg->rxCB )
{
cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt );
}
//串口回调函数
#define SPI_MGR_ZAPP_RX_READY 0x01
SPIMgr_ZAppRxStatus = SPI_MGR_ZAPP_RX_READY;
函数:
void SPIMgr_ProcessZAppData ( uint8 port, uint8 event )
{
osal_event_hdr_t *msg_ptr;
uint16 length = 0;
uint16 rxBufLen = Hal_UART_RxBufLen(SPI_MGR_DEFAULT_PORT);
//获取串口buf中数据的长度
……
if (event & ( HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
{
if ( App_TaskID )//必须这里被注册
{
// 准备向上层发送数据
//如果接收状态为READY,并且数据长度大于0说明有数据了
if ((SPIMgr_ZAppRxStatus == SPI_MGR_ZAPP_RX_READY ) && (length != 0))
{
/*要读取这些数据了,先关闭接收!*/
SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_NOT_READY );
/* 2 more bytes are added, 1 for CMD type, other for length */
msg_ptr = (osal_event_hdr_t *)osal_msg_allocate( length + sizeof(osal_event_hdr_t) );
//分配内存一个字节存放命令event,一个字节存放长度。剩下的存放length的内存存放数据
if ( msg_ptr )
{
msg_ptr->event = SPI_INCOMING_ZAPP_DATA;
msg_ptr->status = length;//这里状态存放了数据的长度
HalUARTRead( SPI_MGR_DEFAULT_PORT, (uint8 *)(msg_ptr + 1), length );//读入buf中的数值
//下面是触发函数
osal_msg_send( App_TaskID, (uint8 *)msg_ptr );
//这里我觉得还要打开串口,让它接收数据啊。怎么没有来这么一句话
//SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_READY );
}
}
}
}
}
关闭接收函数:
void SPIMgr_AppFlowControl ( bool status )
{
/* Make sure only update if needed */
if (status != SPIMgr_ZAppRxStatus )
{
SPIMgr_ZAppRxStatus = status;
}
/* App is ready to read again, ProcessZAppData have to be triggered too */
if (status == SPI_MGR_ZAPP_RX_READY)
{
SPIMgr_ProcessZAppData(SPI_MGR_DEFAULT_PORT, HAL_UART_RX_TIMEOUT );
}
}
因为在 SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_NOT_READY )中SPI_MGR_ZAPP_RX_NOT_READY=0,而SPIMgr_ZAppRxStatus=1;所以会执行SPIMgr_ZAppRxStatus = status;,即接收状态SPIMgr_ZAppRxStatus =0;不再满足SPIMgr_ZAppRxStatus == SPI_MGR_ZAPP_RX_READY。所以不接收数据了。
重点说明:我认为在串口回调函数加上SPIMgr_AppFlowControl ( SPI_MGR_ZAPP_RX_READY )使得串口处理完一次后立即处理下一次。
但是如果在回调函数结束前(函数内)加上这句,那么就会在流控函数中再次调用了SPIMgr_ProcessZAppData()。而导致协议栈陷入一个死循环,所以最后更正为这句话加在事件处理后,即SPI_INCOMING_ZAPP_DATA事件处理以后。不加是不行的!
三、与应用层联姻
底层和应用层联姻呢?在TI的协议栈很多都是通过注册Register。按键、串口…
通过注册可以传递任务ID,即TaskID。从而利用偷梁换柱、浑水摸鱼的技术将底层的任务传到应用层
//注册函数
void SPIMgr_RegisterTaskID( byte taskID )
{
App_TaskID = taskID;//任务ID被偷梁换柱了
}
//如果应用层调用了这个函数,则此时App_TaskID就变成了应用层的任务ID了……
现在我们在回去简单看一下串口的回调函数
void SPIMgr_ProcessZAppData ( uint8 port, uint8 event )
{
……
if (event & ( HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
{
if ( App_TaskID )//这时已经被偷梁换柱了,可怜的底层,就这样被欺骗了
{
//下面是触发函数
osal_msg_send( App_TaskID, (uint8 *)msg_ptr );
//将msg_ptr发送到App_TaskID,即你注册的应用层
……
}
应用层代码:
我是在SampleApp这个例子里修改的:
事件处理函数修改:
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
……
case SPI_INCOMING_ZAPP_DATA:
SampleApp_UART(MSGpkt);
……
}
void SampleApp_UART(afIncomingMSGPacket_t *pkt)
{
HalUARTWrite(0,(uint8 *)((osal_event_hdr_t *)pkt+1),pkt->hdr.status);//将串口数据写到PC(再写回去)
SPIMgr_AppFlowControl(true); //重新打开接收
}