// Calculate & set any offsets to account for execution times. // // Args: // hz: The frequency to calibrate at >= 1000Hz. Default is 38000Hz. // // Status: ALPHA / Untested. // // NOTE: // This will generate an 65535us mark() IR LED signal. // This only needs to be called once, if at all. void IRsend::calibrate(uint16_t hz) { if (hz < 1000) // Were we given kHz? Supports the old call usage. hz *= 1000; periodOffset = 0; // Turn off any existing offset while we calibrate. enableIROut(hz); IRtimer usecTimer = IRtimer(); // Start a timer *just* before we do the call. uint16_t pulses = mark(UINT16_MAX); // Generate a PWM of 65,535 us. (Max.) uint32_t timeTaken = usecTimer.elapsed(); // Record the time it took. // While it shouldn't be neccesary, assume at least 1 pulse, to avoid a // divide by 0 situation. pulses = std::max(pulses, (uint16_t) 1U); uint32_t calcPeriod = calcUSecPeriod(hz); // e.g. @38kHz it should be 26us. // Assuming 38kHz for the example calculations: // In a 65535us pulse, we should have 2520.5769 pulses @ 26us periods. // e.g. 65535.0us / 26us = 2520.5769 // This should have caused approx 2520 loops through the main loop in mark(). // The average over that many interations should give us a reasonable // approximation at what offset we need to use to account for instruction // execution times. // // Calculate the actual period from the actual time & the actual pulses // generated. double_t actualPeriod = (double_t) timeTaken / (double_t) pulses; // Store the difference between the actual time per period vs. calculated. periodOffset = (int8_t) ((double_t) calcPeriod - actualPeriod); }
// Set the output frequency modulation and duty cycle. // // Args: // freq: The freq we want to modulate at. Assumes < 1000 means kHz else Hz. // duty: Percentage duty cycle of the LED. e.g. 25 = 25% = 1/4 on, 3/4 off. // // Note: // Integer timing functions & math mean we can't do fractions of // microseconds timing. Thus minor changes to the freq & duty values may have // limited effect. You've been warned. void IRsend::enableIROut(uint32_t freq, uint8_t duty) { // Can't have more than 100% duty cycle. duty = std::min(duty, (uint8_t) 100); if (freq < 1000) // Were we given kHz? Supports the old call usage. freq *= 1000; uint32_t period = calcUSecPeriod(freq); // Nr. of uSeconds the LED will be on per pulse. onTimePeriod = (period * duty) / 100; // Nr. of uSeconds the LED will be off per pulse. offTimePeriod = period - onTimePeriod; }
// Send a Pronto Code formatted message. // // Args: // data: An array of uint16_t containing the pronto codes. // len: Nr. of entries in the data[] array. // repeat: Nr. of times to repeat the message. // // Status: ALPHA / Not tested in the real world. // // Note: // Pronto codes are typically represented in hexadecimal. // You will need to convert the code to an array of integers, and calculate // it's length. // e.g. // A Sony 20 bit DVD remote command. // "0000 0067 0000 0015 0060 0018 0018 0018 0030 0018 0030 0018 0030 0018 // 0018 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0030 0018 // 0030 0018 0030 0018 0018 0018 0018 0018 0030 0018 0018 0018 0018 0018 // 0030 0018 0018 03f6" // // converts to: // // uint16_t prontoCode[46] = { // 0x0000, 0x0067, 0x0000, 0x0015, // 0x0060, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0030, 0x0018, // 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, // 0x0018, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, // 0x0030, 0x0018, 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, // 0x0030, 0x0018, 0x0018, 0x0018, 0x0018, 0x0018, 0x0030, 0x0018, // 0x0018, 0x03f6}; // // Send the Pronto(Sony) code. Repeat twice as Sony's require that. // sendPronto(prontoCode, 46, SONY_MIN_REPEAT); // // Ref: // http://www.etcwiki.org/wiki/Pronto_Infrared_Format // http://www.remotecentral.com/features/irdisp2.htm void IRsend::sendPronto(uint16_t data[], uint16_t len, uint16_t repeat) { // Check we have enough data to work out what to send. if (len < PRONTO_MIN_LENGTH) return; // We only know how to deal with 'raw' pronto codes types. Reject all others. if (data[PRONTO_TYPE_OFFSET] != 0) return; // Pronto frequency is in Hz. uint16_t hz = (uint16_t) (1000000U / (data[PRONTO_FREQ_OFFSET] * PRONTO_FREQ_FACTOR)); enableIROut(hz); // Grab the length of the two sequences. uint16_t seq_1_len = data[PRONTO_SEQ_1_LEN_OFFSET] * 2; uint16_t seq_2_len = data[PRONTO_SEQ_2_LEN_OFFSET] * 2; // Calculate where each sequence starts in the buffer. uint16_t seq_1_start = PRONTO_DATA_OFFSET; uint16_t seq_2_start = PRONTO_DATA_OFFSET + seq_1_len; uint32_t periodic_time = calcUSecPeriod(hz, false); // Normal (1st sequence) case. // Is there a first (normal) sequence to send? if (seq_1_len > 0) { // Check we have enough data to send the complete first sequence. if (seq_1_len + seq_1_start > len) return; // Send the contents of the 1st sequence. for (uint16_t i = seq_1_start; i < seq_1_start + seq_1_len; i += 2) { mark(data[i] * periodic_time); space(data[i + 1] * periodic_time); } } else { // There was no first sequence to send, it is implied that we have to send // the 2nd/repeat sequence an additional time. i.e. At least once. repeat++; } // Repeat (2nd sequence) case. // Is there a second (repeat) sequence to be sent? if (seq_2_len > 0) { // Check we have enough data to send the complete second sequence. if (seq_2_len + seq_2_start > len) return; // Send the contents of the 2nd sequence. for (uint16_t r = 0; r < repeat; r++) for (uint16_t i = seq_2_start; i < seq_2_start + seq_2_len; i += 2) { mark(data[i] * periodic_time); space(data[i + 1] * periodic_time); } } }
// Set the output frequency modulation and duty cycle. // // Args: // freq: The freq we want to modulate at. Assumes < 1000 means kHz else Hz. // duty: Percentage duty cycle of the LED. e.g. 25 = 25% = 1/4 on, 3/4 off. // This is ignored if modulation is disabled at object instantiation. // // Note: // Integer timing functions & math mean we can't do fractions of // microseconds timing. Thus minor changes to the freq & duty values may have // limited effect. You've been warned. void IRsend::enableIROut(uint32_t freq, uint8_t duty) { // Set the duty cycle to use if we want freq. modulation. if (modulation) { _dutycycle = std::min(duty, (uint8_t) DUTY_MAX); } else { _dutycycle = DUTY_MAX; } if (freq < 1000) // Were we given kHz? Supports the old call usage. freq *= 1000; uint32_t period = calcUSecPeriod(freq); // Nr. of uSeconds the LED will be on per pulse. onTimePeriod = (period * _dutycycle) / DUTY_MAX; // Nr. of uSeconds the LED will be off per pulse. offTimePeriod = period - onTimePeriod; }