Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
325 lines (276 sloc) 8.22 KB
// lab-irkit2.c
//
// Authors: Hans-Gert Dahmen <sexyludernatascha@gmail.com>
// modified by: Patrick Rudolph <siro@das-labor.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
/*
* IR codes are stored in an array of on/off pulse lengths.
* As transmissions must always start with a one and consist
* of alternating ones and zeroes, every even index in the
* array will be a one-pulse and every odd array index
* will be a zero-pulse.
*/
#include "lab-irkit2.h"
#include <avr/interrupt.h>
//set to 1 to debug
#define IR_DEBUG 1
//infrared current code array
uint16_t *volatile ir_curCode;
//length of current code
volatile uint16_t ir_curCodeLen=0;
//index into current code
volatile uint16_t ir_curCodeIdx=0;
//ir tick counter
volatile uint8_t ir_pulse_length=0;
//store which pin will be set next high/low
volatile uint8_t ir_port_buffer=0;
//store the code here
uint8_t code[IR_MAX_SRAM];
//setup timer1 for frequency generation
//our frequency is stored in freq
//timer1 will be in CTC mode and toggle outputs on compare match,
//the timer base frequency will be the full ioclk
//this function seta the ir_pulse_length variable
//ir_pulse_length is et to configure TIMER0
//to match the code specific bit pulse length
//pulselength must be > 0
//0 means an error has occured
uint8_t ir_freqInit(uint16_t freq, uint16_t pulselength)
{
//disable frequency output
FREQGEN_OFF();
//timer1 TOP = freq, we're setting IRPORT high on OCR1B match, low on OCR1A match, PWM :)
OCR1A = freq; //store freq in OCR1A
OCR1B =(uint16_t)((float)freq - (((float)freq) *((float)IR_pulse_width))); //store pulse length in OCR1B
//toggle OC1A on match - not needet here only doku
//TCCR1A = _BV(COM1A0);
//enable Timer1 OC1A,OC1B match interrupt, for pulsing the IR LEDs
TIMSK|=_BV(OCIE1A)|_BV(OCIE1B);
//enable TIMER0 Overflow interrupt
TIMSK|= _BV(TOIE0);
//check if pulse_length is in range
if((pulselength < F_CPU/0x4000) && (pulselength > 0))
{
ir_pulse_length=0xFF-((uint8_t)((F_CPU/64E6) *(pulselength)+1)); //calculate advanced prescaler
}
else
{
return 0; //error return 0
}
return 1; //no error return 1
}
//toggle output pins and send code
ISR(TIMER1_COMPA_vect)
{
//disable all pins on IRPORT that are set to output
IRPORT&=~IRUSEDPORTS;
//softhack: enable Timer0_ovf_int
TIMSK|= _BV(TOIE0);
}
//toggle output pins and send code
ISR(TIMER1_COMPB_vect)
{
//enable all pins on IRPORT that are set to output
//make sure its only the one we want to use
IRPORT|=(ir_port_buffer&IRUSEDPORTS);
//softhack: disable Timer0_ovf_int
TIMSK&= ~_BV(TOIE0);
}
//modulate the pulses generated by TIMER1
ISR(TIMER0_OVF_vect)
{
//update index counter
ir_curCodeIdx++;
//check if the code has been send
if(ir_curCodeIdx >= ir_curCodeLen)
{
ir_disable(); //disable everything
}
else
{
//load the new value into the port_buffer
ir_port_buffer = code[ir_curCodeIdx];
#if IR_DEBUG == 1
if((ir_port_buffer & 1)>0)
PORTC|=4;
else
PORTC&=~4;
#endif
//restore the counter buffer
TCNT0=ir_pulse_length;
}
}
//disable code output
void ir_disable(void)
{
FREQGEN_OFF();
//disable ir code generator
ir_curCodeLen = 0;
ir_curCodeIdx = 0;
TCCR0=0; //turn off Timer0
TIMSK&=~ _BV(TOIE0); //disable TIMER0 Overflow interrupt
IRPORT&=~IRUSEDPORTS; //set low level on used ports, just for sure
asm("nop\t\n");
asm("nop\t\n");
}
//this function converts a bit-encoded code into
//the internal format used by the ir sending function
//
//channel = 0..7
//headerCode <binary encoded> bit 1 will sent light pulses for setpulselength(value)
//headerLen = 1..32
//
//return headerLen
uint8_t ir_genHeader(uint8_t channel, uint32_t headerCode, uint8_t headerLen)
{
//more checks
if((channel > 7) || (headerLen == 0) || (headerLen > 32) ) //TODO calculate memory usage
return 0;
uint8_t i=0;
i = headerLen;
while(i--)
{
if(headerCode & 1)
{
//encode a one
code[i] |= (1<<channel);
}
else
{
//encode a zero
code[i] &= ~(1<<channel);
}
headerCode >>= 1;
}
return headerLen;
}
//this function converts a bit-encoded code into
//the internal format used by the ir sending function
//it takes an array where to place the code (must be large enough),
//the bitcode is assumed to be in MSB first format
//
//the function will return the length of the generated
//code
//channel = 0..7
//oneCode <binary encoded> bit 1 will sent light pulses for setpulselength(value)
//zeroCode <binary encoded> bit 1 will sent light pulses for setpulselength(value)
//zeroCode_length=1..8
//oneCode_length=1..8
//codeLen = 1..32
//bitCode <binary encoded> bit 1 will be replaced by oneCode, bit 0 will be replaced by zeroCode
//bitCode must be > 0
//0 means an error has occured
uint16_t ir_genCode(uint8_t headerlength, uint8_t channel, uint8_t oneCode, uint8_t oneCode_length, uint8_t zeroCode,uint8_t zeroCode_length, uint32_t bitCode, uint8_t codeLen)
{
//check if everything is errorfree
if((oneCode == 0) || (oneCode == zeroCode) || (zeroCode == 0) || (bitCode == 0) || (codeLen == 0)||(oneCode_length == 0)||(zeroCode_length == 0))
return 0;
//more checks
if((channel > 7) || (oneCode_length > 8) || ( zeroCode_length > 8)|| (codeLen > 32) ) //TODO calculate memory usage
return 0;
uint8_t i=codeLen,j=0;
uint16_t k=headerlength;
//convert bitcode
while(i--)
{
if(bitCode & 1)
{ //encode a one
//encode all bits
for(j=0;j<oneCode_length;j++)
{
if((oneCode & (1<<j))!=0)
{
code[k+(oneCode_length-j)] |= (1<<channel);
}
else
{
code[k+(oneCode_length-j)] &= ~(1<<channel);
}
}
k+=oneCode_length;
}
else
{ //encode a zero
//encode all bits
for(j=0;j<zeroCode_length;j++)
{
if((zeroCode & (1<<j))!=0)
{
code[k+(zeroCode_length-j)] |= (1<<channel);
}
else
{
code[k+(zeroCode_length-j)] &= ~(1<<channel);
}
}
k+=zeroCode_length;
}
//shift in next code bit
bitCode >>= 1;
}
//return real code length
return k;
}
//send an ir code, please never use a code length of zero
void ir_sendCode(uint16_t codeLen)
{
//we have to send 0 bytes ?
if(codeLen == 0)
return; //OK done :)
//turn off code sending completely while modifying the code
//turn of Timer1
FREQGEN_OFF();
//the last code to send may never be a zero
//-> it would not make any sense
//-> the frequency generation would give a carrier for one timer
// overflow after the last bit has been sent
//this makes even numbers the next smaller uneven ones
//ir_curCodeLen = (codeLen-1) | 0x01;
ir_curCodeLen = codeLen-1;
//reset timer count to be in sync
TCNT0=ir_pulse_length;
//enable code sending by setting the index to zero
ir_curCodeIdx = 0;
ir_port_buffer =0;
//enable frequency generation
FREQGEN_ON();
//enable code generator
TCCR0=_BV(CS01)|_BV(CS00); //enable Timer0, prescaler 1:64
//enable TIMER0 Overflow interrupt
TIMSK|= _BV(TOIE0);
}
//returns 1 if transmission is pending
//else 0
uint8_t transmission_pending(void)
{
if(((TCCR1B & _BV(CS10))> 0 )&& (ir_curCodeLen > 0))
return 1;
return 0;
}
//all-in-one initialization
//returns 0 on error
uint8_t ir_init(uint16_t freq, uint16_t pulselength)
{
//disable ir code generator
ir_disable();
//ir LED output - NPN tansistor needs low output or LED will die - overcurrent driven
IRDDR|=IRUSEDPORTS; //set used ports to output
IRPORT&=~IRUSEDPORTS; //set low level on used ports
//enable frequency generator, constant frequency
//move this to main()
return ir_freqInit(freq,pulselength);
}