---------------------------------------------------------編程
Author :tiger-john
WebSite :blog.csdn.net/tigerjb併發
Email :jibo.tiger@gmail.comapp
Update-Time : 2011年1月29日星期六異步
Tiger聲明:本人鄙視直接複製本人文章而不加出處的我的或團體,但不排斥別人轉載tiger-john的文章,只是請您註明出處並和本人聯繫或留言給我。3Q函數
---------------------------------------------------------工具
一.串口接收數據在UC/OS設計中應注意的問題ui
1. 串口通訊的數據接收過程:spa
1> UART 接收FIFO接收到預約字節後觸發中斷.net
2> ISR讀取接收到的內容並保存設計
3> 通過一次或若干次ISR完成一個通訊幀的接收(拼裝通訊幀)
4> 處理和解釋通訊內容
5> 根據處理結果觸發其餘任務
2. 串口數據接收程序設計時,應該考慮的問題:
1>即便以上的操做過程很簡單,也最好不要把它所有安排在ISR中完成,若是放在一塊兒的話,就會給UART0通訊帶來危機(此處具體請看前面的文章)。
2>因此要安排一個與ISR關聯的「串口接收」任務來完成後面的工做。再建立一個幀緩衝區。在接收的過程當中,將接收到的內容寫入幀緩衝區。接收完一幀後,處理和解釋過程須要讀幀緩衝區的內容。
3>將寫幀緩衝區的操做安排在ISR中完成,讀幀緩衝去的操做安排在串口接收任務中完成。
4>因爲ISR和串口接收任務是併發程序單元,存在資源同步問題,故須要對幀緩衝區進行互斥訪問。
二.設計ISR與串口接收任務之間的通訊方法:
1. ISR的主要功能是響應異步事件,該異步事件將觸發一系列操做。ISR設計的基本原則是:儘量簡短。
2.ISR與關聯任務的通訊方式有兩種類型:信號型和數據型。
1>當使用信號量進行通訊時,ISR只完成發送信號量的工做,表示事件已經發生,經過信號量的同步功能觸發關聯任務。
2>當使用數據進行通訊時,ISR須要完成對異步事件的信息進行採集工做,而後使用消息郵箱(或消息隊列)將數據發送給關聯任務,由關聯任務完成後續數據處理工做。
3>作項目時常見的三種狀況:
Ø 觸發ISR的事件不包含數據:不須要對事件進行信息採集。此時,ISR使用信號量與關聯任務進行通訊。
Ø 觸發ISR的事件是包含數據的低頻事件:將數據採集的工做放在關聯任務中完成,(產生的時刻延遲與採樣週期相比能夠忽略不計,對採集數據的質量沒有影響。此時,ISR使用信號量與關聯任務進行通訊,從而簡化了ISR。
Ø 觸發ISR的事件是包含數據的中高頻事件:數據採集的工做放在關聯任務中完成時,產生的時延與採樣週期相比不能忽略不計時,對採樣數據的質量有影響。此時,關聯任務從消息郵箱中獲得消息的數據,並完成後續處理工做。
Ø 觸發ISR的事件是包含數據的非週期高頻率事件:對於非週期高頻事件,其最短事件間隔可能小於一個事件數據處理的耗時,若是使用消息郵箱進行通訊,就可能會出現數據丟失現象。此時,數據採集的工做應該在ISR中完成,由ISR使用具備數據緩衝功能的消息隊列與關聯任務進行通訊。關聯任務從消息隊列中獲得消息的數據,並完成後續處理工做。
Tiger-John說明:
具體採用那一種方式來實現ISR與串口接收任務之間的通訊要視具體狀況而定。
如下用信號量和消息隊列兩種方式來實現串口接收編程
三. UC/OS串口接收數據編程
經過一個程序來分析UC/OS串口接收數據設計和實現
程序設計目標:
用串口中斷接收上位機發送的8字節數據,再把它們傳送給上位機。
u 用信號量的方式
1.系統有那些任務組成
2> 接收任務
3> 接收中斷服務例程
4> 發送任務
2.各任務之間的關係
3.啓動任務流程:
l 定義各類通訊工具(例如:信號量)
l 系統硬件初始化
l 初始化UART0
l 建立各個任務
l 建立各類通訊工具
l 刪除本身
程序:
/********************************************************************
** Task0(啓動任務)
********************************************************************/
void Task0 (void *pdata)
{
pdata = pdata;
TargetInit(); //硬件初始化
UART0_Init(115200); //初始化串口
Sem_SendFlag = OSSemCreate(0); //建立發送信號量
Sem_StartFlag = OSSemCreate(1); //建立開始信號量
OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],4); //建立接收任務
OSTaskCreate(Task2,(void *)0, &TaskStk2[TaskStkLengh - 1],5); //建立發送任務
OSTaskDel(OS_PRIO_SELF); //刪除本身
}
4.接收任務流程
l 等待開始信號量
l 處理和解釋通訊內容(本程序較簡單,不涉及)
程序:
/********************************************************************
Task1(接收任務)
********************************************************************/
void Task1 (void *pdata)
{
uint8 err;
pdata = pdata;
while(1)
{
OSSemPend(Sem_StartFlag,0,&err); //等帶開始信號量
//如下能夠根據具體業務來編寫處理和解釋通訊內容
}
}
5.串口中斷接收流程:
l 關中斷
l 清除串口中斷標誌位
l 清除中斷控制寄存器
l 接收數據放入緩衝區
l 開中斷
l 發送發送信號量
程序:
/**********************************************************
* 名 稱: UART0_Exception
* 功 能: 串口接收中斷
* 入口參數: 無
* 出口參數: 無
**********************************************************/
void UART0_Exception(void)
{
uint8 i;
uint32 data;
OS_ENTER_CRITICAL();
data = U0IIR; //清除中斷表示寄存器標誌
VICVectAddr = 0; //清除中斷
for(i=0; i<8; i++)
{
rcv_buf[i] = U0RBR; // 讀取FIFO的數據
}
OS_EXIT_CRITICAL();
OSSemPost(Sem_SendFlag); //發送發送信號量
}
6.發送任務流程
l 等待發送信號量
l 發送數據
l 發送開始信號量
程序:
/**********************************************************
** Task2(發送任務)
**********************************************************/
void Task2 (void *pdata)
{
uint8 i,err;
pdata = pdata;
while(1)
{
OSSemPend(Sem_SendFlag,0,&err); //等待發送信號量
for(i = 0;i < 8; i++)
UART0_SendByte(rcv_buf[i]); //經過輪訓方式來發送串口數據
OSSemPost(Sem_StartFlag); //發送開始信號量
}
}
發送數據函數:
/**********************************************************
* 名 稱: UART0_SendByte
* 功 能: 向串口發送字節數據,並等待發送完畢。
* 入口參數: data 要發送的數據
* 出口參數: 無
**********************************************************/
void UART0_SendByte(uint8 data)
{
U0THR = data;
while(0 == (U0LSR & 0x40));
}
u 用消息隊列接收數據的方式
1. 系統有那些任務組成
1>啓動任務
2>接收任務(中調用一個接受處理函數)
3>接收中斷服務例程
4>發送任務
2. 各任務之間的關係
3.啓動任務流程:
l 定義各類通訊工具(例如:信號量)
l 系統硬件初始化
l 初始化UART0
l 建立各個任務
l 建立各類通訊工具
l 刪除本身
程序:
/********************************************************************
Task0(啓動任務)
********************************************************************/
void Task0 (void *pdata)
{
pdata = pdata;
TargetInit(); //硬件初始化
UART0_Init(115200); //初始化串口
Sem_SendFlag = OSSemCreate(0); //建立發送信號量
Sem_StartFlag = OSSemCreate(1); //建立開始信號量
ReMsg_Qeue = OSQCreate(&MsgGrp_Buf[0],10); //建立消息隊列
OSTaskCreate(Task1,(void *)0, &TaskStk1[TaskStkLengh - 1],4); //建立接收任務
OSTaskCreate(Task2,(void *)0, &TaskStk2[TaskStkLengh - 1],5); //建立發送任務
OSTaskDel(OS_PRIO_SELF); //刪除本身
}
4.接收任務流程
l 等待開始信號量
l 處理接收數據
l 發送發送信號量
程序:
/********************************************************************
** Task1(接收任務)
********************************************************************/
void Task1 (void *pdata)
{
uint8 err;
pdata = pdata;
while(1)
{
OSSemPend(Sem_StartFlag,0,&err); //等待開始信號量
UART0_RcvData(rcv_buf,2); //接收數據
OSSemPost(Sem_SendFlag); //發送發送信號量
}
}
處理接收數據函數
/********************************************************************
* 名 稱: Rcv_Data
* 功 能: rcv_buf:接收中斷返回後的數據 count :控制中斷次數
* 入口參數: 無
* 出口參數: 無
********************************************************************/
void UART0_RcvData(uint8 *rcv_buf,uint8 count)
{
uint8 i;
uint8 j;
uint32 rcv_data;
uint8 err;
for(j = 0;j < count;j++)
{
//等待消息隊列
rcv_data = (uint32)(uint32 *)OSQPend(ReMsg_Qeue,0,&err);
if(0x11223344 == rcv_data)
{
rcv_data = 0x12345678;
}
//將每條消息分解爲4字節,存入幀緩衝區
for(i = 0;i < 4;i++)
{
rcv_buf[4*j+3-i] = (uint8)(rcv_data&0xff);
rcv_data >>= 8;
}
}
}
5.串口中斷接收流程:
l 關中斷
l 清除串口中斷標誌位
l 清除中斷控制寄存器
l 接收4字節數據拼裝成32爲地址
l 發送消息郵箱
l 開中斷
程序:
/********************************************************************
* 名 稱: UART0_Exception
* 功 能: 串口接收中斷
* 入口參數: 無
* 出口參數: 無
********************************************************************/
void UART0_Exception(void)
{
uint8 i;
uint32 data;
OS_ENTER_CRITICAL();
data = U0IIR; //清除中斷表示寄存器標誌
VICVectAddr = 0; //清除中斷
for(i = 0;i < 4;i++)
{
data = (data << 8) | U0RBR; //將接受四字節數據拼裝成32位地址
}
if(0x00000000 == data)
{
data = 0x11223344; //防止00000000地址不能發送
}
OSQPost(ReMsg_Qeue,(void *)data); //發送該地址到消息郵箱
OS_EXIT_CRITICAL();
}
6.發送任務流程
l 等待發送信號量
l 發送數據
l 發送開始信號量
程序:
/**********************************************************
** Task2(發送任務)
**********************************************************/
void Task2 (void *pdata)
{
uint8 i,err;
pdata = pdata;
while(1)
{
OSSemPend(Sem_SendFlag,0,&err); //等待發送信號量
for(i = 0;i < 8; i++)
UART0_SendByte(rcv_buf[i]); //經過輪訓方式來發送串口數據
OSSemPost(Sem_StartFlag); //發送開始信號量
}
}
發送數據函數:
/**********************************************************
* 名 稱: UART0_SendByte
* 功 能: 向串口發送字節數據,並等待發送完畢。
* 入口參數: data 要發送的數據
* 出口參數: 無
**********************************************************/
void UART0_SendByte(uint8 data)
{
U0THR = data;
while(0 == (U0LSR & 0x40));
}