協(xié)議棧中UART有兩種模式:
1、中斷
2、DMA
對(duì)于這兩種模式具體運(yùn)用在哪一步,糾結(jié)了很久.通過(guò)UART配置結(jié)構(gòu):
typedef struct
{
uint8 *rxBuf;
uint8 rxHead;
uint8 rxTail;
uint8 rxMax;
uint8 rxCnt;
uint8 rxTick;
uint8 rxHigh;
uint8 *txBuf;
#if HAL_UART_BIG_TX_BUF
uint16 txHead;
uint16 txTail;
uint16 txMax;
uint16 txCnt;
#else
uint8 txHead;
uint8 txTail;
uint8 txMax;
uint8 txCnt;
#endif
uint8 txTick;
uint8 flag;
halUARTCBack_t rxCB;
} uartCfg_t;
可以看到協(xié)議棧為串口收發(fā)分別配置了一塊內(nèi)存空間rxBuf和txBuf,具體在HalUARTOpen()里配置.
而中斷與DMA這兩種模式具體就運(yùn)用于 數(shù)據(jù)在串口緩存U0_1DBUF與rxBuf/txBuf之間傳送 的過(guò)程.
串口接收DMA模式:(data) —> U0DBUF —(DMA)—> rxBuf —> HalUARTRead()讀取rxBuf數(shù)據(jù)進(jìn)行處理
串口接收中斷模式:(data) —> U0DBUF —(中斷)—> rxBuf —> HalUARTRead()讀取rxBuf數(shù)據(jù)進(jìn)行處理
串口發(fā)送DMA模式:(data) <— U0DBUF <—(DMA)— txBuf
串口發(fā)送中斷模式:(data) <— U0DBUF <—(中斷)— txBuf
我覺(jué)得這樣理解好像還有問(wèn)題,本身對(duì)DMA不了解,網(wǎng)上又幾乎查不到關(guān)于協(xié)議棧有關(guān)UART_DMA模式具體
流程資料,卡在這里很久,所以打算建立個(gè)大概印象就跳出來(lái),以后再看.![協(xié)議中UART的兩種模式 - 小峰 - happy~](http://image40.360doc.com/DownloadImg/2011/10/2209/18668840_2.gif)
先記錄下中斷模式吧~
###########################################################################
###########################################################################
1、中斷模式(UART接收)
當(dāng)1寫入U(xiǎn)xCSR.RE位時(shí),在UART上數(shù)據(jù)接收就開(kāi)始了.然后UART會(huì)在輸入引腳RXDx中尋找有效起始位,并且設(shè)置UxCSR.ACTIVE位為1.當(dāng)檢測(cè)出有效起始位時(shí),收到的字節(jié)就傳入接收寄存器,UxCSR.RX_BUTE位設(shè)置為1.該操作完成時(shí),產(chǎn)生接收中斷。通過(guò)寄存器UxBUF提供收到的數(shù)據(jù)字節(jié)。當(dāng)UxBUF讀出時(shí),UxCSR.RX_BUTE位由硬件清零.當(dāng)產(chǎn)生中斷時(shí),自然進(jìn)入中斷程序,看下UART0接收中斷函數(shù):
***************************************
#if HAL_UART_0_ENABLE
HAL_ISR_FUNCTION( halUart0RxIsr, URX0_VECTOR )
{
cfg0->rxBuf[cfg0->rxHead] = U0DBUF;
if ( cfg0->rxHead == cfg0->rxMax )
{
cfg0->rxHead = 0;
}
else
{
cfg0->rxHead++;
}
}
#endif
/**************************************
中斷函數(shù)完成了把U0DBUF里一字節(jié)的數(shù)據(jù)傳送到rxBuf[ ]存儲(chǔ)空間去.這里rxHead是指向rxBuf[ ]的指針,看單詞像是指在數(shù)組的頭,其實(shí)應(yīng)理解為rxBuf[ ]接收數(shù)據(jù)的個(gè)數(shù)(以字節(jié)為單位).rxMax是rxBuf[ ]可以存儲(chǔ)最大字節(jié)數(shù),為128.而后面當(dāng)用HalUARTRead()來(lái)讀取rxBuf[ ]時(shí),rxTail應(yīng)理解為rxBuf[]轉(zhuǎn)移出去數(shù)據(jù)的個(gè)數(shù)(同樣以字節(jié)為單位).那數(shù)據(jù)傳送到rxBuf[ ]存儲(chǔ)空間去后呢?先看下pollISR()
*****************************************************************************/
//大概每200ms調(diào)用pollISR()函數(shù).當(dāng)串口UxDBUF接收到一字節(jié)數(shù)據(jù)產(chǎn)生中斷,在中斷
//程序中把UxDBUF中數(shù)據(jù)傳送到rxbuf[ ]中(這有個(gè)坎要跨過(guò)來(lái),pollISR()200ms才被調(diào)用一次,而不是每次中斷后都調(diào)用一次,如果串口接收的是大的數(shù)據(jù)包,則200ms內(nèi)rxbuf[ ]已經(jīng)接收了約48字節(jié)(這個(gè)后面分析),中斷了48次??.當(dāng)然如果串口沒(méi)有接收到數(shù)據(jù),也就是說(shuō)沒(méi)有發(fā)生串口接收中斷,cfg應(yīng)為是為空的,則cnt=0).此后pollISR()進(jìn)行輪詢,主要是重置超時(shí)時(shí)間和計(jì)算rxbuf[ ]還有多少數(shù)據(jù)沒(méi)有讀走(即cnt).然后再跳回到HalUARTPoll()函數(shù)進(jìn)行下一步處理.
static void pollISR( uartCfg_t *cfg )
{
uint8 cnt = UART_RX_AVAIL( cfg ); //計(jì)算rxBuf[]中還有多少數(shù)據(jù)沒(méi)有讀出(以字節(jié)為單位)
if ( !(cfg->flag & UART_CFG_RXF) ) //UART_CFG_RXF:Rx flow is disabled.rx流控制未關(guān)閉
{
// If anything received, reset the Rx idle timer.
//如果又有新的數(shù)據(jù)接收到,則重置超時(shí)時(shí)間
if ( cfg->rxCnt != cnt )
{
cfg->rxTick = HAL_UART_RX_IDLE;
cfg->rxCnt = cnt;
}
/* It is necessary to stop Rx flow in advance of a full Rx buffer because
* bytes can keep coming while sending H/W fifo flushes.
*/
//當(dāng)接收數(shù)據(jù)超過(guò)安全界限的時(shí)候,通過(guò)硬件流控制停止接收數(shù)據(jù)
if ( cfg->rxCnt >= (cfg->rxMax - SAFE_RX_MIN) )
{
RX_STOP_FLOW( cfg );
}
}
}
#endif
/******************************************************************************
pollISR()函數(shù)主要就是設(shè)置rxTick和rxCnt,發(fā)生接收中斷并且接收中斷函數(shù)處理完后,這些參數(shù)都會(huì)改變.
pollISR()執(zhí)行完成后就跳回到HalUARTPoll()中.
看下 HalUARTPoll()
/**************************************
void HalUARTPoll( void )
{
#if ( HAL_UART_0_ENABLE | HAL_UART_1_ENABLE )
static uint8 tickShdw;
uartCfg_t *cfg;
uint8 tick;
//---------以下設(shè)置cfg
#if HAL_UART_0_ENABLE
if ( cfg0 ) //當(dāng)串口接收到數(shù)據(jù)時(shí),在中斷函數(shù)或DMA程序中會(huì)改變cfg0值,如果沒(méi)有接收到數(shù)據(jù),cfg0
為空.
{
cfg = cfg0;
}
#endif
//---------
#if HAL_UART_1_ENABLE
if ( cfg1 )
{
cfg = cfg1;//同上
}
#endif
//---------
// Use the LSB of the sleep timer (ST0 must be read first anyway).
//睡眠定時(shí)器.ST0為睡眠定時(shí)器計(jì)數(shù)值的低8位
tick = ST0 - tickShdw;
tickShdw = ST0;
do
{
//------------------------發(fā)送超時(shí)時(shí)間
if ( cfg->txTick > tick )
{
cfg->txTick -= tick;
}
else
{
cfg->txTick = 0;
}
//------------------------接收超時(shí)時(shí)間
if ( cfg->rxTick > tick )
{
cfg->rxTick -= tick;
}
else
{
cfg->rxTick = 0;
}
//------------------------
#if HAL_UART_ISR
#if HAL_UART_DMA
if ( cfg->flag & UART_CFG_DMA )
{
pollDMA( cfg ); //pollDMA( cfg )
}
else
#endif
{
pollISR( cfg ); //pollISR( cfg )
}
#elif HAL_UART_DMA
pollDMA( cfg );
#endif
/* The following logic makes continuous callbacks on any eligible flag(合法標(biāo)志)
* until the condition corresponding to the flag is rectified.
* So even if new data is not received, continuous callbacks are made.
*/
if ( cfg->rxHead != cfg->rxTail ) //初始化時(shí)rxHead=rxTail=0,不相等,rxbuf[ ]中有數(shù)據(jù)
{
uint8 evt;
if ( cfg->rxHead >= (cfg->rxMax - SAFE_RX_MIN) )
{
evt = HAL_UART_RX_FULL; //rxBuf接收[ ]滿,則觸發(fā)事件
}
else if ( cfg->rxHigh && (cfg->rxHead >= cfg->rxHigh) )
{
evt = HAL_UART_RX_ABOUT_FULL; //rxBuf[ ]接收到預(yù)設(shè)值(默認(rèn)80字節(jié)),則觸發(fā)事件
}
else if ( cfg->rxTick == 0 )
{
evt = HAL_UART_RX_TIMEOUT; //RX接收事件超時(shí),則觸發(fā)事件
}
else
{
evt = 0;
}
if ( evt && cfg->rxCB ) //有事件并且有回調(diào)函數(shù)
{
cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt ); //調(diào)用回調(diào)函數(shù)處理這些事件
}
}
………………
} while ( TRUE );
#else
return;
#endif
}
/**************************************
可以看到,初始化時(shí)rxHead=rxTail=0,如果發(fā)生接收中斷,在中斷服務(wù)函數(shù)中把UxDBUF中的數(shù)據(jù)傳送到rxbuf[ ]中(這一步也可以在DMA服務(wù)程序中完成),則rxHead與rxTail值不等(rxHead是rxBuf[ ]接收數(shù)據(jù)的個(gè)數(shù),rxTail是rxBuf[ ]轉(zhuǎn)移出去數(shù)據(jù)個(gè)數(shù)),再根據(jù)兩值的差值,判斷具體事件evt,最后再調(diào)用回調(diào)函數(shù):cfg->rxCB();回調(diào)函數(shù)在哪?回調(diào)函數(shù) cfg->rxCB( ((cfg->flag & UART_CFG_U1F)!=0), evt ),在cfg中有halUARTCBack_t rxCB;則肯定有個(gè)地方配置cfg這個(gè)結(jié)構(gòu)體,這個(gè)地方是HalUARTOpen( uint8 port, halUARTCfg_t *config );在這個(gè)函數(shù)中可以看到,結(jié)構(gòu)體config的內(nèi)容一項(xiàng)項(xiàng)賦給了結(jié)構(gòu)體cfg,其中包括cfg->rxCB = config->callBackFunc;那肯定有個(gè)地方配置config這個(gè)結(jié)構(gòu)體,這個(gè)地方是SPIMgr_Init (),這個(gè)函數(shù)有一句HalUARTOpen (SPI_MGR_DEFAULT_PORT, &uartConfig),*config與&uartConfig是同一halUARTCfg_t類型結(jié)構(gòu)體,而且SPIMgr_Init ()函數(shù)中對(duì)uartConfig進(jìn)行了定義,如下:
/* UART Configuration */
uartConfig.configured = TRUE;
uartConfig.baudRate = SPI_MGR_DEFAULT_BAUDRATE;
uartConfig.flowControl = 0;//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; //回調(diào)函數(shù)
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
uartConfig.callBackFunc = SPIMgr_ProcessZAppData; //回調(diào)函數(shù)
#else
uartConfig.callBackFunc = NULL;
#endif
來(lái)看下SPIMgr_ProcessZToolData ( uint8 port, uint8 event )
/*****************************************************
* @fn SPIMgr_ProcessZToolRxData
*
* @brief | SOP | CMD | Data Length | FSC | //幀格式
* | 1 | 2 | 1 | 1 |
*
* Parses the data and determine either is SPI or just simply serial data
* then send the data to correct place (MT or APP)
*
* @param pBuffer - pointer to the buffer that contains the data
* length - length of the buffer
*
*
* @return None
void SPIMgr_ProcessZToolData ( uint8 port, uint8 event )
{
uint8 ch;
/* Verify events */
if (event == HAL_UART_TX_FULL)
{
// Do something when TX if full
return;
}
if (event & (HAL_UART_RX_FULL | HAL_UART_RX_ABOUT_FULL | HAL_UART_RX_TIMEOUT))
{
while (Hal_UART_RxBufLen(SPI_MGR_DEFAULT_PORT)) //RxBuf[ ]中字節(jié)數(shù),即有數(shù)據(jù)
{
HalUARTRead (SPI_MGR_DEFAULT_PORT, &ch, 1); //每次讀取1字節(jié)到ch所指空間
//接下來(lái)對(duì)所讀字節(jié)的state進(jìn)行判斷
switch (state) //state 參見(jiàn)上面宏定義/* State values for ZTool protocal */
{
case SOP_STATE:
if (ch == SOP_VALUE) //SOP_VALUE=0x02
state = CMD_STATE1;
break;
case CMD_STATE1:
CMD_Token[0] = ch;
state = CMD_STATE2;
break;
case CMD_STATE2:
CMD_Token[1] = ch;
state = LEN_STATE;
break;
case LEN_STATE:
LEN_Token = ch;
if (ch == 0)
state = FCS_STATE;
else
state = DATA_STATE; //state = DATA_STATE=0x04
tempDataLen = 0;
/* Allocate memory for the data */
SPI_Msg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof ( mtOSALSerialData_t ) +
2+1+LEN_Token );
if (SPI_Msg) //構(gòu)造消息:把SOP字節(jié)和FCS字節(jié)去掉,剩下2字節(jié)的CMD和1字節(jié)的Data
{
/* Fill up what we can */
SPI_Msg->hdr.event = CMD_SERIAL_MSG; //CMD_SERIAL_MSG
SPI_Msg->msg = (uint8*)(SPI_Msg+1);
SPI_Msg->msg[0] = CMD_Token[0];
SPI_Msg->msg[1] = CMD_Token[1];
SPI_Msg->msg[2] = LEN_Token;
}
else
{
state = SOP_STATE;
return;
}
break;
case DATA_STATE:
SPI_Msg->msg[3 + tempDataLen++] = ch;
if ( tempDataLen == LEN_Token )
state = FCS_STATE;
break;
case FCS_STATE: //幀校驗(yàn)序列(FCS),則判斷是接收數(shù)據(jù)是否正確
FSC_Token = ch;
/* Make sure it's correct */
if ((SPIMgr_CalcFCS ((uint8*)&SPI_Msg->msg[0], 2 + 1 + LEN_Token) == FSC_Token))
{
osal_msg_send( MT_TaskID, (byte *)SPI_Msg ); //接收數(shù)據(jù)正確則發(fā)送系統(tǒng)信息觸發(fā)MT層任
務(wù).事件為CMD_SERIAL_MSG
}
else
{
/* deallocate the msg */
osal_msg_deallocate ( (uint8 *)SPI_Msg);
}
/* Reset the state, send or discard the buffers at this point */
state = SOP_STATE;
break;
default:
break;
}
}
}
}
#endif*********************************************************
(這個(gè)函數(shù)只是總體上了解了下)
回調(diào)函數(shù)SPIMgr_ProcessZToolData()對(duì)rxbuf[]中的每一字節(jié)數(shù)據(jù)進(jìn)行分析,并且構(gòu)造一個(gè)發(fā)往系統(tǒng)的消息包(具體事件為SPI_Msg->hdr.event = CMD_SERIAL_MSG)然后調(diào)用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )給系統(tǒng)發(fā)送消息,而下面肯定又會(huì)調(diào)用osal_set_event()來(lái)設(shè)置任務(wù)事件發(fā)生標(biāo)志,最后調(diào)用MT層的事件處理函數(shù),下面來(lái)看下MT層的事件處理函數(shù):
/**********************************************************
* @fn MT_ProcessEvent
*
* @brief
*
* MonitorTest Task Event Processor. This task is put into the
* task table.
*
* @param byte task_id - task ID of the MT Task
* @param UINT16 events - event(s) for the MT Task
*
* @return void
*/
UINT16 MT_ProcessEvent( byte task_id, UINT16 events )
{
uint8 *msg_ptr;
………………
if ( events & SYS_EVENT_MSG )
{
while ( (msg_ptr = osal_msg_receive( MT_TaskID )) )
{
MT_ProcessCommand( (mtOSALSerialData_t *)msg_ptr );
}
………………
/*********************************************************************
可以看到它調(diào)用了MT_ProcessCommand( (mtOSALSerialData_t *)msg_ptr )來(lái)進(jìn)行下一步的操作,來(lái)看下MT_ProcessCommand()這個(gè)函數(shù):
/*********************************************************************
* @fn MT_ProcessCommand *
* @brief
*
* Process Event Messages.
*
* @param byte *msg - pointer to event message
*
* @return
*/
void MT_ProcessCommand( mtOSALSerialData_t *msg )
{
……………………
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
case CMD_SERIAL_MSG:
MT_ProcessSerialCommand( msg->msg );
break;
……………………
}
/*********************************************************************
通過(guò)上面的MT_ProcessCommand(),再根據(jù)先前SPI_Msg->hdr.event = CMD_SERIAL_MSG,可以得到它調(diào)用MT_ProcessSerialCommand( msg->msg )這個(gè)函數(shù),而MT_ProcessSerialCommand()函數(shù)根據(jù)參數(shù)cmd來(lái)選擇命令,包括讀RAM,寫RAM等等很多命令指令來(lái)對(duì)接收到的數(shù)據(jù)進(jìn)行下一步操作(先不鉆了……),對(duì)于這個(gè)cmd,cmd = BUILD_UINT16( msg[1], msg[0] ),SPIMgr_ProcessZToolData()對(duì)msg[1], msg[0]進(jìn)行了配置:
SPI_Msg->msg[0] = CMD_Token[0];
SPI_Msg->msg[1] = CMD_Token[1];
而CMD_Token[0]與CMD_Token[1]的值又與SPIMgr_ProcessZToolData()中的參數(shù)ch密切相關(guān),對(duì)于這個(gè)ch具體的定義,只是看到個(gè)uint8 ch;,沒(méi)看到具體初始化為什么值.個(gè)人在想,是不是通過(guò)對(duì)參數(shù)ch的自由設(shè)定來(lái)選擇串口命令cmd,通過(guò)cmd來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)的不同處理……【現(xiàn)在發(fā)現(xiàn)這個(gè)應(yīng)該是跟ZTOOL有關(guān),晴天那天說(shuō)ZTOOL上面可以發(fā)送不同命令的】
迷糊中!~
~糾結(jié)中!~![協(xié)議中UART的兩種模式 - 小峰 - happy~](http://image40.360doc.com/DownloadImg/2011/10/2209/18668840_4.gif)
上面分析了UART中斷模式下,從接收到數(shù)據(jù)進(jìn)入中斷函數(shù),把數(shù)據(jù)寫入rxbuf[ ],200ms調(diào)用一次pollISR()輪詢,跳回到HalUARTPoll()進(jìn)行數(shù)據(jù)處理……………….那這個(gè)具體輪詢時(shí)間rxTick約為200ms是怎么得出來(lái)的?借用《Z-STACK問(wèn)題之串口結(jié)構(gòu)uartCfg_t亂說(shuō)》這篇文章的分析:
************
cfg->rxTick = HAL_UART_RX_IDLE
#define HAL_UART_RX_IDLE (6 * RX_MSECS_TO_TICKS)
#define RX_MSECS_TO_TICKS 33 (// The timeout tick is at 32-kHz, so multiply msecs by 33.)
(這個(gè)超時(shí)計(jì)數(shù)采用的是睡眠定時(shí)器,32KHZ時(shí)鐘,一個(gè)微秒計(jì)數(shù)約33次)
因此cfg->rxTick=198,即大約200ms調(diào)用一次pollISR();
論證 #define SAFE_RX_MIN 48 // bytes - max expected per poll @ 115.2k
因?yàn)镃C2430串口波特率為38400bps下,一個(gè)字節(jié)需要時(shí)間約為:4.16ms(這里……這?),那么198/4.16等于47.6(198ms是中斷處理數(shù)據(jù)的間隔,這期間串口是不斷接收數(shù)據(jù),那在這期間能接收到多少數(shù)據(jù)呢?為47.6字節(jié),因此要保證至少48字節(jié)的存儲(chǔ)空間),約為48.所以這也就是上面定義這個(gè)48的原因。因?yàn)?cfg->rxTick定義了多久處理一次串口緩存區(qū),而為了保證串口緩沖區(qū)不被新的數(shù)據(jù)覆蓋,那么在這個(gè)間隔期間就必須保證大于48字節(jié)存儲(chǔ)空間空閑。
*************
hal_uart.c有這段話:
/* Need to leave enough of the Rx buffer free to handle the incoming bytes
* after asserting flow control, but before the transmitter has obeyed it.
* At the max expected baud rate of 115.2k, 16 bytes will only take ~1.3 msecs,
* but at the min expected baud rate of 38.4k, they could take ~4.2 msecs.
* SAFE_RX_MIN and DMA_RX_DLY must both be consistent according to
* the min & max expected baud rate.
*/
我算了半天也算不出上面幾個(gè)數(shù)來(lái)……悲?。∠冗@樣吧……
對(duì)于串口里面的幾個(gè)參數(shù),后面直接轉(zhuǎn)載《Z-STACK問(wèn)題之串口結(jié)構(gòu)uartCfg_t亂說(shuō)》文章好了.
********************************************************
UART接收數(shù)據(jù)中斷模式流程:(純屬個(gè)人理解,還有很多地方不清楚!
)
(1)
ZMain.c調(diào)用HalDriverInit(),HalDriverInit調(diào)用HalUARTInit()初始化串口驅(qū)動(dòng)程序任務(wù)初始化函數(shù),MT_TaskInit()調(diào)用SPIMgr_Init()初始化串口配置
(2)
串口接收到數(shù)據(jù)產(chǎn)生中斷,進(jìn)入接收中斷服務(wù)程序,把串口緩存UxDBUF中的數(shù)據(jù)傳送到rxbuf[]中去.UxDBUF只能存放1字節(jié),rxbuf[]能存放128字節(jié),但要空出48字節(jié)作為安全區(qū),因此接收數(shù)據(jù)達(dá)到80字節(jié)時(shí)作為一個(gè)臨界值rxHigh,可以觸發(fā)事件HAL_UART_RX_ABOUT_FUL.
(3)
系統(tǒng)主循環(huán)函數(shù)進(jìn)入HalUARTPoll()輪詢串口,大約每200ms調(diào)用一次pollISR(),然后跳回HalUARTPoll()對(duì)rxbuf[]中的數(shù)據(jù)進(jìn)行處理——>調(diào)用回調(diào)函數(shù)SPIMgr_ProcessZToolData()分析rxbuf[]中每一字節(jié),構(gòu)造發(fā)往系統(tǒng)的消息——>調(diào)用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )發(fā)送消息——>觸發(fā)MT任務(wù)事件,調(diào)用MT任務(wù)事件處理函數(shù) MT_ProcessEvent()——>調(diào)用MT_ProcessCommand(),根據(jù)msg->hdr.event——>調(diào)用MT_ProcessSerialCommand(),根據(jù)參數(shù)cmd來(lái)進(jìn)行最終處理.
********************************************************
UART發(fā)送數(shù)據(jù)中斷模式暫時(shí)不分析了,一頭霧水……
############################################(下面小段于2010.5.25更新)
對(duì)于串口的回調(diào)函數(shù),SPIMgr_Init()中配置了兩個(gè):
#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
uartConfig.callBackFunc = SPIMgr_ProcessZToolData; //回調(diào)函數(shù)
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
uartConfig.callBackFunc = SPIMgr_ProcessZAppData; //回調(diào)函數(shù)
#else
uartConfig.callBackFunc = NULL;
有什么區(qū)別呢?在《Zigbee技術(shù)規(guī)范與協(xié)議棧分析》這篇文章中看到過(guò)一段話:
作為協(xié)調(diào)器,如果程序使用了串口調(diào)試助手,則DMA將上位機(jī)的數(shù)據(jù)按照一個(gè)字節(jié)波特率加一個(gè)字節(jié)數(shù)據(jù)的形式組裝到cfg->rxBuf中供其他函數(shù)調(diào)用,并且通過(guò)回調(diào)函數(shù)SPIMgr_ProcessZToolData ( uint8 port, uint8 event )將任務(wù)的ID和強(qiáng)制事件發(fā)送到任務(wù)列表中,供主循環(huán)處理函數(shù)掃描;作為終端節(jié)點(diǎn)和路由設(shè)備,無(wú)法使用串口調(diào)試助手,則通過(guò)回調(diào)函數(shù) SPIMgr_ProcessZAppData ( uint8 port, uint8 event ) 將任務(wù)的ID和強(qiáng)制事件發(fā)送到任務(wù)列表中。當(dāng)掃描至參數(shù)events=1,則進(jìn)入相應(yīng)層的處理程序進(jìn)行任務(wù)ID和events的約定比對(duì),完成相應(yīng)的功能。
根據(jù)這段話個(gè)人在想:ZTOOL上可以發(fā)命令數(shù)據(jù)到ZC??,協(xié)調(diào)器調(diào)用SPIMgr_ProcessZToolData()進(jìn)行處理,那ZR和ED的串口接收到數(shù)據(jù)(怎么接收?)調(diào)用SPIMgr_ProcessZAppData(),SPIMgr_ProcessZAppData()最終會(huì)調(diào)用osal_msg_send():
/**************************************
void SPIMgr_ProcessZAppData ( uint8 port, uint8 event )
{
…………
if ( msg_ptr )
{
msg_ptr->event = SPI_INCOMING_ZAPP_DATA;//SPI_INCOMING_ZAPP_DATA
msg_ptr->status = length;
/* Read the data of Rx buffer *///Rx buffer是讀到上面為msg_ptr 分配的空間去的
HalUARTRead( SPI_MGR_DEFAULT_PORT, (uint8 *)(msg_ptr + 1), length );
/* Send the raw data to application...or where ever */
osal_msg_send( App_TaskID, (uint8 *)msg_ptr );
}
…………
}
/**************************************
至于把串口接收到的數(shù)據(jù)傳送到哪里去,這就跟App_TaskID有關(guān),而App_TaskID是通過(guò)下面這個(gè)函數(shù)來(lái)定義的:
/**************************************
* @fn MT_SerialRegisterTaskID
*
* @brief
*
* This function registers the taskID of the application so it knows
* where to send the messages whent they come in.
*
* @param void
*
* @return void
**************************************
void SPIMgr_RegisterTaskID( byte taskID )
{
App_TaskID = taskID;
}
/*************************************
可以看到,如果想把串口接收到的數(shù)據(jù)送到應(yīng)用層任務(wù)(比如SampleApp)中,那這里應(yīng)該注冊(cè)SPIMgr_RegisterTaskID( SampleApp_TaskID ).這樣就可以觸發(fā)應(yīng)用層任務(wù)(SampleApp)的事件(SPI_INCOMING_ZAPP_DATA),在事件處理函數(shù)中添加一個(gè)對(duì)事件SPI_INCOMING_ZAPP_DATA進(jìn)行處理的程序就可以.
但是,我好像看到協(xié)調(diào)器設(shè)備的預(yù)編譯里都是ZTOOL_P1,終端和路由功能的設(shè)備的預(yù)編譯為XZTOOL_P1,沒(méi)有編譯ZAPP_P1和ZAPP_P2,也就是沒(méi)有回調(diào)函數(shù),是不是就不能進(jìn)行串口傳輸了?
那如果我在終端和路由節(jié)點(diǎn)上面編譯ZTOOL_P1是否可行??
那如果我在協(xié)調(diào)器上編譯ZAPP_P1,再在應(yīng)用任務(wù)事件處理函數(shù)中添加相應(yīng)SPI_INCOMING_ZAPP_DATA事件的處理程序,串口接收的數(shù)據(jù)是否可以順利傳送到應(yīng)用層??
——等有了實(shí)驗(yàn)套件,我再驗(yàn)證下!
個(gè)人在想,協(xié)調(diào)器串口接收的數(shù)據(jù)送到應(yīng)用層任務(wù)中的方法有:
(1)通過(guò)默認(rèn)的SPIMgr_ProcessZToolData()處理函數(shù)把數(shù)據(jù)發(fā)送到應(yīng)用層供應(yīng)用層使用(這點(diǎn)只是猜測(cè),還沒(méi)找著,畢竟MT_ProcessSerialCommand()函數(shù)這么多命令)
(2)通過(guò)SPIMgr_RegisterTaskID()注冊(cè)相應(yīng)TaskID,再通過(guò)SPIMgr_ProcessZAppData()處理函數(shù)把數(shù)據(jù)發(fā)送到相應(yīng)任務(wù)中供其使用(也只是猜測(cè),沒(méi)有驗(yàn)證)
(3)通過(guò)串口讀寫函數(shù)HalUARTRead()和HalUARTWrite(),這兩個(gè)函數(shù)可以在應(yīng)用層中直接調(diào)用來(lái)讀取串口接收到的數(shù)據(jù)以及通過(guò)串口發(fā)送數(shù)據(jù)。
HalUARTRead( uint8 port, uint8 *buf, uint16 len )//從rxbuf[ ]讀取len長(zhǎng)度的數(shù)據(jù)到*buf所指空間
HalUARTWrite( uint8 port, uint8 *buf, uint16 len )//把len長(zhǎng)度的數(shù)據(jù)從*buf所指空間發(fā)送到txbuf[ ]
以上問(wèn)題見(jiàn)《協(xié)議棧中串口的兩種回調(diào)函數(shù)》記錄。。。。。。。。
純屬個(gè)人想法,有待驗(yàn)證!
###########################################################################
###########################################################################
2、DMA模式(UART接收)
hal_board_cfg.h中有這段話:
/* The preferred method of implementation is by DMA for faster buad rate
* support. Customer may prefer to use the DMA channels for something else,
* in which case USART can be driven by ISR. Also, if the 2nd USART is to be
* used, this module does not currently support using 2 more DMA channels for
* it, so it must use ISR.
*/
對(duì)于USART操作,波特率高的情況下推薦使用DMA驅(qū)動(dòng)模式.用戶可能習(xí)慣把DMA應(yīng)用在其它地方,這種情況下可以用中斷模式.同樣,如果第二個(gè)USART也被使用了,由于USART模塊不能在同一時(shí)間支持兩條以上DMA通道,因此這個(gè)時(shí)候必須使用中斷來(lái)驅(qū)動(dòng).
********************
DMA:
允許不同速度的硬件裝置來(lái)溝通,而不需要依于CPU 的大量中斷負(fù)載。否則,CPU需要從來(lái)源把每一片段的資料復(fù)制到暫存器,然后把他們?cè)俅螌懟氐叫碌牡胤健T谶@個(gè)時(shí)間中,CPU 對(duì)于其他的工作來(lái)說(shuō)就無(wú)法使用。DMA傳輸方式無(wú)需CPU直接控制傳輸,也沒(méi)有中斷處理方式那樣保留現(xiàn)場(chǎng)和恢復(fù)現(xiàn)場(chǎng)的過(guò)程,通過(guò)硬件為RAM與I/O設(shè)備開(kāi)辟一條直接傳送數(shù)據(jù)的通路,使CPU的效率大為提高。
********************
typedef struct {
uint8 srcAddrH;
uint8 srcAddrL;
uint8 dstAddrH;
uint8 dstAddrL;
uint8 xferLenV;
uint8 xferLenL;
uint8 ctrlA;
uint8 ctrlB;
} halDMADesc_t; //DMA描述符結(jié)構(gòu)體
每一個(gè)通道都有相應(yīng)的描述符.DMA傳送首先要進(jìn)行DMA參數(shù)配置,需要配置以下參數(shù):
(1)源地址:DMA信道要讀的數(shù)據(jù)的首地址
(2)目標(biāo)地址:DMA信道從源地址讀出的要寫數(shù)據(jù)的首地址.目標(biāo)地址要可寫
(3)傳送長(zhǎng)度
(4)可變長(zhǎng)度(VLEN)設(shè)置
(5)優(yōu)先級(jí)別
(6)觸發(fā)事件
(7)源地址和目標(biāo)地址增量
(8)DMA傳送模式
(9)字節(jié)傳送或字傳送
(10)中斷屏蔽
(11)設(shè)置M8模式
CC2430中DMA有5條通道,UART默認(rèn)的是哪個(gè)通道呢?
********************
HalUARTInit()中初始化為:
// Setup Tx by DMA.
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX );
// Setup Rx by DMA.
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );
#define HAL_DMA_GET_DESC1234( a ) (dmaCh1234+((a)-1))
// Used by DMA macros to shift 1 to create a mask for DMA registers.
#define HAL_DMA_CH_TX 3 //協(xié)議棧默認(rèn)的用于UART發(fā)送的DMA通道 映射后為DMA通道2
#define HAL_DMA_CH_RX 4 //協(xié)議棧默認(rèn)的用于UART接收的DMA通道 映射后為DMA通道3
********************
對(duì)這兩條分別用于發(fā)送與接收的DMA通道的配置是如何的?
********************
在HalUARTInit()中初始化為:
#if HAL_UART_DMA
//---------------------配置發(fā)送為DMA模式;數(shù)據(jù)從內(nèi)存空間傳送到DMA_UDBUF???
/*選擇DMA通道*/
// Setup Tx by DMA.
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_TX ); //協(xié)議棧默認(rèn)的用于UART發(fā)送的DMA通道映射后為DMA通道2
/*配置ch->dstAddrH/L*/
// The start address of the destination.目的地的起始地址
HAL_DMA_SET_DEST( ch, DMA_UDBUF ); //DMA_UDBUF為目的地,通過(guò)這個(gè)函數(shù)把DMA_UDBUF地址的高8位
//賦給ch->dstAddrH,低8位賦給ch->dstAddrL
/*配置ch->xferLenV*/
// Using the length field to determine how many bytes to transfer.
HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN ); //HAL_DMA_VLEN_USE_LEN=0x00 //配置VLEN域使用LEN字段
/*配置ch->ctrlA(通過(guò)多次配置ctrlA的每一位,同理包括ctrlB)*/
// One byte is transferred each time. //初始化為一次傳輸一字節(jié)
HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_BYTE );
/*配置ch->ctrlA*/
// The bytes are transferred 1-by-1 on Tx Complete trigger. //一次觸發(fā)只傳輸一個(gè)字節(jié),觸發(fā)源為UART0 TX完成
HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_TX );
/*配置ch->ctrlB*/
// The source address is decremented by 1 byte after each transfer.每次傳送后源地址減1
HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_1 );
/*配置ch->ctrlB*/
// The destination address is constant - the Tx Data Buffer.目的地址不變,始終為Tx Data Buffer
HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_0 );
/*配置ch->ctrlB*/
// The DMA is to be polled and shall not issue an IRQ upon completion.傳送完成后不傳送中斷請(qǐng)求
HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );
/*配置ch->ctrlB*/
// Xfer all 8 bits of a byte xfer. 8位一字節(jié)
HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );
/*配置ch->ctrlB*/
// DMA Tx has shared priority for memory access - every other one. 高優(yōu)先級(jí)
HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );
//---------------------配置接收為DMA模式:數(shù)據(jù)從DMA_UDBUF傳送到內(nèi)存空間???
// Setup Rx by DMA.
ch = HAL_DMA_GET_DESC1234( HAL_DMA_CH_RX );//
協(xié)議棧默認(rèn)的用于UART接收的DMA通道 映射后為DMA通道3
// The start address of the source. 發(fā)源地起始地址
HAL_DMA_SET_SOURCE( ch, DMA_UDBUF );
// Using the length field to determine how many bytes to transfer.
HAL_DMA_SET_VLEN( ch, HAL_DMA_VLEN_USE_LEN );
/* The trick is to cfg DMA to xfer 2 bytes for every 1 byte of Rx.
* The byte after the Rx Data Buffer is the Baud Cfg Register,
* which always has a known value. So init Rx buffer to inverse of that
* known value. DMA word xfer will flip the bytes, so every valid Rx byte
* in the Rx buffer will be preceded by a DMA_PAD char equal to the
* Baud Cfg Register value.
*/
HAL_DMA_SET_WORD_SIZE( ch, HAL_DMA_WORDSIZE_WORD );
// The bytes are transferred 1-by-1 on Rx Complete trigger.
HAL_DMA_SET_TRIG_MODE( ch, HAL_DMA_TMODE_SINGLE );
HAL_DMA_SET_TRIG_SRC( ch, DMATRIG_RX );
// The source address is constant - the Rx Data Buffer.
HAL_DMA_SET_SRC_INC( ch, HAL_DMA_SRCINC_0 );
// The destination address is incremented by 1 word after each transfer.
HAL_DMA_SET_DST_INC( ch, HAL_DMA_DSTINC_1 );
// The DMA is to be polled and shall not issue an IRQ upon completion.
HAL_DMA_SET_IRQ( ch, HAL_DMA_IRQMASK_DISABLE );
// Xfer all 8 bits of a byte xfer.
HAL_DMA_SET_M8( ch, HAL_DMA_M8_USE_8_BITS );
// DMA has highest priority for memory access.
HAL_DMA_SET_PRIORITY( ch, HAL_DMA_PRI_HIGH );
#endif
********************
對(duì)于發(fā)送,觸發(fā)事件為HAL_DMA_TRIG_UTX1/0 :USART1/0 TX complete UART中數(shù)據(jù)發(fā)送完成就觸發(fā)DMA從內(nèi)存空間txbuf[ ]傳送數(shù)據(jù)到串口??
對(duì)于接收,觸發(fā)事件為HAL_DMA_TRIG_URX1/0:USART1/0 RX complete UART中數(shù)據(jù)接收完成就觸發(fā)DMA把數(shù)據(jù)從串口緩存?zhèn)魉偷絻?nèi)存空間rxbuf[ ]??
——我不敢確定!
事實(shí)上這一步所完成的任務(wù)和中斷服務(wù)程序完成的任務(wù)是一樣的,發(fā)送中斷服務(wù)函數(shù):把數(shù)據(jù)從txbuf[]傳送到串口UDBUF;接收中斷服務(wù)函數(shù):把數(shù)據(jù)從UDBUF傳送到rxbuf[ ];
UART接收數(shù)據(jù)DMA模式流程:(純屬個(gè)人理解,還有很多地方不清楚!
)
(1)
ZMain.c調(diào)用HalDriverInit(),HalDriverInit調(diào)用HalDmaInit()初始化DMA通道01234的地址:
halDMADesc_t dmaCh0;
halDMADesc_t dmaCh1234[4];
HAL_DMA_SET_ADDR_DESC0( &dmaCh0 );
HAL_DMA_SET_ADDR_DESC1234( dmaCh1234 );
(2)
HalUARTInit()函數(shù)中對(duì)通道2和3進(jìn)行配置.
(3)
系統(tǒng)主循環(huán)函數(shù)進(jìn)入HalUARTPoll()輪詢串口,大約每200ms調(diào)用一次 pollDMA()???,然后跳回HalUARTPoll()對(duì)rxbuf[]中的數(shù)據(jù)進(jìn)行處理——>調(diào)用回調(diào)函數(shù)SPIMgr_ProcessZToolData()分析rxbuf[]中每一字節(jié),構(gòu)造發(fā)往系統(tǒng)的消息——>調(diào)用osal_msg_send( MT_TaskID, (byte *)SPI_Msg )發(fā)送消息——>觸發(fā)MT任務(wù)事件,調(diào)用MT任務(wù)事件處理函數(shù) MT_ProcessEvent()——>調(diào)用MT_ProcessCommand(),根據(jù)msg->hdr.event——>調(diào)用MT_ProcessSerialCommand(),根據(jù)參數(shù)cmd來(lái)進(jìn)行最終處理.