Commit 1e9aafb0e7b5b118bc042269f1b4e7d0b290229f
Committed by
GitHub
1 parent
ed9f0a60
.
Showing
5 changed files
with
310 additions
and
185 deletions
Examples/Commissioning/Commissioning.ino
| ... | ... | @@ -28,9 +28,9 @@ Changelog: |
| 28 | 28 | #define DALI_OUTPUT_PIN 3 |
| 29 | 29 | #define DALI_INPUT_PIN 4 |
| 30 | 30 | |
| 31 | -#include "qqqDali.h" | |
| 31 | +#include "qqqDali_ATMega328.h" | |
| 32 | 32 | |
| 33 | -Dali dali; | |
| 33 | +Dali_ATMega328 dali; | |
| 34 | 34 | |
| 35 | 35 | void setup() { |
| 36 | 36 | Serial.begin(115200); |
| ... | ... | @@ -50,42 +50,52 @@ void loop() { |
| 50 | 50 | while (Serial.available() > 0) { |
| 51 | 51 | int incomingByte = Serial.read(); |
| 52 | 52 | switch(incomingByte) { |
| 53 | - case '1': scan_short_addr(); menu(); break; | |
| 54 | - case '2': delete_short_addr(); menu(); break; | |
| 55 | - case '3': commission_wo_short(); menu(); break; | |
| 56 | - case '4': commission_all(); menu(); break; | |
| 57 | - case '5': flash(); menu(); break; } | |
| 53 | + case '1': menu_flash(); menu(); break; | |
| 54 | + case '2': menu_scan_short_addr(); menu(); break; | |
| 55 | + case '3': menu_commission(); menu(); break; | |
| 56 | + case '4': menu_delete_short_addr(); menu(); break; | |
| 57 | + } | |
| 58 | 58 | } |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | void menu() { |
| 62 | 62 | Serial.println("----------------------------"); |
| 63 | - Serial.println("1 Scan all short addresses"); | |
| 64 | - Serial.println("2 Dele1te all short addresses"); | |
| 65 | - Serial.println("3 Commission w/o short adr"); | |
| 66 | - Serial.println("4 Commission all short addresses"); | |
| 67 | - Serial.println("5 Flash all lights"); | |
| 63 | + Serial.println("1 Flash all lights"); | |
| 64 | + Serial.println("2 Scan short addresses"); | |
| 65 | + Serial.println("3 Commission short addresses"); | |
| 66 | + Serial.println("4 Delete short addresses"); | |
| 68 | 67 | Serial.println("----------------------------"); |
| 69 | 68 | } |
| 70 | 69 | |
| 71 | -void delete_short_addr() { | |
| 72 | - Serial.println("Running: Delete all short addresses"); | |
| 73 | - //remove all short addresses | |
| 74 | - dali.cmd(DALI_DATA_TRANSFER_REGISTER0,0xFF); | |
| 75 | - dali.cmd(DALI_SET_SHORT_ADDRESS, 0xFF); | |
| 70 | + | |
| 71 | +void menu_flash() { | |
| 72 | + Serial.println("Running: Flash all"); | |
| 73 | + for(uint8_t i=0;i<5;i++) { | |
| 74 | + dali.set_level(254); | |
| 75 | + Serial.print("."); | |
| 76 | + delay(500); | |
| 77 | + dali.set_level(0); | |
| 78 | + Serial.print("."); | |
| 79 | + delay(500); | |
| 80 | + } | |
| 81 | + Serial.println(); | |
| 76 | 82 | } |
| 77 | 83 | |
| 78 | -void scan_short_addr() { | |
| 84 | +void menu_scan_short_addr() { | |
| 79 | 85 | Serial.println("Running: Scan all short addresses"); |
| 80 | 86 | uint8_t sa; |
| 87 | + uint8_t cnt = 0; | |
| 81 | 88 | for(sa = 0; sa<64; sa++) { |
| 82 | 89 | int16_t rv = dali.cmd(DALI_QUERY_STATUS,sa); |
| 83 | 90 | if(rv!=DALI_RESULT_NO_REPLY) { |
| 91 | + cnt++; | |
| 92 | + Serial.print("short address="); | |
| 84 | 93 | Serial.print(sa); |
| 85 | 94 | Serial.print(" status=0x"); |
| 86 | 95 | Serial.print(rv,HEX); |
| 87 | 96 | Serial.print(" minLevel="); |
| 88 | 97 | Serial.print(dali.cmd(DALI_QUERY_MIN_LEVEL,sa)); |
| 98 | + Serial.print(" flashing"); | |
| 89 | 99 | for(uint8_t i=0;i<5;i++) { |
| 90 | 100 | dali.set_level(254,sa); |
| 91 | 101 | Serial.print("."); |
| ... | ... | @@ -102,28 +112,79 @@ void scan_short_addr() { |
| 102 | 112 | // dali.cmd(DALI_SET_SHORT_ADDRESS, sa); |
| 103 | 113 | // } |
| 104 | 114 | } |
| 115 | + Serial.print("DONE, found ");Serial.print(cnt);Serial.println(" short addresses"); | |
| 105 | 116 | } |
| 106 | 117 | |
| 107 | 118 | //might need a couple of calls to find everything... |
| 108 | -void commission_wo_short(){ | |
| 109 | - Serial.println("Running: Commission w/o short adr"); | |
| 110 | - dali.commission(0xff); //init_arg=0b11111111 : all without short address | |
| 119 | +void menu_commission(){ | |
| 120 | + Serial.println("Running: Commission - be patient, this takes a while ...."); | |
| 121 | +// uint8_t cnt = dali.commission(0xff); //init_arg=0b11111111 : all without short address | |
| 122 | + uint8_t cnt = debug_commission(0xff); //init_arg=0b11111111 : all without short address | |
| 123 | + Serial.print("DONE, assigned ");Serial.print(cnt);Serial.println(" new short addresses"); | |
| 111 | 124 | } |
| 112 | 125 | |
| 113 | -void commission_all(){ | |
| 114 | - Serial.println("Running: Commission all"); | |
| 115 | - dali.commission(0x00); //init_arg=0b00000000 : all | |
| 126 | +void menu_delete_short_addr() { | |
| 127 | + Serial.println("Running: Delete all short addresses"); | |
| 128 | + //remove all short addresses | |
| 129 | + dali.cmd(DALI_DATA_TRANSFER_REGISTER0,0xFF); | |
| 130 | + dali.cmd(DALI_SET_SHORT_ADDRESS, 0xFF); | |
| 131 | + Serial.println("DONE delete"); | |
| 116 | 132 | } |
| 117 | 133 | |
| 118 | -void flash() { | |
| 119 | - Serial.println("Running: Flash all"); | |
| 120 | - for(uint8_t i=0;i<5;i++) { | |
| 121 | - dali.set_level(254); | |
| 122 | - Serial.print("."); | |
| 123 | - delay(500); | |
| 124 | - dali.set_level(0); | |
| 125 | - Serial.print("."); | |
| 126 | - delay(500); | |
| 134 | +//init_arg=11111111 : all without short address | |
| 135 | +//init_arg=00000000 : all | |
| 136 | +//init_arg=0AAAAAA1 : only for this shortadr | |
| 137 | +//returns number of new short addresses assigned | |
| 138 | +uint8_t debug_commission(uint8_t init_arg) { | |
| 139 | + uint8_t cnt = 0; | |
| 140 | + uint8_t arr[64]; | |
| 141 | + uint8_t sa; | |
| 142 | + for(sa=0; sa<64; sa++) arr[sa]=0; | |
| 143 | + | |
| 144 | + //find existing short addresses when not assigning all | |
| 145 | + if(init_arg!=0b00000000) { | |
| 146 | + Serial.println("Find existing short adr"); | |
| 147 | + for(sa = 0; sa<64; sa++) { | |
| 148 | + int16_t rv = dali.cmd(DALI_QUERY_STATUS,sa); | |
| 149 | + if(rv!=DALI_RESULT_NO_REPLY) { | |
| 150 | + arr[sa]=1; | |
| 151 | + Serial.print("sortadr="); | |
| 152 | + Serial.print(sa); | |
| 153 | + Serial.print(" status=0x"); | |
| 154 | + Serial.print(rv,HEX); | |
| 155 | + Serial.print(" minLevel="); | |
| 156 | + Serial.println(dali.cmd(DALI_QUERY_MIN_LEVEL,sa)); | |
| 157 | + } | |
| 158 | + } | |
| 127 | 159 | } |
| 128 | - Serial.println(); | |
| 160 | + | |
| 161 | + dali.cmd(DALI_INITIALISE,init_arg); | |
| 162 | + dali.cmd(DALI_RANDOMISE,0x00); | |
| 163 | + | |
| 164 | + Serial.println("Find random adr"); | |
| 165 | + while(1) { | |
| 166 | + uint32_t adr = dali.find_addr(); | |
| 167 | + if(adr>0xffffff) break; | |
| 168 | + Serial.print("found="); | |
| 169 | + Serial.println(adr,HEX); | |
| 170 | + | |
| 171 | + //find available address | |
| 172 | + for(sa=0; sa<64; sa++) { | |
| 173 | + if(arr[sa]==0) break; | |
| 174 | + } | |
| 175 | + if(sa>=64) break; | |
| 176 | + arr[sa] = 1; | |
| 177 | + cnt++; | |
| 178 | + | |
| 179 | + Serial.print("program short adr="); | |
| 180 | + Serial.println(sa); | |
| 181 | + dali.program_short_address(sa); | |
| 182 | + Serial.print("read short adr="); | |
| 183 | + Serial.println(dali.query_short_address()); | |
| 184 | + | |
| 185 | + dali.cmd(DALI_WITHDRAW,0x00); | |
| 186 | + } | |
| 187 | + | |
| 188 | + dali.cmd(DALI_TERMINATE,0x00); | |
| 189 | + return cnt; | |
| 129 | 190 | } | ... | ... |
Examples/Dimmer/Dimmer.ino
qqqDali.cpp
| ... | ... | @@ -16,39 +16,25 @@ |
| 16 | 16 | |
| 17 | 17 | ---------------------------------------------------------------------------- |
| 18 | 18 | Changelog: |
| 19 | +2020-11-10 Split off hardware specific code into separate class | |
| 19 | 20 | 2020-11-08 Created & tested on ATMega328 @ 8Mhz |
| 20 | 21 | ###########################################################################*/ |
| 21 | 22 | #include "qqqDali.h" |
| 22 | -#include "arduino.h" | |
| 23 | 23 | |
| 24 | 24 | //########################################################################### |
| 25 | 25 | // Helpers |
| 26 | 26 | //########################################################################### |
| 27 | -#define DALI_BUS_LOW() digitalWrite(this->tx_pin,LOW); this->tx_bus_low=1 | |
| 28 | -#define DALI_BUS_HIGH() digitalWrite(this->tx_pin,HIGH); this->tx_bus_low=0 | |
| 29 | -#define DALI_IS_BUS_LOW() (digitalRead(this->rx_pin)==LOW) | |
| 30 | -#define DALI_BAUD 1200 | |
| 27 | +#define DALI_TOL 15 //allow for 15% tolerance on timing (DALI spec calls for 10%, add 5% for uc clock tolerance) | |
| 31 | 28 | #define DALI_TE ((1000000+(DALI_BAUD))/(2*(DALI_BAUD))) //417us |
| 32 | -#define DALI_TE_MIN (80*DALI_TE)/100 | |
| 33 | -#define DALI_TE_MAX (120*DALI_TE)/100 | |
| 29 | +#define DALI_TE_MIN ((100-DALI_TOL)*DALI_TE)/100 | |
| 30 | +#define DALI_TE_MAX ((100+DALI_TOL)*DALI_TE)/100 | |
| 34 | 31 | #define DALI_IS_TE(x) ((DALI_TE_MIN)<=(x) && (x)<=(DALI_TE_MAX)) |
| 35 | 32 | #define DALI_IS_2TE(x) ((2*(DALI_TE_MIN))<=(x) && (x)<=(2*(DALI_TE_MAX))) |
| 36 | 33 | |
| 37 | 34 | //########################################################################### |
| 38 | 35 | // Transmitter ISR |
| 39 | 36 | //########################################################################### |
| 40 | -static Dali *IsrTimerHooks[DALI_HOOK_COUNT+1]; | |
| 41 | - | |
| 42 | -// timer compare interrupt service routine | |
| 43 | -ISR(TIMER1_COMPA_vect) { | |
| 44 | - | |
| 45 | - for(uint8_t i=0;i<DALI_HOOK_COUNT;i++) { | |
| 46 | - if(IsrTimerHooks[i]==NULL) {return;} | |
| 47 | - IsrTimerHooks[i]->ISR_timer(); | |
| 48 | - } | |
| 49 | -} | |
| 50 | - | |
| 51 | -//called every Te period (417us) | |
| 37 | +//called by derived class every Te period (417us) | |
| 52 | 38 | void Dali::ISR_timer() { |
| 53 | 39 | if(this->bus_idle_te_cnt<0xff) this->bus_idle_te_cnt++; |
| 54 | 40 | |
| ... | ... | @@ -59,26 +45,29 @@ void Dali::ISR_timer() { |
| 59 | 45 | case TX_START: |
| 60 | 46 | //wait for timeslot, then send start bit |
| 61 | 47 | if(this->bus_idle_te_cnt >= 22) { |
| 62 | - DALI_BUS_LOW(); | |
| 48 | + this->HAL_set_bus_low(); | |
| 49 | + this->tx_bus_low=1; | |
| 63 | 50 | this->tx_state = TX_START_X; |
| 64 | 51 | } |
| 65 | 52 | break; |
| 66 | 53 | case TX_START_X: |
| 67 | - DALI_BUS_HIGH(); | |
| 54 | + this->HAL_set_bus_high(); | |
| 55 | + this->tx_bus_low=0; | |
| 68 | 56 | this->tx_pos=0; |
| 69 | 57 | this->tx_state = TX_BIT; |
| 70 | 58 | break; |
| 71 | 59 | case TX_BIT: |
| 72 | - if(this->tx_msg[this->tx_pos>>3] & 1<<(7-(this->tx_pos&0x7))) {DALI_BUS_LOW();} else {DALI_BUS_HIGH();} | |
| 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;} | |
| 73 | 61 | this->tx_state = TX_BIT_X; |
| 74 | 62 | break; |
| 75 | 63 | case TX_BIT_X: |
| 76 | - if(this->tx_msg[this->tx_pos>>3] & 1<<(7-(this->tx_pos&0x7))) {DALI_BUS_HIGH();} else {DALI_BUS_LOW();} | |
| 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;} | |
| 77 | 65 | this->tx_pos++; |
| 78 | 66 | if(this->tx_pos < this->tx_len) {this->tx_state = TX_BIT;} else {this->tx_state = TX_STOP1;} |
| 79 | 67 | break; |
| 80 | 68 | case TX_STOP1: |
| 81 | - DALI_BUS_HIGH(); | |
| 69 | + this->HAL_set_bus_high(); | |
| 70 | + this->tx_bus_low=0; | |
| 82 | 71 | this->tx_state = TX_STOP1_X; |
| 83 | 72 | break; |
| 84 | 73 | case TX_STOP1_X: |
| ... | ... | @@ -105,7 +94,7 @@ void Dali::ISR_timer() { |
| 105 | 94 | uint8_t bitlen = (this->rx_halfbitlen+1)>>1; |
| 106 | 95 | if((bitlen & 0x7) == 0) { |
| 107 | 96 | this->rx_len = bitlen>>3; |
| 108 | - if(this->EventHandlerReceivedData!=NULL) this->EventHandlerReceivedData(this, (uint8_t*)this->rx_msg, this->rx_len); | |
| 97 | + if(this->EventHandlerReceivedData) this->EventHandlerReceivedData(this, (uint8_t*)this->rx_msg, this->rx_len); | |
| 109 | 98 | }else{ |
| 110 | 99 | //invalid bitlen |
| 111 | 100 | //TODO handle this |
| ... | ... | @@ -116,29 +105,11 @@ void Dali::ISR_timer() { |
| 116 | 105 | //########################################################################### |
| 117 | 106 | // Receiver ISR |
| 118 | 107 | //########################################################################### |
| 119 | -//pin PCINT | |
| 120 | -//0-7 PCINT2_vect PCINT16-23 | |
| 121 | -//8-13 PCINT0_vect PCINT0-5 | |
| 122 | -//14-19 PCINT1_vect PCINT8-13 | |
| 123 | -static Dali *IsrPCINT0Hook; | |
| 124 | -static Dali *IsrPCINT1Hook; | |
| 125 | -static Dali *IsrPCINT2Hook; | |
| 126 | - | |
| 127 | -ISR(PCINT0_vect) { | |
| 128 | - if(IsrPCINT0Hook!=NULL) IsrPCINT0Hook->ISR_pinchange(); | |
| 129 | -} | |
| 130 | -ISR(PCINT1_vect) { | |
| 131 | - if(IsrPCINT1Hook!=NULL) IsrPCINT1Hook->ISR_pinchange(); | |
| 132 | -} | |
| 133 | -ISR(PCINT2_vect) { | |
| 134 | - if(IsrPCINT2Hook!=NULL) IsrPCINT2Hook->ISR_pinchange(); | |
| 135 | -} | |
| 136 | - | |
| 137 | - | |
| 108 | +//called by derived class on bus state change | |
| 138 | 109 | void Dali::ISR_pinchange() { |
| 139 | - uint32_t ts = micros(); //get timestamp of change | |
| 110 | + uint32_t ts = this->HAL_micros(); //get timestamp of change | |
| 140 | 111 | this->bus_idle_te_cnt=0; //reset idle counter |
| 141 | - uint8_t bus_low = DALI_IS_BUS_LOW(); | |
| 112 | + uint8_t bus_low = this->HAL_is_bus_low(); | |
| 142 | 113 | |
| 143 | 114 | //exit if transmitting |
| 144 | 115 | if(this->tx_state != TX_IDLE) { |
| ... | ... | @@ -206,63 +177,6 @@ void Dali::push_halfbit(uint8_t bit) { |
| 206 | 177 | //########################################################################### |
| 207 | 178 | // Dali Class |
| 208 | 179 | //########################################################################### |
| 209 | -void Dali::begin(int8_t tx_pin, int8_t rx_pin) { | |
| 210 | - this->tx_pin = tx_pin; | |
| 211 | - this->rx_pin = rx_pin; | |
| 212 | - this->tx_state = TX_IDLE; | |
| 213 | - this->rx_state = RX_IDLE; | |
| 214 | - | |
| 215 | - //setup tx | |
| 216 | - if(this->tx_pin>=0) { | |
| 217 | - //setup tx pin | |
| 218 | - pinMode(this->tx_pin, OUTPUT); | |
| 219 | - DALI_BUS_HIGH(); | |
| 220 | - | |
| 221 | - //setup tx timer interrupt | |
| 222 | - TCCR1A = 0; | |
| 223 | - TCCR1B = 0; | |
| 224 | - TCNT1 = 0; | |
| 225 | - | |
| 226 | - OCR1A = (F_CPU+(DALI_BAUD))/(2*(DALI_BAUD)); // compare match register 16MHz/256/2Hz | |
| 227 | - TCCR1B |= (1 << WGM12); // CTC mode | |
| 228 | - TCCR1B |= (1 << CS10); // 1:1 prescaler | |
| 229 | - TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt | |
| 230 | - | |
| 231 | - //setup timer interrupt hooks | |
| 232 | - for(uint8_t i=0;i<DALI_HOOK_COUNT;i++) { | |
| 233 | - if(IsrTimerHooks[i] == NULL) { | |
| 234 | - IsrTimerHooks[i] = this; | |
| 235 | - break; | |
| 236 | - } | |
| 237 | - } | |
| 238 | - } | |
| 239 | - | |
| 240 | - //setup rx | |
| 241 | - if(this->rx_pin>=0) { | |
| 242 | - //setup rx pin | |
| 243 | - pinMode(this->rx_pin, INPUT); | |
| 244 | - | |
| 245 | - //setup rx pinchange interrupt | |
| 246 | - // 0- 7 PCINT2_vect PCINT16-23 | |
| 247 | - // 8-13 PCINT0_vect PCINT0-5 | |
| 248 | - //14-19 PCINT1_vect PCINT8-13 | |
| 249 | - if(this->rx_pin<=7){ | |
| 250 | - PCICR |= (1<<PCIE2); | |
| 251 | - PCMSK2 |= (1<< (this->rx_pin)); | |
| 252 | - IsrPCINT2Hook = this; //setup pinchange interrupt hook | |
| 253 | - }else if(this->rx_pin<=13) { | |
| 254 | - PCICR |= (1<<PCIE0); | |
| 255 | - PCMSK0 |= (1<< (this->rx_pin-8)); | |
| 256 | - IsrPCINT0Hook = this; //setup pinchange interrupt hook | |
| 257 | - }else if(this->rx_pin<=19) { | |
| 258 | - PCICR |= (1<<PCIE1); | |
| 259 | - PCMSK1 |= (1<< (this->rx_pin-14)); | |
| 260 | - IsrPCINT1Hook = this; //setup pinchange interrupt hook | |
| 261 | - } | |
| 262 | - } | |
| 263 | -} | |
| 264 | - | |
| 265 | - | |
| 266 | 180 | uint8_t Dali::send(uint8_t* tx_msg, uint8_t tx_len_bytes) { |
| 267 | 181 | if(tx_len_bytes>3) return -(DALI_RESULT_INVALID_TOO_LONG); |
| 268 | 182 | if(this->tx_state != TX_IDLE) return -(DALI_RESULT_TIMEOUT); |
| ... | ... | @@ -273,41 +187,41 @@ uint8_t Dali::send(uint8_t* tx_msg, uint8_t tx_len_bytes) { |
| 273 | 187 | return 0; |
| 274 | 188 | } |
| 275 | 189 | |
| 276 | -uint8_t Dali::sendwait(uint8_t* tx_msg, uint8_t tx_len_bytes, uint32_t timeout_ms) { | |
| 190 | +uint8_t Dali::sendwait(uint8_t* tx_msg, uint8_t tx_len_bytes, uint32_t timeout_us) { | |
| 277 | 191 | if(tx_len_bytes>3) return -(DALI_RESULT_INVALID_TOO_LONG); |
| 278 | - uint32_t ts = millis(); | |
| 192 | + uint32_t ts = HAL_micros(); | |
| 279 | 193 | //wait for idle |
| 280 | 194 | while(this->tx_state != TX_IDLE) { |
| 281 | - if(millis() - ts > timeout_ms) return -(DALI_RESULT_TIMEOUT); | |
| 195 | + if(HAL_micros() - ts > timeout_us) return -(DALI_RESULT_TIMEOUT); | |
| 282 | 196 | } |
| 283 | 197 | //start transmit |
| 284 | 198 | uint8_t rv = this->send(tx_msg,tx_len_bytes); |
| 285 | 199 | if(rv) return rv; |
| 286 | 200 | //wait for completion |
| 287 | 201 | while(this->tx_state != TX_IDLE) { |
| 288 | - if(millis() - ts > timeout_ms) return -(DALI_RESULT_TX_TIMEOUT); | |
| 202 | + if(HAL_micros() - ts > timeout_us) return -(DALI_RESULT_TX_TIMEOUT); | |
| 289 | 203 | } |
| 290 | 204 | return 0; |
| 291 | 205 | } |
| 292 | 206 | |
| 293 | 207 | //transmit 2 byte command, receive 1 byte reply |
| 294 | -int16_t Dali::tx(uint8_t cmd0, uint8_t cmd1, uint32_t timeout_ms) { | |
| 208 | +int16_t Dali::tx(uint8_t cmd0, uint8_t cmd1, uint32_t timeout_us) { | |
| 295 | 209 | uint8_t tx[2]; |
| 296 | 210 | tx[0] = cmd0; |
| 297 | 211 | tx[1] = cmd1; |
| 298 | - int16_t rv = this->sendwait(tx,2); | |
| 212 | + int16_t rv = this->sendwait(tx, 2, timeout_us); | |
| 299 | 213 | this->rx_halfbitlen = 0; |
| 300 | 214 | if(rv) return -rv;; |
| 301 | 215 | |
| 302 | 216 | //wait up to 10 ms for start of reply |
| 303 | - uint32_t ts = millis(); | |
| 217 | + uint32_t ts = HAL_micros(); | |
| 304 | 218 | while(this->rx_state == RX_IDLE) { |
| 305 | - if(millis() - ts > 10) return DALI_RESULT_NO_REPLY; | |
| 219 | + if(HAL_micros() - ts > 10000) return DALI_RESULT_NO_REPLY; | |
| 306 | 220 | } |
| 307 | 221 | //wait up to 15 ms for completion of reply |
| 308 | - ts = millis(); | |
| 222 | + ts = HAL_micros(); | |
| 309 | 223 | while(this->rx_len == 0) { |
| 310 | - if(millis() - ts > 15) return DALI_RESULT_NO_REPLY; | |
| 224 | + if(HAL_micros() - ts > 15000) return DALI_RESULT_NO_REPLY; | |
| 311 | 225 | } |
| 312 | 226 | if(this->rx_len > 1) return DALI_RESULT_INVALID_REPLY; |
| 313 | 227 | return this->rx_msg[0]; |
| ... | ... | @@ -464,41 +378,40 @@ uint32_t Dali::find_addr() { |
| 464 | 378 | //init_arg=11111111 : all without short address |
| 465 | 379 | //init_arg=00000000 : all |
| 466 | 380 | //init_arg=0AAAAAA1 : only for this shortadr |
| 381 | +//returns number of new short addresses assigned | |
| 467 | 382 | uint8_t Dali::commission(uint8_t init_arg) { |
| 468 | 383 | uint8_t cnt = 0; |
| 469 | 384 | uint8_t arr[64]; |
| 470 | 385 | uint8_t sa; |
| 471 | 386 | for(sa=0; sa<64; sa++) arr[sa]=0; |
| 472 | 387 | |
| 473 | - //find existing short addresses | |
| 474 | -// if(init_arg==0xff) { | |
| 475 | - Serial.println("Short adr"); | |
| 388 | + //find existing short addresses when not assigning all | |
| 389 | + if(init_arg!=0b00000000) { | |
| 390 | + //Serial.println("Short adr"); | |
| 476 | 391 | for(sa = 0; sa<64; sa++) { |
| 477 | 392 | int16_t rv = this->cmd(DALI_QUERY_STATUS,sa); |
| 478 | 393 | if(rv!=DALI_RESULT_NO_REPLY) { |
| 479 | 394 | arr[sa]=1; |
| 480 | - cnt++; | |
| 481 | - Serial.print(sa); | |
| 482 | - Serial.print(" status=0x"); | |
| 483 | - Serial.print(rv,HEX); | |
| 484 | - Serial.print(" minLevel="); | |
| 485 | - Serial.println(this->cmd(DALI_QUERY_MIN_LEVEL,sa)); | |
| 395 | + //Serial.print(sa); | |
| 396 | + //Serial.print(" status=0x"); | |
| 397 | + //Serial.print(rv,HEX); | |
| 398 | + //Serial.print(" minLevel="); | |
| 399 | + //Serial.println(this->cmd(DALI_QUERY_MIN_LEVEL,sa)); | |
| 486 | 400 | } |
| 487 | 401 | } |
| 488 | -// } | |
| 402 | + } | |
| 489 | 403 | |
| 490 | 404 | this->cmd(DALI_INITIALISE,init_arg); |
| 491 | 405 | this->cmd(DALI_RANDOMISE,0x00); |
| 492 | - delay(100); | |
| 493 | 406 | |
| 494 | - while(cnt<64) { | |
| 407 | + while(1) { | |
| 495 | 408 | //Serial.print("addr="); |
| 496 | 409 | //Serial.println(this->get_random_address(0xff),HEX); |
| 497 | 410 | |
| 498 | 411 | uint32_t adr = this->find_addr(); |
| 499 | - if(adr>0xffffff) break; | |
| 500 | - Serial.print("found adr="); | |
| 501 | - Serial.println(adr,HEX); | |
| 412 | + if(adr>0xffffff) break; //no more random addresses found -> exit | |
| 413 | + //Serial.print("found adr="); | |
| 414 | + //Serial.println(adr,HEX); | |
| 502 | 415 | |
| 503 | 416 | //Serial.print("short adr="); |
| 504 | 417 | //Serial.println(dali_query_short_address()); |
| ... | ... | @@ -507,17 +420,17 @@ uint8_t Dali::commission(uint8_t init_arg) { |
| 507 | 420 | for(sa=0; sa<64; sa++) { |
| 508 | 421 | if(arr[sa]==0) break; |
| 509 | 422 | } |
| 510 | - if(sa>=64) break; | |
| 423 | + if(sa>=64) break; //all 64 short addresses assigned -> exit | |
| 511 | 424 | arr[sa] = 1; |
| 512 | 425 | cnt++; |
| 513 | 426 | |
| 514 | - Serial.print("program short adr="); | |
| 515 | - Serial.println(sa); | |
| 427 | + //Serial.print("program short adr="); | |
| 428 | + //Serial.println(sa); | |
| 516 | 429 | this->program_short_address(sa); |
| 517 | 430 | //dali_program_short_address(0xff); |
| 518 | 431 | |
| 519 | - Serial.print("read short adr="); | |
| 520 | - Serial.println(this->query_short_address()); | |
| 432 | + //Serial.print("read short adr="); | |
| 433 | + //Serial.println(this->query_short_address()); //TODO check read adr, handle if not the same... | |
| 521 | 434 | |
| 522 | 435 | this->cmd(DALI_WITHDRAW,0x00); |
| 523 | 436 | } | ... | ... |
qqqDali.h
| ... | ... | @@ -16,14 +16,24 @@ |
| 16 | 16 | |
| 17 | 17 | ---------------------------------------------------------------------------- |
| 18 | 18 | Changelog: |
| 19 | +2020-11-10 Split off hardware specific code into separate class | |
| 19 | 20 | 2020-11-08 Created & tested on ATMega328 @ 8Mhz |
| 20 | 21 | ###########################################################################*/ |
| 21 | 22 | #include <inttypes.h> |
| 22 | 23 | |
| 23 | - | |
| 24 | 24 | class Dali { |
| 25 | 25 | public: |
| 26 | - void begin(int8_t tx_pin, int8_t rx_pin); | |
| 26 | + //Hardware Abstraction Layer overrides | |
| 27 | + virtual void HAL_set_bus_low() const = 0; //set DALI bus to low state | |
| 28 | + virtual void HAL_set_bus_high() const = 0; //set DALI bus to high state | |
| 29 | + virtual uint8_t HAL_is_bus_low() const = 0; //is DALI bus in low state? | |
| 30 | + virtual uint32_t HAL_micros() const = 0; //get microsecond time stamp | |
| 31 | + void ISR_timer(); //call this function every 417us | |
| 32 | + void ISR_pinchange(); //call this function on change of DALI bus | |
| 33 | + | |
| 34 | + //callback on received data from DALI bus | |
| 35 | + typedef void (*EventHandlerReceivedDataFuncPtr)(Dali *sender, uint8_t *data, uint8_t len); | |
| 36 | + EventHandlerReceivedDataFuncPtr EventHandlerReceivedData; | |
| 27 | 37 | |
| 28 | 38 | //high level functions |
| 29 | 39 | void set_level(uint8_t level, uint8_t adr=0xFF); //set arc level |
| ... | ... | @@ -43,24 +53,17 @@ public: |
| 43 | 53 | uint8_t query_short_address(); |
| 44 | 54 | uint32_t find_addr(); |
| 45 | 55 | |
| 46 | - | |
| 47 | 56 | //low level functions |
| 48 | - typedef void (*EventHandlerReceivedDataFuncPtr)(Dali *sender, uint8_t *data, uint8_t len); | |
| 49 | - EventHandlerReceivedDataFuncPtr EventHandlerReceivedData; | |
| 50 | - | |
| 51 | 57 | uint8_t send(uint8_t* tx_msg, uint8_t tx_len_bytes); |
| 52 | - uint8_t sendwait(uint8_t* tx_msg, uint8_t tx_len_bytes, uint32_t timeout_ms=500); | |
| 53 | - int16_t tx(uint8_t cmd0, uint8_t cmd1, uint32_t timeout_ms=500); | |
| 54 | - void ISR_timer(); | |
| 55 | - void ISR_pinchange(); | |
| 58 | + uint8_t sendwait(uint8_t* tx_msg, uint8_t tx_len_bytes, uint32_t timeout_us=500000); | |
| 59 | + int16_t tx(uint8_t cmd0, uint8_t cmd1, uint32_t timeout_us=500000); | |
| 56 | 60 | |
| 57 | - #define DALI_HOOK_COUNT 3 | |
| 58 | - | |
| 59 | - | |
| 60 | -private: | |
| 61 | + //initialize variables | |
| 62 | + Dali() : tx_state(TX_IDLE), rx_state(RX_IDLE), tx_bus_low(0), tx_len(0), EventHandlerReceivedData(0) {}; | |
| 63 | + | |
| 64 | +protected: | |
| 61 | 65 | //low level functions |
| 62 | 66 | enum tx_stateEnum { TX_IDLE=0,TX_START,TX_START_X,TX_BIT,TX_BIT_X,TX_STOP1,TX_STOP1_X,TX_STOP2,TX_STOP2_X,TX_STOP3}; |
| 63 | - uint8_t tx_pin; //transmitter pin | |
| 64 | 67 | uint8_t tx_msg[3]; //message to transmit |
| 65 | 68 | uint8_t tx_len; //number of bits to transmit |
| 66 | 69 | volatile uint8_t tx_pos; //current bit transmit position |
| ... | ... | @@ -69,7 +72,6 @@ private: |
| 69 | 72 | volatile uint8_t tx_bus_low; //bus is low according to transmitter? |
| 70 | 73 | |
| 71 | 74 | enum rx_stateEnum { RX_IDLE,RX_START,RX_BIT}; |
| 72 | - uint8_t rx_pin; //receiver pin | |
| 73 | 75 | volatile uint8_t rx_last_bus_low; //receiver as low at last pinchange |
| 74 | 76 | volatile uint32_t rx_last_change_ts; //timestamp last pinchange |
| 75 | 77 | volatile rx_stateEnum rx_state; //current state |
| ... | ... | @@ -88,6 +90,10 @@ private: |
| 88 | 90 | }; |
| 89 | 91 | |
| 90 | 92 | |
| 93 | + | |
| 94 | + | |
| 95 | +#define DALI_BAUD 1200 | |
| 96 | + | |
| 91 | 97 | #define DALI_RESULT_TIMEOUT -1 //Timeout waiting for DALI bus |
| 92 | 98 | #define DALI_RESULT_INVALID_TOO_LONG -2 //Trying to send too many bytes (max 3) |
| 93 | 99 | #define DALI_RESULT_TX_TIMEOUT -3 //Timeout during transmission | ... | ... |
qqqDali_ATMega328.h
0 โ 100644
| 1 | +/*########################################################################### | |
| 2 | + qqqDali_ATMega328.h - 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 Created & tested on ATMega328 @ 8Mhz | |
| 20 | +###########################################################################*/ | |
| 21 | +#include "arduino.h" | |
| 22 | +#include "qqqDali.h" | |
| 23 | + | |
| 24 | +#define DALI_HOOK_COUNT 3 | |
| 25 | + | |
| 26 | +class Dali_ATMega328 : public Dali { | |
| 27 | +public: | |
| 28 | + void begin(int8_t tx_pin, int8_t rx_pin); | |
| 29 | + virtual void HAL_set_bus_low() const override; | |
| 30 | + virtual void HAL_set_bus_high() const override; | |
| 31 | + virtual uint8_t HAL_is_bus_low() const override; | |
| 32 | + virtual uint32_t HAL_micros() const override; | |
| 33 | + | |
| 34 | +private: | |
| 35 | + uint8_t tx_pin; //transmitter pin | |
| 36 | + uint8_t rx_pin; //receiver pin | |
| 37 | +}; | |
| 38 | + | |
| 39 | +//----------------------------------------- | |
| 40 | +//Hardware Abstraction Layer | |
| 41 | +void Dali_ATMega328::HAL_set_bus_low() const { | |
| 42 | + digitalWrite(this->tx_pin,LOW); | |
| 43 | +} | |
| 44 | + | |
| 45 | +void Dali_ATMega328::HAL_set_bus_high() const { | |
| 46 | + digitalWrite(this->tx_pin,HIGH); | |
| 47 | +} | |
| 48 | + | |
| 49 | +uint8_t Dali_ATMega328::HAL_is_bus_low() const { | |
| 50 | + return (digitalRead(this->rx_pin) == LOW); | |
| 51 | +} | |
| 52 | + | |
| 53 | +uint32_t Dali_ATMega328::HAL_micros() const { | |
| 54 | + return micros(); | |
| 55 | +} | |
| 56 | + | |
| 57 | +//----------------------------------------- | |
| 58 | +// Transmitter ISR | |
| 59 | +static Dali *IsrTimerHooks[DALI_HOOK_COUNT+1]; | |
| 60 | + | |
| 61 | +// timer compare interrupt service routine | |
| 62 | +ISR(TIMER1_COMPA_vect) { | |
| 63 | + | |
| 64 | + for(uint8_t i=0;i<DALI_HOOK_COUNT;i++) { | |
| 65 | + if(IsrTimerHooks[i]==NULL) {return;} | |
| 66 | + IsrTimerHooks[i]->ISR_timer(); | |
| 67 | + } | |
| 68 | +} | |
| 69 | + | |
| 70 | +//----------------------------------------- | |
| 71 | +// Receiver ISR | |
| 72 | +//pin PCINT | |
| 73 | +//0-7 PCINT2_vect PCINT16-23 | |
| 74 | +//8-13 PCINT0_vect PCINT0-5 | |
| 75 | +//14-19 PCINT1_vect PCINT8-13 | |
| 76 | +static Dali *IsrPCINT0Hook; | |
| 77 | +static Dali *IsrPCINT1Hook; | |
| 78 | +static Dali *IsrPCINT2Hook; | |
| 79 | + | |
| 80 | +ISR(PCINT0_vect) { | |
| 81 | + if(IsrPCINT0Hook!=NULL) IsrPCINT0Hook->ISR_pinchange(); | |
| 82 | +} | |
| 83 | +ISR(PCINT1_vect) { | |
| 84 | + if(IsrPCINT1Hook!=NULL) IsrPCINT1Hook->ISR_pinchange(); | |
| 85 | +} | |
| 86 | +ISR(PCINT2_vect) { | |
| 87 | + if(IsrPCINT2Hook!=NULL) IsrPCINT2Hook->ISR_pinchange(); | |
| 88 | +} | |
| 89 | + | |
| 90 | + | |
| 91 | +//----------------------------------------- | |
| 92 | +// begin | |
| 93 | +void Dali_ATMega328::begin(int8_t tx_pin, int8_t rx_pin) { | |
| 94 | + this->tx_pin = tx_pin; | |
| 95 | + this->rx_pin = rx_pin; | |
| 96 | + | |
| 97 | + //setup tx | |
| 98 | + if(this->tx_pin>=0) { | |
| 99 | + //setup tx pin | |
| 100 | + pinMode(this->tx_pin, OUTPUT); | |
| 101 | + this->HAL_set_bus_high(); | |
| 102 | + this->tx_bus_low=0; | |
| 103 | + | |
| 104 | + //setup tx timer interrupt | |
| 105 | + TCCR1A = 0; | |
| 106 | + TCCR1B = 0; | |
| 107 | + TCNT1 = 0; | |
| 108 | + OCR1A = (F_CPU+(DALI_BAUD))/(2*(DALI_BAUD)); // compare match register at baud rate * 2 | |
| 109 | + TCCR1B |= (1 << WGM12); // CTC mode | |
| 110 | + TCCR1B |= (1 << CS10); // 1:1 prescaler | |
| 111 | + TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt | |
| 112 | + | |
| 113 | + //setup timer interrupt hooks | |
| 114 | + for(uint8_t i=0;i<DALI_HOOK_COUNT;i++) { | |
| 115 | + if(IsrTimerHooks[i] == NULL) { | |
| 116 | + IsrTimerHooks[i] = this; | |
| 117 | + break; | |
| 118 | + } | |
| 119 | + } | |
| 120 | + } | |
| 121 | + | |
| 122 | + //setup rx | |
| 123 | + if(this->rx_pin>=0) { | |
| 124 | + //setup rx pin | |
| 125 | + pinMode(this->rx_pin, INPUT); | |
| 126 | + | |
| 127 | + //setup rx pinchange interrupt | |
| 128 | + // 0- 7 PCINT2_vect PCINT16-23 | |
| 129 | + // 8-13 PCINT0_vect PCINT0-5 | |
| 130 | + //14-19 PCINT1_vect PCINT8-13 | |
| 131 | + if(this->rx_pin <= 7){ | |
| 132 | + PCICR |= (1 << PCIE2); | |
| 133 | + PCMSK2 |= (1 << (this->rx_pin)); | |
| 134 | + IsrPCINT2Hook = this; //setup pinchange interrupt hook | |
| 135 | + }else if(this->rx_pin <= 13) { | |
| 136 | + PCICR |= (1 << PCIE0); | |
| 137 | + PCMSK0 |= (1 << (this->rx_pin - 8)); | |
| 138 | + IsrPCINT0Hook = this; //setup pinchange interrupt hook | |
| 139 | + }else if(this->rx_pin <= 19) { | |
| 140 | + PCICR |= (1 << PCIE1); | |
| 141 | + PCMSK1 |= (1 << (this->rx_pin - 14)); | |
| 142 | + IsrPCINT1Hook = this; //setup pinchange interrupt hook | |
| 143 | + } | |
| 144 | + } | |
| 145 | +} | ... | ... |