Commit 7241451596ad427642eee4383443a263fa8919fb

Authored by qqqlab
Committed by GitHub
1 parent 1e6536ea

Delete qqqDali.cpp

Showing 1 changed file with 0 additions and 436 deletions
qqqDali.cpp deleted
1 -/*###########################################################################  
2 - qqqDali.cpp - copyright qqqlab.com / github.com/qqqlab  
3 -  
4 - This program is free software: you can redistribute it and/or modify  
5 - it under the terms of the GNU General Public License as published by  
6 - the Free Software Foundation, either version 3 of the License, or  
7 - (at your option) any later version.  
8 -  
9 - This program is distributed in the hope that it will be useful,  
10 - but WITHOUT ANY WARRANTY; without even the implied warranty of  
11 - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  
12 - GNU General Public License for more details.  
13 -  
14 - You should have received a copy of the GNU General Public License  
15 - along with this program. If not, see <http://www.gnu.org/licenses/>.  
16 -  
17 -----------------------------------------------------------------------------  
18 -Changelog:  
19 -2020-11-10 Split off hardware specific code into separate class  
20 -2020-11-08 Created & tested on ATMega328 @ 8Mhz  
21 -###########################################################################*/  
22 -#include "qqqDali.h"  
23 -  
24 -//###########################################################################  
25 -// Helpers  
26 -//###########################################################################  
27 -#define DALI_TOL 25 //percentage tolerance on timing. DALI specs call for 10%, but use higher value to allow for implementation micros() jitter. NOTE: Max value is 50% to differentiate between TE and 2TE.  
28 -#define DALI_TE ((1000000+(DALI_BAUD))/(2*(DALI_BAUD))) //417us  
29 -#define DALI_TE_MIN ((100-DALI_TOL)*DALI_TE)/100  
30 -#define DALI_TE_MAX ((100+DALI_TOL)*DALI_TE)/100  
31 -#define DALI_IS_TE(x) ((DALI_TE_MIN)<=(x) && (x)<=(DALI_TE_MAX))  
32 -#define DALI_IS_2TE(x) ((2*(DALI_TE_MIN))<=(x) && (x)<=(2*(DALI_TE_MAX)))  
33 -  
34 -//###########################################################################  
35 -// Transmitter ISR  
36 -//###########################################################################  
37 -//called by derived class every Te period (417us)  
38 -void Dali::ISR_timer() {  
39 - if(this->bus_idle_te_cnt<0xff) this->bus_idle_te_cnt++;  
40 -  
41 - //send starbit, message bytes, 2 stop bits.  
42 - switch(this->tx_state) {  
43 - case TX_IDLE:  
44 - break;  
45 - case TX_START:  
46 - //wait for timeslot, then send start bit  
47 - if(this->bus_idle_te_cnt >= 22) {  
48 - this->HAL_set_bus_low();  
49 - this->tx_bus_low=1;  
50 - this->tx_state = TX_START_X;  
51 - }  
52 - break;  
53 - case TX_START_X:  
54 - this->HAL_set_bus_high();  
55 - this->tx_bus_low=0;  
56 - this->tx_pos=0;  
57 - this->tx_state = TX_BIT;  
58 - break;  
59 - case TX_BIT:  
60 - if(this->tx_msg[this->tx_pos>>3] & 1<<(7-(this->tx_pos&0x7))) {this->HAL_set_bus_low();this->tx_bus_low=1;} else {this->HAL_set_bus_high();this->tx_bus_low=0;}  
61 - this->tx_state = TX_BIT_X;  
62 - break;  
63 - case TX_BIT_X:  
64 - if(this->tx_msg[this->tx_pos>>3] & 1<<(7-(this->tx_pos&0x7))) {this->HAL_set_bus_high();this->tx_bus_low=0;} else {this->HAL_set_bus_low();this->tx_bus_low=1;}  
65 - this->tx_pos++;  
66 - if(this->tx_pos < this->tx_len) {this->tx_state = TX_BIT;} else {this->tx_state = TX_STOP1;}  
67 - break;  
68 - case TX_STOP1:  
69 - this->HAL_set_bus_high();  
70 - this->tx_bus_low=0;  
71 - this->tx_state = TX_STOP1_X;  
72 - break;  
73 - case TX_STOP1_X:  
74 - this->tx_state = TX_STOP2;  
75 - break;  
76 - case TX_STOP2:  
77 - this->tx_state = TX_STOP2_X;  
78 - break;  
79 - case TX_STOP2_X:  
80 - this->tx_state = TX_STOP3;  
81 - break;  
82 - case TX_STOP3:  
83 - this->bus_idle_te_cnt=0;  
84 - this->tx_state = TX_IDLE;  
85 - this->rx_state = RX_IDLE;  
86 - this->rx_len = 0;  
87 - break;  
88 - }  
89 -  
90 - //handle receiver stop bits  
91 - if(this->rx_state == RX_BIT && this->bus_idle_te_cnt>4) {  
92 - this->rx_state = RX_IDLE;  
93 - //received two stop bits, got message in rx_msg + rx_halfbitlen  
94 - uint8_t bitlen = (this->rx_halfbitlen+1)>>1;  
95 - if((bitlen & 0x7) == 0) {  
96 - this->rx_len = bitlen>>3;  
97 - if(this->EventHandlerReceivedData) this->EventHandlerReceivedData(this, (uint8_t*)this->rx_msg, this->rx_len);  
98 - }else{  
99 - //invalid bitlen  
100 - //TODO handle this  
101 - }  
102 - }  
103 -}  
104 -  
105 -//###########################################################################  
106 -// Receiver ISR  
107 -//###########################################################################  
108 -//called by derived class on bus state change  
109 -void Dali::ISR_pinchange() {  
110 - uint32_t ts = this->HAL_micros(); //get timestamp of change  
111 - this->bus_idle_te_cnt=0; //reset idle counter  
112 - uint8_t bus_low = this->HAL_is_bus_low();  
113 -  
114 - //exit if transmitting  
115 - if(this->tx_state != TX_IDLE) {  
116 - //check tx collision  
117 - if(bus_low && !this->tx_bus_low) {  
118 - this->tx_state = TX_IDLE; //stop transmitter  
119 - this->tx_collision = 1; //mark collision  
120 - }  
121 - return;  
122 - }  
123 -  
124 - //no bus change, ignore  
125 - if(bus_low == this->rx_last_bus_low) return;  
126 -  
127 - //store values for next loop  
128 - uint32_t dt = ts - this->rx_last_change_ts;  
129 - this->rx_last_change_ts = ts;  
130 - this->rx_last_bus_low = bus_low;  
131 -  
132 - switch(this->rx_state) {  
133 - case RX_IDLE:  
134 - if(bus_low) {  
135 - this->rx_state = RX_START;  
136 - }  
137 - break;  
138 - case RX_START:  
139 - if(bus_low || !DALI_IS_TE(dt)) {  
140 - this->rx_state = RX_IDLE;  
141 - }else{  
142 - this->rx_halfbitlen=-1;  
143 - for(uint8_t i=0;i<7;i++) this->rx_msg[0]=0;  
144 - this->rx_state = RX_BIT;  
145 - }  
146 - break;  
147 - case RX_BIT:  
148 - if(DALI_IS_TE(dt)) {  
149 - //got a single Te pulse  
150 - this->push_halfbit(bus_low);  
151 - } else if(DALI_IS_2TE(dt)) {  
152 - //got a double Te pulse  
153 - this->push_halfbit(bus_low);  
154 - this->push_halfbit(bus_low);  
155 - } else {  
156 - //got something else -> no good  
157 - this->rx_state = RX_IDLE;  
158 - //TODO rx error  
159 - return;  
160 - }  
161 - break;  
162 - }  
163 -}  
164 -  
165 -void Dali::push_halfbit(uint8_t bit) {  
166 - bit = (~bit)&1;  
167 - if((this->rx_halfbitlen & 1)==0) {  
168 - uint8_t i = this->rx_halfbitlen>>4;  
169 - if(i<3) {  
170 - this->rx_msg[i] = (this->rx_msg[i]<<1) | bit;  
171 - }  
172 - }  
173 - this->rx_halfbitlen++;  
174 -}  
175 -  
176 -  
177 -//###########################################################################  
178 -// Dali Class  
179 -//###########################################################################  
180 -  
181 -//non blocking send - check tx_state for completion, check tx_collision for collision errors  
182 -uint8_t Dali::send(uint8_t* tx_msg, uint8_t tx_len_bytes) {  
183 - if(tx_len_bytes>3) return -(DALI_RESULT_DATA_TOO_LONG);  
184 - if(this->tx_state != TX_IDLE) return -(DALI_RESULT_TIMEOUT);  
185 - for(uint8_t i=0;i<tx_len_bytes;i++) this->tx_msg[i]=tx_msg[i];  
186 - this->tx_len = tx_len_bytes<<3;  
187 - this->tx_collision=0;  
188 - this->tx_state = TX_START; //start transmission  
189 - return 0;  
190 -}  
191 -  
192 -//blocking send - wait until successful send or timeout  
193 -uint8_t Dali::sendwait(uint8_t* tx_msg, uint8_t tx_len_bytes, uint32_t timeout_us) {  
194 - if(tx_len_bytes>3) return -(DALI_RESULT_DATA_TOO_LONG);  
195 - uint32_t ts = HAL_micros();  
196 -  
197 - while(1) {  
198 - //wait for idle  
199 - while(this->tx_state != TX_IDLE) {  
200 - if(HAL_micros() - ts > timeout_us) return -(DALI_RESULT_TIMEOUT);  
201 - }  
202 - //start transmit  
203 - uint8_t rv = this->send(tx_msg,tx_len_bytes);  
204 - if(rv) return rv;  
205 - //wait for completion  
206 - while(this->tx_state != TX_IDLE) {  
207 - if(HAL_micros() - ts > timeout_us) return -(DALI_RESULT_TX_TIMEOUT);  
208 - }  
209 - //check for collisions  
210 - if(this->tx_collision==0) return 0;  
211 - }  
212 - return -(DALI_RESULT_TIMEOUT);  
213 -}  
214 -  
215 -//blocking transmit 2 byte command, receive 1 byte reply (if reply was sent)  
216 -int16_t Dali::tx(uint8_t cmd0, uint8_t cmd1, uint32_t timeout_us) {  
217 - uint8_t tx[2];  
218 - tx[0] = cmd0;  
219 - tx[1] = cmd1;  
220 - int16_t rv = this->sendwait(tx, 2, timeout_us);  
221 - this->rx_halfbitlen = 0;  
222 - if(rv) return -rv;;  
223 -  
224 - //wait up to 10 ms for start of reply  
225 - uint32_t ts = HAL_micros();  
226 - while(this->rx_state == RX_IDLE) {  
227 - if(HAL_micros() - ts > 10000) return DALI_RESULT_NO_REPLY;  
228 - }  
229 - //wait up to 15 ms for completion of reply  
230 - ts = HAL_micros();  
231 - while(this->rx_len == 0) {  
232 - if(HAL_micros() - ts > 15000) return DALI_RESULT_NO_REPLY;  
233 - }  
234 - if(this->rx_len > 1) return DALI_RESULT_INVALID_REPLY;  
235 - return this->rx_msg[0];  
236 -}  
237 -  
238 -//=================================================================  
239 -// High level  
240 -//=================================================================  
241 -//check YAAAAAA: 0000 0000 to 0011 1111 adr, 0100 0000 to 0100 1111 group, x111 1111 broadcast  
242 -uint8_t Dali::check_yaaaaaa(uint8_t yaaaaaa) {  
243 - return (yaaaaaa<=0b01001111 || yaaaaaa==0b01111111 || yaaaaaa==0b11111111);  
244 -}  
245 -  
246 -void Dali::set_level(uint8_t level, uint8_t adr) {  
247 - if(this->check_yaaaaaa(adr)) this->tx(adr<<1,level);  
248 -}  
249 -  
250 -int16_t Dali::cmd(uint16_t cmd, uint8_t arg) {  
251 - //Serial.print("dali_cmd[");Serial.print(cmd,HEX);Serial.print(",");Serial.print(arg,HEX);Serial.print(")");  
252 - uint8_t cmd0,cmd1;  
253 - if(cmd&0x0100) {  
254 - //special commands: MUST NOT have YAAAAAAX pattern for cmd  
255 - //Serial.print(" SPC");  
256 - if(!this->check_yaaaaaa(cmd>>1)) {  
257 - cmd0 = cmd;  
258 - cmd1 = arg;  
259 - }else{  
260 - return DALI_RESULT_INVALID_CMD;  
261 - }  
262 - }else{  
263 - //regular commands: MUST have YAAAAAA pattern for arg  
264 -  
265 - //Serial.print(" REG");  
266 - if(this->check_yaaaaaa(arg)) {  
267 - cmd0 = arg<<1|1;  
268 - cmd1 = cmd;  
269 - }else{  
270 - return DALI_RESULT_INVALID_CMD;  
271 - }  
272 - }  
273 - if(cmd&0x0200) {  
274 - //Serial.print(" REPEAT");  
275 - this->tx(cmd0, cmd1);  
276 - }  
277 - int16_t rv = this->tx(cmd0, cmd1);  
278 - //Serial.print(" rv=");Serial.println(rv);  
279 - return rv;  
280 -}  
281 -  
282 -  
283 -uint8_t Dali::set_operating_mode(uint8_t v, uint8_t adr) {  
284 - return set_value(DALI_SET_OPERATING_MODE, DALI_QUERY_OPERATING_MODE, v, adr);  
285 -}  
286 -  
287 -uint8_t Dali::set_max_level(uint8_t v, uint8_t adr) {  
288 - return set_value(DALI_SET_MAX_LEVEL, DALI_QUERY_MAX_LEVEL, v, adr);  
289 -}  
290 -  
291 -uint8_t Dali::set_min_level(uint8_t v, uint8_t adr) {  
292 - return set_value(DALI_SET_MIN_LEVEL, DALI_QUERY_MIN_LEVEL, v, adr);  
293 -}  
294 -  
295 -  
296 -uint8_t Dali::set_system_failure_level(uint8_t v, uint8_t adr) {  
297 - return set_value(DALI_SET_SYSTEM_FAILURE_LEVEL, DALI_QUERY_SYSTEM_FAILURE_LEVEL, v, adr);  
298 -}  
299 -  
300 -uint8_t Dali::set_power_on_level(uint8_t v, uint8_t adr) {  
301 - return set_value(DALI_SET_POWER_ON_LEVEL, DALI_QUERY_POWER_ON_LEVEL, v, adr);  
302 -}  
303 -  
304 -//set a parameter value, returns 0 on success  
305 -uint8_t Dali::set_value(uint16_t setcmd, uint16_t getcmd, uint8_t v, uint8_t adr) {  
306 - int16_t current_v = this->cmd(getcmd,adr); //get current parameter value  
307 - if(current_v == v) return 0;  
308 - this->cmd(DALI_DATA_TRANSFER_REGISTER0,v); //store value in DTR  
309 - int16_t dtr = this->cmd(DALI_QUERY_CONTENT_DTR0,adr); //get DTR value  
310 - if(dtr != v) return 1;  
311 - this->cmd(setcmd,adr); //set parameter value = DTR  
312 - current_v = this->cmd(getcmd,adr); //get current parameter value  
313 - if(current_v != v) return 2;  
314 - return 0;  
315 -}  
316 -  
317 -  
318 -//======================================================================  
319 -// Commissioning short addresses  
320 -//======================================================================  
321 -  
322 -//Sets the slave Note 1 to the INITIALISE status for15 minutes.  
323 -//Commands 259 to 270 are enabled only for a slave in this  
324 -//status.  
325 -  
326 -//set search address  
327 -void Dali::set_searchaddr(uint32_t adr) {  
328 - this->cmd(DALI_SEARCHADDRH,adr>>16);  
329 - this->cmd(DALI_SEARCHADDRM,adr>>8);  
330 - this->cmd(DALI_SEARCHADDRL,adr);  
331 -}  
332 -  
333 -//set search address, but set only changed bytes (takes less time)  
334 -void Dali::set_searchaddr_diff(uint32_t adr_new,uint32_t adr_current) {  
335 - if( (uint8_t)(adr_new>>16) != (uint8_t)(adr_current>>16) ) this->cmd(DALI_SEARCHADDRH,adr_new>>16);  
336 - if( (uint8_t)(adr_new>>8) != (uint8_t)(adr_current>>8) ) this->cmd(DALI_SEARCHADDRM,adr_new>>8);  
337 - if( (uint8_t)(adr_new) != (uint8_t)(adr_current) ) this->cmd(DALI_SEARCHADDRL,adr_new);  
338 -}  
339 -  
340 -//Is the random address smaller or equal to the search address?  
341 -uint8_t Dali::compare() {  
342 - return (0xff == this->cmd(DALI_COMPARE,0x00));  
343 -}  
344 -  
345 -//The slave shall store the received 6-bit address (AAAAAA) as a short address if it is selected.  
346 -void Dali::program_short_address(uint8_t shortadr) {  
347 - this->cmd(DALI_PROGRAM_SHORT_ADDRESS, (shortadr << 1) | 0x01);  
348 -}  
349 -  
350 -//What is the short address of the slave being selected?  
351 -uint8_t Dali::query_short_address() {  
352 - return this->cmd(DALI_QUERY_SHORT_ADDRESS, 0x00) >> 1;  
353 -}  
354 -  
355 -//find addr with binary search  
356 -uint32_t Dali::find_addr() {  
357 - uint32_t adr = 0x800000;  
358 - uint32_t addsub = 0x400000;  
359 - uint32_t adr_last = adr;  
360 - this->set_searchaddr(adr);  
361 -  
362 - while(addsub) {  
363 - this->set_searchaddr_diff(adr,adr_last);  
364 - adr_last = adr;  
365 - uint8_t cmp = this->compare();  
366 - //Serial.print("cmp ");  
367 - //Serial.print(adr,HEX);  
368 - //Serial.print(" = ");  
369 - //Serial.println(cmp);  
370 - if(cmp) adr-=addsub; else adr+=addsub;  
371 - addsub >>= 1;  
372 - }  
373 - this->set_searchaddr_diff(adr,adr_last);  
374 - adr_last = adr;  
375 - if(!this->compare()) {  
376 - adr++;  
377 - this->set_searchaddr_diff(adr,adr_last);  
378 - }  
379 - return adr;  
380 -}  
381 -  
382 -//init_arg=11111111 : all without short address  
383 -//init_arg=00000000 : all  
384 -//init_arg=0AAAAAA1 : only for this shortadr  
385 -//returns number of new short addresses assigned  
386 -uint8_t Dali::commission(uint8_t init_arg) {  
387 - uint8_t cnt = 0;  
388 - uint8_t arr[64];  
389 - uint8_t sa;  
390 - for(sa=0; sa<64; sa++) arr[sa]=0;  
391 -  
392 - //start commissioning  
393 - this->cmd(DALI_RESET,0x00);  
394 - this->cmd(DALI_INITIALISE,init_arg);  
395 - this->cmd(DALI_RANDOMISE,0x00);  
396 -  
397 - //find used short addresses (run always, seems to work better than without...)  
398 - for(sa = 0; sa<64; sa++) {  
399 - int16_t rv = this->cmd(DALI_QUERY_STATUS,sa);  
400 - if(rv!=DALI_RESULT_NO_REPLY) {  
401 - if(init_arg!=0b00000000) arr[sa]=1; //remove address from list if not in "all" mode  
402 - }  
403 - }  
404 -  
405 - //find random addresses and assign unused short addresses  
406 - while(1) {  
407 - uint32_t adr = this->find_addr();  
408 - if(adr>0xffffff) break; //no more random addresses found -> exit  
409 -  
410 - //find first unused short address  
411 - for(sa=0; sa<64; sa++) {  
412 - if(arr[sa]==0) break;  
413 - }  
414 - if(sa>=64) break; //all 64 short addresses assigned -> exit  
415 -  
416 - //mark short address as used  
417 - arr[sa] = 1;  
418 - cnt++;  
419 -  
420 - //assign short address  
421 - this->program_short_address(sa);  
422 -  
423 - //Serial.println(this->query_short_address()); //TODO check read adr, handle if not the same...  
424 -  
425 - //remove the device from the search  
426 - this->cmd(DALI_WITHDRAW,0x00);  
427 - }  
428 -  
429 - //terminate the DALI_INITIALISE command  
430 - this->cmd(DALI_TERMINATE,0x00);  
431 - return cnt;  
432 -}  
433 -  
434 -  
435 -  
436 -//======================================================================