最近在學習DALI調光相關知識并下載了Microchip提供的基于ATMega88PA的軟件工程及硬件設計參考方案。寫這些文章的目的就是把自己對知識的理解作一些梳理。
芯片廠果然專業,考慮得相當周到,為了芯片銷量連軟件和硬件方案全都提供了。芯片廠關于DALI1.0實現的軟硬件參考鏈接地址如下:Salesforcehttps://microchip.my.salesforce.com/sfc/p/#o0000000KAkK/a/3l000001Iuci/0yucLkmht5A3PuLOo2MVtWQlQA1Ca1FgNK1KkCKafeg
Firmware部分是DALI1.0實現的源代碼,包括底層驅動及上層應用,使用AVR系列專用開發IDE:Microchip Studio。
這一篇主要理解下如何解碼主機發送來的BIT信號,重點是dali_bit.c文件,關于DALI主機發送數據的時序可以參考這篇博文:DALI通信及C語言實現 - 斑鳩,一生。 - 博客園 (cnblogs.com)
主機和從機通信使用半雙工,波特率為1200,參考示例中從機使用GPIO的邊沿跳變(上升沿和下降沿都觸發)結合定時器來解碼主機發送來的信號,具體配置如下:
1、GPIO為雙邊沿跳變中斷,中斷處理完成后定時器計數清零;
2、定時器溢出時間為32微秒。1200BIT/S的波特率傳輸一個BIT的時間約833微秒,半個BIT,也就是一個TE為416微秒,所以定時器約溢出13次左右,曼切斯特編碼是在傳輸一半BIT時間的時候產生邊沿跳變,傳輸1就是上升沿,0是下降沿;
定時器和GPIO配置完成后,就開始等待主機發送前向幀,從機解析數據的思路如下:
1、總線在沒有數據發送時,從機的接收引腳(也就是雙邊沿跳變引腳)一直是高電平;
2、當主機發送起始號時,從機的接收引腳會檢測到一個下降沿,當經過約半個BIT的時間后如果檢測到一個上升沿,則證明是有效的起始信號,進入準備接收信號階段;
3、以后每隔兩個TE就讀取一次實際傳輸的BIT,如圖1所示,同時不停檢測是否有連續4個TE,如果數據接收完成后出現4個連續TE,證明是接收到了停止位,本次數據接收成功,如果數據沒有接收完成就出現4個TE說明傳輸出錯;
圖1:DALI時序
4、正確檢測到停止位后,將接收到的地址和數據遞交給上一層進行幀邏輯處理。
再來看源代碼文件dali_bit.c,DALI從機的接收引腳邊沿跳變中斷處理程序dali_bit_pcint_interrupt函數如下:
/*** \brief External pin interrupt handler** This is the handler for external pin interrupt*/
void dali_bit_pcint_interrupt(void)
{static uint8_t bit_index;uint8_t bit_index_temp;uint8_t dali_bit_rx;bit_index_temp = bit_index;pin_level = DALI_INPORT & (1 << DALI_INPUT);if (status_receive == 0) {if (pin_level == LOW) {/* dali bus falling edge indicates start bit */bit_index_temp = 0;status_receive = BIT_START;dali_rec_addr = 0;dali_rec_data = 0;}} else if (status_receive == BIT_START) {/* dali pin must be high after the second INT0 edge */if ((level_time > MIN_TE_CNT) && (level_time < MAX_TE_CNT)) { /* get the start bit and get ready for 16 bit data. */status_receive = BIT_0;bit_index_temp += 1;} else {/* Start bit error */status_receive = 0;}} else if (status_receive < BIT_STOP1) {if (level_time > MIN_2TE_CNT) {/* Long level (2xTe) is detected */bit_index_temp += 2;} else if ((level_time > MIN_TE_CNT) &&(level_time < MAX_TE_CNT)) {/* Short level (1xTe) is detected */bit_index_temp += 1;} else {status_receive = 0;}if (bit_index_temp >= 34) { /* If the last Te is low (dali bit 0), a rising edge is* detected before stop bit */ status_receive = BIT_STOP1;}/* Decode dali bit at every second Te bit */if (bit_index_temp & 0x01) {/* shift out the lowest bit to get dali bit */ dali_bit_rx = bit_index_temp >> 1;if (dali_bit_rx <= BIT_7) { //the current bit number, maximum 8 for the address byte/* get the address byte */dali_rec_addr <<= 1;if (pin_level) {dali_rec_addr |= 0x01;}} else {/* get the data byte */dali_rec_data <<= 1;if (pin_level) {dali_rec_data |= 0x01;if (dali_bit_rx == BIT_15) { /* if the last Te is high (dali bit 1), a Te * high period is added before stop bit */ status_receive = BIT_STOP2;}}}}} else {status_receive = 0;}dali_slave_set_addr_to_service(dali_rec_addr);dali_slave_set_data_to_service(dali_rec_data);bit_index = bit_index_temp;TCNT0 = 0;level_time = 0;
}
代碼分析:
1、由于在接收主機發送的數據時會多次進入邊沿跳變中斷,必須使用靜態變量bit_index并結合變量bit_index_temp保存當前已獲取的BIT索引,確切的說應該是TE索引,pin_level用于獲取從機輸入引腳的電平狀態,變量status_receive表示當前接收的是起始信號、數據還是停止位;
2、當主機發送起始信號時會觸發第一個下降沿,第16行判斷如果DALI輸入引腳是低電平,則初始化一些變量,并將接收狀態轉為BIT_START,也就是起始信號;
3、起始信號下降沿觸發后,第二次邊沿跳變觸發時必須是上升沿,且觸發時間必須是在一個TE周期內才被認為是正常的起始信號,如圖2所示,Start部分先有一個下降沿然后一個上升沿才開始發送MSB。變量level_time會在定時器0的溢出中斷處理程序中累加,溢出時間約為32微秒(8MHz頻率不分頻,最大計數256,溢出時間 = 256/8000000),約13個溢出周期后就是一個TE的時間:416微秒左右,當然這個時間不可能剛好,有正負5個溢出周期左右誤差范圍,所以第25行使用了if ((level_time > MIN_TE_CNT) && (level_time < MAX_TE_CNT))。此時變量bit_index_temp被設置為1,同時status_receive設置為準備接收第一個數據BIT,即BIT_0;
圖2:起始信號,先一個下降沿然后一個上升沿
4、正確接收到起始信號后,準備接收地址和數據字節。代碼第34行判斷只要不是接收停止位就保存當前接收到的BIT。代碼第35行到第41行判斷:如果是經過兩個TE時間才產生邊沿跳變中斷的話則將bit_index_temp加2,否則加1。當發送不同BIT值時就會出現兩個TE后才會出現邊沿跳變的情況,例如:前面的比特是0,后面接著是1就會出現連續兩個TE的低電平然后產生上升沿跳變,如圖3所示;
圖3:BIT值改變時會有兩個連續TE的高或低電平
5、代碼第46行判斷當bit_index_temp>=34時就將status_receive切換到接收停止位1,因為包括起始BIT、地址字節和數據字節共17個BIT,也就是34個TE,當接收到最后一個BIT并且是低電平,這里要注意:只有當最后一個BIT是低電平時,bit_index_temp才會等于34,高電平應該是33。最后一個BIT是0且它后面出現上升沿則證明接收到的是停止位1;
6、代碼第53行到76行是解碼接收到的BIT值并賦值給地址變量dali_rec_addr和數據變量dali_rec_data。首先第53行使用了if (bit_index_temp & 0x01),bit_index_temp必須為奇數且大于1時if條件才為真。那我們來看一下當接收地址和數據的BIT時,bit_index_temp是不是奇數并且對應的BIT是否正確,如圖4所示,當起始位的上升沿產生后bit_index_temp被設置為1,然后從35行到41行根據邊沿跳變觸發時間將bit_index_temp加2或者加1,我們可以看到bit_index_temp確實是在每次為奇數時才讀取接收到的BIT,為節省空間在圖中我把bit_index_temp改為了bit_idx。
第1個紅點位置:也就是有效起始信號上升沿中斷時bit_idx = 1
第2個紅點位置:由于經過兩個TE才產生下降沿中斷,bit_idx執行了加2,變成3,此時if (bit_index_temp & 0x01)條件為真,讀取數據剛好是BIT值0,和曼切斯特編碼含義一樣
第3個紅點位置:經過了兩個TE產生了上升沿中斷,bit_idx執行了加2,變成5,此時if (bit_index_temp & 0x01)條件為真,讀取數據剛好是BIT值1,和曼切斯特編碼含義一樣
第4個紅點位置:經過了1個TE產生了下降沿中斷,bit_idx執行了加1,變成6,此時if (bit_index_temp & 0x01)條件為假,所以沒有讀取數據
第5個紅點位置:經過了1個TE產生了上升沿中斷,bit_idx執行了加1,變成7,此時if (bit_index_temp & 0x01)條件為真,讀取數據剛好是BIT值1,和曼切斯特編碼含義一樣
第6個紅點位置:經過了兩個TE產生了下降沿中斷,bit_idx執行了加2,變成9,此時if (bit_index_temp & 0x01)條件為真,讀取數據剛好是BIT值0,和曼切斯特編碼含義一樣
后面的以此類推,可見確實是bit_index_temp變量為奇數且非起始位時才讀取并保存BIT值。
圖4:bit_index_temp為奇數時讀取數據
7、dali_bit_rx用于判斷當前接收到的是地址字節還是數據字節并計算出BIT號,BIT_0到BIT_15,對應值:1-16,小于等于BIT_7時是地址字節,否則就是數據字節。前面說過奇數位獲取一次數據,每隔兩個值計算一下當前的BIT序號,所以dali_bit_rx = bit_index_temp >> 1。代碼第60行和第66行根據當前引腳的電平來保存數據,在第68行判斷如果是最后一個數據BIT了,則設置接收狀態為停止位2。
8、最后幾行代碼主要將定時器0的計數值清零并且將定時器0溢出次數level_time清零。用局部靜態變量保存當前的TE索引號,以便下次產生邊沿跳變中斷時賦值給bit_index_temp。
DALI1.0的比特解碼部分就介紹到這里,后續繼續整理其他內容,希望和大家一起學習交流。