在雙碳目標下,具有調光功能的LED驅動電源是重要的分支。 DALI通信常用在LED的數字調光控制中,下文將通過C語言與單片機結合,解釋DALI的原理及實現方法。
一、通信原理
1.1 DALI 的物理電平信號定義如下:
9.5~22.5V: 高電平或者DALI 空閑狀態
6.5~9.5V: 未定義
-6.5V~6.5V:定義為低電平
1.2 波特率:1200bps + 10%
1.3 DALI 負載最大短路電流<250mA
當從機發生故障,例如短路時,DALI總線上的電流,需要限制在250mA以下。
1.4 編碼方式
使用曼切斯特編碼,即上升沿為信號1,下降沿為信號0。
1.5 主機發送指令結構
主機發送的包含1個起始位、1個地址位類型位、6個地址位、一個選擇位、8個數據位和兩個停止位。
1.6 從機回復指令結構
從機向主機回復包含1個起始位、8個數據位和兩個停止位。
1.7 前向幀與后向幀時序約束
Te表示半個位的時間,即4.16.67uS;
兩個前向幀時間間隔大于22個Te;
前向幀與后向幀之間時間間隔為7~22個Te;
后向幀與前向幀之間時間間隔大于22個Te;
1.8 主機與從機的握手
主機在發送指令后,在等候響應階段,
如果收到從機發送的”0xFF“,就會認為從機接收成功;
如果在這個階段處于空閑狀態,就會認為從機沒有接收成功;
二、實現方法
2.1 硬件原理圖
下面的硬件主要是將DALI的電平信號,轉為單片機能夠接受的電平,下面那張是微芯公司DALI的參考通信電路。
2.1 從機接收思路及實現
本次從機的接收端,主要使用了一個邊沿檢測中斷和一個定時器中斷。
代碼思路:
1)由于空閑狀態,接收端的電平為高電平,產生起始信號時,需要從產生一個上升沿。 于是,使用了外部下降沿觸發中斷,并關閉邊沿觸發中斷;
2)檢測到第一個下降沿后,定時器定時到0.75個周期,0.75個周期后讀取第一位數據,并修改定時器周期為1個數據位時長。
3)第二次定時結束時讀取第二位數據,依次讀取后面的數據
4)當讀到最后一位數據的時候,也就是LSB后的兩位時,停止定時器,并初始化定時器為0.75個數據周期,然后開啟邊沿觸發中斷。
C語言程序:
1 //配置邊沿觸發及中斷
2 void IO_Change_Init(void){
3
4 //Set the CN2 as the IO state change flag
5 CNEN1bits.CN2IE=1;//Open the IO state interrupt
6 CNPU1bits.CN2PUE=0;//Disable the weak up
7
8 IFS1bits.CNIF=0;//Clear the interrupt flag
9 IPC4bits.CNIP=7;//Configure the interrupt level 7
10 IEC1bits.CNIE=1;//Enable this interrupt
11 }
12 //檢測到第一個下降沿
13 void __attribute__ ((__interrupt__,__no_auto_psv__)) _CNInterrupt(void){
14
15 IFS1bits.CNIF = 0; //Clear the interrupt flag
16
17 //Disable the IO State Interrupt and start the Time
18 T1CONbits.TON = 1;
19 CNEN1bits.CN2IE = 0;
20
21 }
22 //配置定時器初使周期為0.75個數據位時長
23 void Tim1_Init(void){
24 T1CON = 0x0020;
25
26 IEC0bits.T1IE = 1;
27 IPC0bits.T1IP = 7;
28 IFS0bits.T1IF = 0;
29
30 TMR1 = 0;
31 PR1 = 390;
32 T1CONbits.TON = 0;
33 }
34 //在定時器中斷里面讀取數據
35 void __attribute__((__interrupt__,auto_psv,__shadow__)) _T1Interrupt(void)
36 {
37 IFS0bits.T1IF = 0;
38
39 if(LLC_DALI_Rx_Mode == 1)
40 {
41 switch(Timer_Num)
42 {
43 case 0:
44 Timer_Num++;
45 T1CONbits.TON = 0; //關閉定時器
46 PR1 = 520; //設置下一個定時時長為1個周期
47 TMR1 = 0;//初使化定時器初始值
48 T1CONbits.TON = 1;//開啟定時器
49 break;
50 case 1:
51 if(_RB0 == 1 )Address_temp |= (1<<7);
52 Timer_Num++;
53 break;
54 case 2:
55 if(_RB0 == 1 )Address_temp |= (1<<6);
56 Timer_Num++;
57 break;
58 case 3:
59 if(_RB0 == 1 )Address_temp |= (1<<5);
60 Timer_Num++;
61 break;
62 case 4:
63 if(_RB0 == 1 )Address_temp |= (1<<4);
64 Timer_Num++;
65 break;
66 case 5:
67 if(_RB0 == 1 )Address_temp |= (1<<3);
68 Timer_Num++;
69 break;
70 case 6:
71 if(_RB0 == 1 )Address_temp |= (1<<2);
72 Timer_Num++;
73 break;
74 case 7:
75 if(_RB0 == 1 )Address_temp |= (1<<1);
76 Timer_Num++;
77 break;
78 case 8:
79 if(_RB0 == 1 )Address_temp |= (1<<0);
80 Timer_Num++;
81 break;
82 case 9:
83 if(_RB0 == 1 )Command_temp |= (1<<7);
84 Timer_Num++;
85 break;
86 case 10:
87 if(_RB0 == 1 )Command_temp |= (1<<6);
88 Timer_Num++;
89 break;
90 case 11:
91 if(_RB0 == 1 )Command_temp |= (1<<5);
92 Timer_Num++;
93 break;
94 case 12:
95 if(_RB0 == 1 )Command_temp |= (1<<4);
96 Timer_Num++;
97 break;
98 case 13:
99 if(_RB0 == 1 )Command_temp |= (1<<3);
100 Timer_Num++;
101 break;
102 case 14:
103 if(_RB0 == 1 )Command_temp |= (1<<2);
104 Timer_Num++;
105 break;
106 case 15:
107 if(_RB0 == 1 )Command_temp |= (1<<1);
108 Timer_Num++;
109 break;
110 case 16:
111 if(_RB0 == 1 )Command_temp |= (1<<0);
112 Timer_Num++;
113 break;
114 case 17:
115 if(_RB0 == 1 )StopBit_temp |= (1<<1);
116 Timer_Num++;
117 break;
118 case 18:
119 if(_RB0 == 1 )StopBit_temp |= (1<<0);
120 Timer_Num++;
121 break;
122 case 19:
123 T1CONbits.TON = 0;//關閉定時器
124 PR1 = 390;//設置下一個定時器周期為0.75個數據位時長
125 TMR1 = 0;//定時器計數初始值置0
126 CNEN1bits.CN2IE = 1;//開啟邊沿檢測中斷
127 //數據獲取,這里還可以添加數據包檢驗程序
128 Command = Command_temp;
129 Address = Address_temp;
130 StopBit = StopBit_temp;
131
132 Command_temp = 0;
133 Address_temp = 0;
134 StopBit_temp = 0;
135 Timer_Num = 0;
136 break;
137 }
138 }
139
140
141
2.2 從機思路及實現
從機的回復相對較簡單,只需要在每半個數據位修改輸出引腳的電平。 分別發送1個起始位、8個數據位和兩個停止位。
代碼思路:
1)接收數據完成并定時等待8Te
2)發送一個引腳低電平,并設置下一個定時周期為Te,定時器初使值為0,并開啟定時器;
3)在后面的定時器中斷里面,發送起始位;
4)在后面的定時器中斷里面,發送數據位;
5)在后面的定時器中斷里面,發送停止位;
6)初使化發送數據計數變量,初始化定時器計數值為零,關閉定時器;
7)開啟邊沿觸發中斷;
C語言程序實現
1 if(LLC_DALI_Tx_Mode == 1){
2 switch(Timer_Num)
3 {
4 case 0:
5 //Send the Start Bit
6 _RF3 = 0;
7 T1CONbits.TON = 0;
8 PR1 = 260;
9 TMR1 = 0;
10 T1CONbits.TON = 1;
11 Timer_Num++;
12 break;
13 case 1:
14 _RF3 = 1;
15 Timer_Num++;
16 break;
17 case 2:
18 //Send the Data Bits
19 _RF3 = ((Transfer_Data & 0x80)>0)?0:1;
20 Timer_Num++;
21 break;
22 case 3:
23 _RF3 = ((Transfer_Data & 0x80)>0)?1:0;
24 Timer_Num++;
25 break;
26 case 4:
27 _RF3 = ((Transfer_Data & 0x40)>0)?0:1;
28 Timer_Num++;
29 break;
30 case 5:
31 _RF3 = ((Transfer_Data & 0x40)>0)?1:0;
32 Timer_Num++;
33 break;
34 case 6:
35 _RF3 = ((Transfer_Data & 0x20)>0)?0:1;
36 Timer_Num++;
37 break;
38 case 7:
39 _RF3 = ((Transfer_Data & 0x20)>0)?1:0;
40 Timer_Num++;
41 break;
42 case 8:
43 _RF3 = ((Transfer_Data & 0x10)>0)?0:1;
44 Timer_Num++;
45 break;
46 case 9:
47 _RF3 = ((Transfer_Data & 0x10)>0)?1:0;
48 Timer_Num++;
49 break;
50 case 10:
51 _RF3 = ((Transfer_Data & 0x08)>0)?0:1;
52 Timer_Num++;
53 break;
54 case 11:
55 _RF3 = ((Transfer_Data & 0x08)>0)?1:0;
56 Timer_Num++;
57 break;
58 case 12:
59 _RF3 = ((Transfer_Data & 0x04)>0)?0:1;
60 Timer_Num++;
61 break;
62 case 13:
63 _RF3 = ((Transfer_Data & 0x04)>0)?1:0;
64 Timer_Num++;
65 break;
66 case 14:
67 _RF3 = ((Transfer_Data & 0x02)>0)?0:1;
68 Timer_Num++;
69 break;
70 case 15:
71 _RF3 = ((Transfer_Data & 0x02)>0)?1:0;
72 Timer_Num++;
73 break;
74 case 16:
75 _RF3 = ((Transfer_Data & 0x01)>0)?0:1;
76 Timer_Num++;
77 break;
78 case 17:
79 _RF3 = ((Transfer_Data & 0x01)>0)?1:0;
80 Timer_Num++;
81 break;
82 case 18:
83 //Send the stop bit;
84 T1CONbits.TON = 0;
85 TMR1 = 0;
86 PR1 = 260<<2;
87 T1CONbits.TON = 1;
88 _RF3 = 1;
89 Timer_Num++;
90 break;
91 case 19:
92 T1CONbits.TON = 0; //關閉定時器
93 CNEN1bits.CN2IE = 1;//開啟邊沿檢測中斷
94 TMR1 = 0; //定時器初始值置0
95 PR1 = 260; //定時器周期設置為Te
96 Timer_Num = 0;//初使定時器數據位計數
97 break;
98 }
99 }
三、測試結果
3.1 從機發送測試
從機發送數據100,對應二進制為0b0110 0100,實際發送波形見下圖:
實際發送數據為0b01100100,發送正常。
3.2 主機發送從機識別測試
主機通過上位機發送調光指令為239,從機在線調試識別出來的數據為239。 接收正常。
四、小結
從機的接收程序,定時器的定時步長先是1.5個Te,然后是2個Te;
從機的發送程序,定時器的定時步長為1個Te;
從機的接收程序,邊沿觸發只觸發依次就關閉了。
從機的發送程序,發送完畢開啟邊沿觸發。
在定時器中斷里面,修改下一個定時時長,理論上可以做到每一個定時周期都不一樣,這思維可以用于實現更加復雜的功能。
注意:
1)以上代碼發送和接收是獨立的,沒有遵循通信的時序。 1.7節里面有具體時序要求,根據時序稍做修改就可以啦。
2)本次代碼,沒有考慮到時序有10%的誤差。 有待改善。
-
單片機
+關注
關注
6035文章
44554瀏覽量
634637 -
通信
+關注
關注
18文章
6024瀏覽量
135950 -
C語言
+關注
關注
180文章
7604瀏覽量
136692 -
DALI
+關注
關注
4文章
67瀏覽量
20760 -
驅動電源
+關注
關注
22文章
406瀏覽量
42348
發布評論請先 登錄
相關推薦
評論