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   -//======================================================================