The-Legend-of-Delta/Repetier Firmware/Repetier/HAL.cpp

1282 lines
44 KiB
C++

#include "Repetier.h"
#include <compat/twi.h>
//extern "C" void __cxa_pure_virtual() { }
HAL::HAL()
{
//ctor
}
HAL::~HAL()
{
//dtor
}
uint16_t HAL::integerSqrt(uint32_t a)
{
// http://www.mikrocontroller.net/articles/AVR_Arithmetik#32_Bit_.2F_32_Bit
//-----------------------------------------------------------
// Fast and short 32 bits AVR sqrt routine, avr-gcc ABI compliant
// R25:R24 = SQRT (R25:R24:R23:R22) rounded to the
// nearest integer (0.5 rounds up)
// Destroys R18-R19,R22-R23,R26-R27
// Cycles incl call & ret = 265-310
// Stack incl call = 2-3
//-----------------------------------------------------------
uint16_t b;
__asm__ __volatile__ (
"ldi R19, 0xc0 \n\t"
"clr R18 \n\t" // rotation mask in R19:R18
"ldi R27, 0x40 \n\t"
"sub R26, R26 \n\t" // developing sqrt in R27:R26, C=0
"1: brcs 2f \n\t" // C --> Bit is always 1
"cp %C1, R26 \n\t"
"cpc %D1, R27 \n\t" // Does test value fit?
"brcs 3f \n\t" // C --> nope, bit is 0
"2: sub %C1, R26 \n\t"
"sbc %D1, R27 \n\t" // Adjust argument for next bit
"or R26, R18 \n\t"
"or R27, R19 \n\t" // Set bit to 1
"3: lsr R19 \n\t"
"ror R18 \n\t" // Shift right mask, C --> end loop
"eor R27, R19 \n\t"
"eor R26, R18 \n\t" // Shift right only test bit in result
"rol %A1 \n\t" // Bit 0 only set if end of loop
"rol %B1 \n\t"
"rol %C1 \n\t"
"rol %D1 \n\t" // Shift left remaining argument (C used at 1:)
"sbrs %A1, 0 \n\t" // Skip if 15 bits developed
"rjmp 1b \n\t" // Develop 15 bits of the sqrt
"brcs 4f \n\t" // C--> Last bits always 1
"cp R26, %C1 \n\t"
"cpc R27, %D1 \n\t" // Test for last bit 1
"brcc 5f \n\t" // NC --> bit is 0
"4: sbc %B1, R19 \n\t" // Subtract C (any value from 1 to 0x7f will do)
"sbc %C1, R26 \n\t"
"sbc %D1, R27 \n\t" // Update argument for test
"inc R26 \n\t" // Last bit is 1
"5: lsl %B1 \n\t" // Only bit 7 matters
"rol %C1 \n\t"
"rol %D1 \n\t" // Remainder * 2 + C
"brcs 6f \n\t" // C --> Always round up
"cp R26, %C1 \n\t"
"cpc R27, %D1 \n\t" // C decides rounding
"6: adc R26, R19 \n\t"
"adc R27, R19 \n\t" // Round up if C (R19=0)
"mov %B0, R27 \n\t" // return in R25:R24 for avr-gcc ABI compliance
"mov %A0, R26 \n\t"
:"=r"(b)
:"r"(a)
:"r18","r19","r27","r26" );
return b;
}
const uint16_t fast_div_lut[17] PROGMEM = {0,F_CPU/4096,F_CPU/8192,F_CPU/12288,F_CPU/16384,F_CPU/20480,F_CPU/24576,F_CPU/28672,F_CPU/32768,F_CPU/36864
,F_CPU/40960,F_CPU/45056,F_CPU/49152,F_CPU/53248,F_CPU/57344,F_CPU/61440,F_CPU/65536
};
const uint16_t slow_div_lut[257] PROGMEM = {0,0,0,0,0,0,0,0,F_CPU/256,F_CPU/288,F_CPU/320,F_CPU/352
,F_CPU/384,F_CPU/416,F_CPU/448,F_CPU/480,F_CPU/512,F_CPU/544,F_CPU/576,F_CPU/608,F_CPU/640,F_CPU/672,F_CPU/704,F_CPU/736,F_CPU/768,F_CPU/800,F_CPU/832
,F_CPU/864,F_CPU/896,F_CPU/928,F_CPU/960,F_CPU/992,F_CPU/1024,F_CPU/1056,F_CPU/1088,F_CPU/1120,F_CPU/1152,F_CPU/1184,F_CPU/1216,F_CPU/1248,F_CPU/1280,F_CPU/1312
,F_CPU/1344,F_CPU/1376,F_CPU/1408,F_CPU/1440,F_CPU/1472,F_CPU/1504,F_CPU/1536,F_CPU/1568,F_CPU/1600,F_CPU/1632,F_CPU/1664,F_CPU/1696,F_CPU/1728,F_CPU/1760,F_CPU/1792
,F_CPU/1824,F_CPU/1856,F_CPU/1888,F_CPU/1920,F_CPU/1952,F_CPU/1984,F_CPU/2016
,F_CPU/2048,F_CPU/2080,F_CPU/2112,F_CPU/2144,F_CPU/2176,F_CPU/2208,F_CPU/2240,F_CPU/2272,F_CPU/2304,F_CPU/2336,F_CPU/2368,F_CPU/2400
,F_CPU/2432,F_CPU/2464,F_CPU/2496,F_CPU/2528,F_CPU/2560,F_CPU/2592,F_CPU/2624,F_CPU/2656,F_CPU/2688,F_CPU/2720,F_CPU/2752,F_CPU/2784,F_CPU/2816,F_CPU/2848,F_CPU/2880
,F_CPU/2912,F_CPU/2944,F_CPU/2976,F_CPU/3008,F_CPU/3040,F_CPU/3072,F_CPU/3104,F_CPU/3136,F_CPU/3168,F_CPU/3200,F_CPU/3232,F_CPU/3264,F_CPU/3296,F_CPU/3328,F_CPU/3360
,F_CPU/3392,F_CPU/3424,F_CPU/3456,F_CPU/3488,F_CPU/3520,F_CPU/3552,F_CPU/3584,F_CPU/3616,F_CPU/3648,F_CPU/3680,F_CPU/3712,F_CPU/3744,F_CPU/3776,F_CPU/3808,F_CPU/3840
,F_CPU/3872,F_CPU/3904,F_CPU/3936,F_CPU/3968,F_CPU/4000,F_CPU/4032,F_CPU/4064
,F_CPU/4096,F_CPU/4128,F_CPU/4160,F_CPU/4192,F_CPU/4224,F_CPU/4256,F_CPU/4288,F_CPU/4320,F_CPU/4352,F_CPU/4384,F_CPU/4416,F_CPU/4448,F_CPU/4480,F_CPU/4512,F_CPU/4544
,F_CPU/4576,F_CPU/4608,F_CPU/4640,F_CPU/4672,F_CPU/4704,F_CPU/4736,F_CPU/4768,F_CPU/4800,F_CPU/4832,F_CPU/4864,F_CPU/4896,F_CPU/4928,F_CPU/4960,F_CPU/4992,F_CPU/5024
,F_CPU/5056,F_CPU/5088,F_CPU/5120,F_CPU/5152,F_CPU/5184,F_CPU/5216,F_CPU/5248,F_CPU/5280,F_CPU/5312,F_CPU/5344,F_CPU/5376,F_CPU/5408,F_CPU/5440,F_CPU/5472,F_CPU/5504
,F_CPU/5536,F_CPU/5568,F_CPU/5600,F_CPU/5632,F_CPU/5664,F_CPU/5696,F_CPU/5728,F_CPU/5760,F_CPU/5792,F_CPU/5824,F_CPU/5856,F_CPU/5888,F_CPU/5920,F_CPU/5952,F_CPU/5984
,F_CPU/6016,F_CPU/6048,F_CPU/6080,F_CPU/6112,F_CPU/6144,F_CPU/6176,F_CPU/6208,F_CPU/6240,F_CPU/6272,F_CPU/6304,F_CPU/6336,F_CPU/6368,F_CPU/6400,F_CPU/6432,F_CPU/6464
,F_CPU/6496,F_CPU/6528,F_CPU/6560,F_CPU/6592,F_CPU/6624,F_CPU/6656,F_CPU/6688,F_CPU/6720,F_CPU/6752,F_CPU/6784,F_CPU/6816,F_CPU/6848,F_CPU/6880,F_CPU/6912,F_CPU/6944
,F_CPU/6976,F_CPU/7008,F_CPU/7040,F_CPU/7072,F_CPU/7104,F_CPU/7136,F_CPU/7168,F_CPU/7200,F_CPU/7232,F_CPU/7264,F_CPU/7296,F_CPU/7328,F_CPU/7360,F_CPU/7392,F_CPU/7424
,F_CPU/7456,F_CPU/7488,F_CPU/7520,F_CPU/7552,F_CPU/7584,F_CPU/7616,F_CPU/7648,F_CPU/7680,F_CPU/7712,F_CPU/7744,F_CPU/7776,F_CPU/7808,F_CPU/7840,F_CPU/7872,F_CPU/7904
,F_CPU/7936,F_CPU/7968,F_CPU/8000,F_CPU/8032,F_CPU/8064,F_CPU/8096,F_CPU/8128,F_CPU/8160,F_CPU/8192
};
/** \brief approximates division of F_CPU/divisor
In the stepper interrupt a division is needed, which is a slow operation.
The result is used for timer calculation where small errors are ok. This
function uses lookup tables to find a fast approximation of the result.
*/
int32_t HAL::CPUDivU2(unsigned int divisor)
{
#if CPU_ARCH==ARCH_AVR
int32_t res;
unsigned short table;
if(divisor<8192)
{
if(divisor<512)
{
if(divisor<10) divisor = 10;
return Div4U2U(F_CPU,divisor); // These entries have overflows in lookuptable!
}
table = (unsigned short)&slow_div_lut[0];
__asm__ __volatile__( // needs 64 ticks neu 49 Ticks
"mov r18,%A1 \n\t"
"andi r18,31 \n\t" // divisor & 31 in r18
"lsr %B1 \n\t" // divisor >> 4
"ror %A1 \n\t"
"lsr %B1 \n\t"
"ror %A1 \n\t"
"lsr %B1 \n\t"
"ror %A1 \n\t"
"lsr %B1 \n\t"
"ror %A1 \n\t"
"andi %A1,254 \n\t"
"add %A2,%A1 \n\t" // table+divisor>>3
"adc %B2,%B1 \n\t"
"lpm %A0,Z+ \n\t" // y0 in res
"lpm %B0,Z+ \n\t" // %C0,%D0 are 0
"movw r4,%A0 \n\t" // y0 nach gain (r4-r5)
"lpm r0,Z+ \n\t" // gain = gain-y1
"sub r4,r0 \n\t"
"lpm r0,Z+ \n\t"
"sbc r5,r0 \n\t"
"mul r18,r4 \n\t" // gain*(divisor & 31)
"movw %A1,r0 \n\t" // divisor not needed any more, use for byte 0,1 of result
"mul r18,r5 \n\t"
"add %B1,r0 \n\t"
"mov %A2,r1 \n\t"
"lsl %A1 \n\t"
"rol %B1 \n\t"
"rol %A2 \n\t"
"lsl %A1 \n\t"
"rol %B1 \n\t"
"rol %A2 \n\t"
"lsl %A1 \n\t"
"rol %B1 \n\t"
"rol %A2 \n\t"
"sub %A0,%B1 \n\t"
"sbc %B0,%A2 \n\t"
"clr %C0 \n\t"
"clr %D0 \n\t"
"clr r1 \n\t"
: "=&r" (res),"=&d"(divisor),"=&z"(table) : "1"(divisor),"2"(table) : "r18","r4","r5");
return res;
/*unsigned short adr0 = (unsigned short)&slow_div_lut+(divisor>>4)&1022;
long y0= pgm_read_dword_near(adr0);
long gain = y0-pgm_read_dword_near(adr0+2);
return y0-((gain*(divisor & 31))>>5);*/
}
else
{
table = (unsigned short)&fast_div_lut[0];
__asm__ __volatile__( // needs 49 ticks
"movw r18,%A1 \n\t"
"andi r19,15 \n\t" // divisor & 4095 in r18,r19
"lsr %B1 \n\t" // divisor >> 3, then %B1 is 2*(divisor >> 12)
"lsr %B1 \n\t"
"lsr %B1 \n\t"
"andi %B1,254 \n\t"
"add %A2,%B1 \n\t" // table+divisor>>11
"adc %B2,r1 \n\t" //
"lpm %A0,Z+ \n\t" // y0 in res
"lpm %B0,Z+ \n\t"
"movw r4,%A0 \n\t" // y0 to gain (r4-r5)
"lpm r0,Z+ \n\t" // gain = gain-y1
"sub r4,r0 \n\t"
"lpm r0,Z+ \n\t"
"sbc r5,r0 \n\t" // finished - result has max. 16 bit
"mul r18,r4 \n\t" // gain*(divisor & 4095)
"movw %A1,r0 \n\t" // divisor not needed any more, use for byte 0,1 of result
"mul r19,r5 \n\t"
"mov %A2,r0 \n\t" // %A2 = byte 3 of result
"mul r18,r5 \n\t"
"add %B1,r0 \n\t"
"adc %A2,r1 \n\t"
"mul r19,r4 \n\t"
"add %B1,r0 \n\t"
"adc %A2,r1 \n\t"
"andi %B1,240 \n\t" // >> 12
"swap %B1 \n\t"
"swap %A2 \r\n"
"mov %A1,%A2 \r\n"
"andi %A1,240 \r\n"
"or %B1,%A1 \r\n"
"andi %A2,15 \r\n"
"sub %A0,%B1 \n\t"
"sbc %B0,%A2 \n\t"
"clr %C0 \n\t"
"clr %D0 \n\t"
"clr r1 \n\t"
: "=&r" (res),"=&d"(divisor),"=&z"(table) : "1"(divisor),"2"(table) : "r18","r19","r4","r5");
return res;
/*
// The asm mimics the following code
unsigned short adr0 = (unsigned short)&fast_div_lut+(divisor>>11)&254;
unsigned short y0= pgm_read_word_near(adr0);
unsigned short gain = y0-pgm_read_word_near(adr0+2);
return y0-(((long)gain*(divisor & 4095))>>12);*/
}
#else
return F_CPU/divisor;
#endif
}
void HAL::setupTimer()
{
#if USE_ADVANCE
EXTRUDER_TCCR = 0; // need Normal not fastPWM set by arduino init
EXTRUDER_TIMSK |= (1<<EXTRUDER_OCIE); // Activate compa interrupt on timer 0
#endif
PWM_TCCR = 0; // Setup PWM interrupt
PWM_OCR = 64;
PWM_TIMSK |= (1<<PWM_OCIE);
TCCR1A = 0; // Steup timer 1 interrupt to no prescale CTC mode
TCCR1C = 0;
TIMSK1 = 0;
TCCR1B = (_BV(WGM12) | _BV(CS10)); // no prescaler == 0.0625 usec tick | 001 = clk/1
OCR1A=65500; //start off with a slow frequency.
TIMSK1 |= (1<<OCIE1A); // Enable interrupt
#if FEATURE_SERVO
#if SERVO0_PIN>-1
SET_OUTPUT(SERVO0_PIN);
WRITE(SERVO0_PIN,LOW);
#endif
#if SERVO1_PIN>-1
SET_OUTPUT(SERVO1_PIN);
WRITE(SERVO1_PIN,LOW);
#endif
#if SERVO2_PIN>-1
SET_OUTPUT(SERVO2_PIN);
WRITE(SERVO2_PIN,LOW);
#endif
#if SERVO3_PIN>-1
SET_OUTPUT(SERVO3_PIN);
WRITE(SERVO3_PIN,LOW);
#endif
TCCR3A = 0; // normal counting mode
TCCR3B = _BV(CS31); // set prescaler of 8
TCNT3 = 0; // clear the timer count
#if defined(__AVR_ATmega128__)
TIFR |= _BV(OCF3A); // clear any pending interrupts;
ETIMSK |= _BV(OCIE3A); // enable the output compare interrupt
#else
TIFR3 = _BV(OCF3A); // clear any pending interrupts;
TIMSK3 = _BV(OCIE3A) ; // enable the output compare interrupt
#endif
#endif
}
void HAL::showStartReason()
{
// Check startup - does nothing if bootloader sets MCUSR to 0
uint8_t mcu = MCUSR;
if(mcu & 1) Com::printInfoFLN(Com::tPowerUp);
if(mcu & 2) Com::printInfoFLN(Com::tExternalReset);
if(mcu & 4) Com::printInfoFLN(Com::tBrownOut);
if(mcu & 8) Com::printInfoFLN(Com::tWatchdog);
if(mcu & 32) Com::printInfoFLN(Com::tSoftwareReset);
MCUSR=0;
}
int HAL::getFreeRam()
{
int freeram = 0;
InterruptProtectedBlock noInts;
uint8_t * heapptr, * stackptr;
heapptr = (uint8_t *)malloc(4); // get heap pointer
free(heapptr); // free up the memory again (sets heapptr to 0)
stackptr = (uint8_t *)(SP); // save value of stack pointer
freeram = (int)stackptr-(int)heapptr;
return freeram;
}
void(* resetFunc) (void) = 0; //declare reset function @ address 0
void HAL::resetHardware()
{
resetFunc();
}
void HAL::analogStart()
{
#if ANALOG_INPUTS > 0
ADMUX = ANALOG_REF; // refernce voltage
for(uint8_t i=0; i<ANALOG_INPUTS; i++)
{
osAnalogInputCounter[i] = 0;
osAnalogInputBuildup[i] = 0;
osAnalogInputValues[i] = 0;
}
ADCSRA = _BV(ADEN)|_BV(ADSC)|ANALOG_PRESCALER;
//ADCSRA |= _BV(ADSC); // start ADC-conversion
while (ADCSRA & _BV(ADSC) ) {} // wait for conversion
/* ADCW must be read once, otherwise the next result is wrong. */
uint dummyADCResult;
dummyADCResult = ADCW;
// Enable interrupt driven conversion loop
uint8_t channel = pgm_read_byte(&osAnalogInputChannels[osAnalogInputPos]);
#if defined(ADCSRB) && defined(MUX5)
if(channel & 8) // Reading channel 0-7 or 8-15?
ADCSRB |= _BV(MUX5);
else
ADCSRB &= ~_BV(MUX5);
#endif
ADMUX = (ADMUX & ~(0x1F)) | (channel & 7);
ADCSRA |= _BV(ADSC); // start conversion without interrupt!
#endif
}
/*************************************************************************
* Title: I2C master library using hardware TWI interface
* Author: Peter Fleury <pfleury@gmx.ch> http://jump.to/fleury
* File: $Id: twimaster.c,v 1.3 2005/07/02 11:14:21 Peter Exp $
* Software: AVR-GCC 3.4.3 / avr-libc 1.2.3
* Target: any AVR device with hardware TWI
* Usage: API compatible with I2C Software Library i2cmaster.h
**************************************************************************/
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 304
#error "This library requires AVR-GCC 3.4 or later, update to newer AVR-GCC compiler !"
#endif
#include <avr/io.h>
/*************************************************************************
Initialization of the I2C bus interface. Need to be called only once
*************************************************************************/
void HAL::i2cInit(unsigned long clockSpeedHz)
{
/* initialize TWI clock: 100 kHz clock, TWPS = 0 => prescaler = 1 */
TWSR = 0; /* no prescaler */
TWBR = ((F_CPU/clockSpeedHz)-16)/2; /* must be > 10 for stable operation */
}
/*************************************************************************
Issues a start condition and sends address and transfer direction.
return 0 = device accessible, 1= failed to access device
*************************************************************************/
unsigned char HAL::i2cStart(unsigned char address)
{
uint8_t twst;
// send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait until transmission completed
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START)) return 1;
// send device address
TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN);
// wail until transmission completed and ACK/NACK has been received
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ) return 1;
return 0;
}
/*************************************************************************
Issues a start condition and sends address and transfer direction.
If device is busy, use ack polling to wait until device is ready
Input: address and transfer direction of I2C device
*************************************************************************/
void HAL::i2cStartWait(unsigned char address)
{
uint8_t twst;
while ( 1 )
{
// send START condition
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
// wait until transmission completed
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst != TW_START) && (twst != TW_REP_START)) continue;
// send device address
TWDR = address;
TWCR = (1<<TWINT) | (1<<TWEN);
// wail until transmission completed
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits.
twst = TW_STATUS & 0xF8;
if ( (twst == TW_MT_SLA_NACK )||(twst ==TW_MR_DATA_NACK) )
{
/* device busy, send stop condition to terminate write operation */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
// wait until stop condition is executed and bus released
while(TWCR & (1<<TWSTO));
continue;
}
//if( twst != TW_MT_SLA_ACK) return 1;
break;
}
}
/*************************************************************************
Terminates the data transfer and releases the I2C bus
*************************************************************************/
void HAL::i2cStop(void)
{
/* send stop condition */
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
// wait until stop condition is executed and bus released
while(TWCR & (1<<TWSTO));
}
/*************************************************************************
Send one byte to I2C device
Input: byte to be transfered
Return: 0 write successful
1 write failed
*************************************************************************/
unsigned char HAL::i2cWrite( unsigned char data )
{
uint8_t twst;
// send data to the previously addressed device
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
// wait until transmission completed
while(!(TWCR & (1<<TWINT)));
// check value of TWI Status Register. Mask prescaler bits
twst = TW_STATUS & 0xF8;
if( twst != TW_MT_DATA_ACK) return 1;
return 0;
}
/*************************************************************************
Read one byte from the I2C device, request more data from device
Return: byte read from I2C device
*************************************************************************/
unsigned char HAL::i2cReadAck(void)
{
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}
/*************************************************************************
Read one byte from the I2C device, read is followed by a stop condition
Return: byte read from I2C device
*************************************************************************/
unsigned char HAL::i2cReadNak(void)
{
TWCR = (1<<TWINT) | (1<<TWEN);
while(!(TWCR & (1<<TWINT)));
return TWDR;
}
#if FEATURE_SERVO
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__) || defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
#define SERVO2500US F_CPU/3200
#define SERVO5000US F_CPU/1600
unsigned int HAL::servoTimings[4] = {0,0,0,0};
static uint8_t servoIndex = 0;
void HAL::servoMicroseconds(uint8_t servo,int ms)
{
if(ms<500) ms = 0;
if(ms>2500) ms = 2500;
servoTimings[servo] = (unsigned int)(((F_CPU/1000000)*(long)ms)>>3);
}
SIGNAL (TIMER3_COMPA_vect)
{
switch(servoIndex)
{
case 0:
TCNT3 = 0;
if(HAL::servoTimings[0])
{
#if SERVO0_PIN>-1
WRITE(SERVO0_PIN,HIGH);
#endif
OCR3A = HAL::servoTimings[0];
}
else OCR3A = SERVO2500US;
break;
case 1:
#if SERVO0_PIN>-1
WRITE(SERVO0_PIN,LOW);
#endif
OCR3A = SERVO5000US;
break;
case 2:
TCNT3 = 0;
if(HAL::servoTimings[1])
{
#if SERVO1_PIN>-1
WRITE(SERVO1_PIN,HIGH);
#endif
OCR3A = HAL::servoTimings[1];
}
else OCR3A = SERVO2500US;
break;
case 3:
#if SERVO1_PIN>-1
WRITE(SERVO1_PIN,LOW);
#endif
OCR3A = SERVO5000US;
break;
case 4:
TCNT3 = 0;
if(HAL::servoTimings[2])
{
#if SERVO2_PIN>-1
WRITE(SERVO2_PIN,HIGH);
#endif
OCR3A = HAL::servoTimings[2];
}
else OCR3A = SERVO2500US;
break;
case 5:
#if SERVO2_PIN>-1
WRITE(SERVO2_PIN,LOW);
#endif
OCR3A = SERVO5000US;
break;
case 6:
TCNT3 = 0;
if(HAL::servoTimings[3])
{
#if SERVO3_PIN>-1
WRITE(SERVO3_PIN,HIGH);
#endif
OCR3A = HAL::servoTimings[3];
}
else OCR3A = SERVO2500US;
break;
case 7:
#if SERVO3_PIN>-1
WRITE(SERVO3_PIN,LOW);
#endif
OCR3A = SERVO5000US;
break;
}
servoIndex++;
if(servoIndex>7)
servoIndex = 0;
}
#else
#error No servo support for your board, please diable FEATURE_SERVO
#endif
#endif
// ================== Interrupt handling ======================
/** \brief Sets the timer 1 compare value to delay ticks.
This function sets the OCR1A compare counter to get the next interrupt
at delay ticks measured from the last interrupt. delay must be << 2^24
*/
inline void setTimer(uint32_t delay)
{
__asm__ __volatile__ (
"cli \n\t"
"tst %C[delay] \n\t" //if(delay<65536) {
"brne else%= \n\t"
"cpi %B[delay],255 \n\t"
"breq else%= \n\t" // delay <65280
"sts stepperWait,r1 \n\t" // stepperWait = 0;
"sts stepperWait+1,r1 \n\t"
"sts stepperWait+2,r1 \n\t"
"lds %C[delay],%[time] \n\t" // Read TCNT1
"lds %D[delay],%[time]+1 \n\t"
"ldi r18,100 \n\t" // Add 100 to TCNT1
"add %C[delay],r18 \n\t"
"adc %D[delay],r1 \n\t"
"cp %A[delay],%C[delay] \n\t" // delay<TCNT1+1
"cpc %B[delay],%D[delay] \n\t"
"brcc exact%= \n\t"
"sts %[ocr]+1,%D[delay] \n\t" // OCR1A = TCNT1+100;
"sts %[ocr],%C[delay] \n\t"
"rjmp end%= \n\t"
"exact%=: sts %[ocr]+1,%B[delay] \n\t" // OCR1A = delay;
"sts %[ocr],%A[delay] \n\t"
"rjmp end%= \n\t"
"else%=: subi %B[delay], 0x80 \n\t" //} else { stepperWait = delay-32768;
"sbci %C[delay], 0x00 \n\t"
"sts stepperWait,%A[delay] \n\t"
"sts stepperWait+1,%B[delay] \n\t"
"sts stepperWait+2,%C[delay] \n\t"
"ldi %D[delay], 0x80 \n\t" //OCR1A = 32768;
"sts %[ocr]+1, %D[delay] \n\t"
"sts %[ocr], r1 \n\t"
"end%=: \n\t"
:[delay]"=&d"(delay) // Output
:"0"(delay),[ocr]"i" (_SFR_MEM_ADDR(OCR1A)),[time]"i"(_SFR_MEM_ADDR(TCNT1)) // Input
:"r18" // Clobber
);
/* // Assembler above replaced this code
if(delay<65280) {
stepperWait = 0;
unsigned int count = TCNT1+100;
if(delay<count)
OCR1A = count;
else
OCR1A = delay;
} else {
stepperWait = delay-32768;
OCR1A = 32768;
}*/
}
volatile uint8_t insideTimer1 = 0;
long stepperWait = 0;
/** \brief Timer interrupt routine to drive the stepper motors.
*/
ISR(TIMER1_COMPA_vect)
{
if(insideTimer1) return;
uint8_t doExit;
__asm__ __volatile__ (
"ldi %[ex],0 \n\t"
"lds r23,stepperWait+2 \n\t"
"tst r23 \n\t" //if(stepperWait<65536) {
"brne else%= \n\t" // Still > 65535
"lds r23,stepperWait+1 \n\t"
"tst r23 \n\t"
"brne last%= \n\t" // Still not 0, go ahead
"lds r22,stepperWait \n\t"
"breq end%= \n\t" // stepperWait is 0, do your work
"last%=: \n\t"
"sts %[ocr]+1,r23 \n\t" // OCR1A = stepper wait;
"sts %[ocr],r22 \n\t"
"sts stepperWait,r1 \n\t"
"sts stepperWait+1,r1 \n\t"
"rjmp end1%= \n\t"
"else%=: lds r22,stepperWait+1 \n\t" //} else { stepperWait = stepperWait-32768;
"subi r22, 0x80 \n\t"
"sbci r23, 0x00 \n\t"
"sts stepperWait+1,r22 \n\t" // ocr1a stays 32768
"sts stepperWait+2,r23 \n\t"
"end1%=: ldi %[ex],1 \n\t"
"end%=: \n\t"
:[ex]"=&d"(doExit):[ocr]"i" (_SFR_MEM_ADDR(OCR1A)):"r22","r23" );
if(doExit) return;
insideTimer1 = 1;
OCR1A = 61000;
if(PrintLine::hasLines())
{
setTimer(PrintLine::bresenhamStep());
}
else
if(FEATURE_BABYSTEPPING && Printer::zBabystepsMissing) {
Printer::zBabystep();
setTimer(Printer::interval);
} else
{
if(waitRelax == 0)
{
#if USE_ADVANCE
if(Printer::advanceStepsSet)
{
Printer::extruderStepsNeeded -= Printer::advanceStepsSet;
#if ENABLE_QUADRATIC_ADVANCE
Printer::advanceExecuted = 0;
#endif
Printer::advanceStepsSet = 0;
}
#endif
#if USE_ADVANCE
if(!Printer::extruderStepsNeeded) if(DISABLE_E) Extruder::disableCurrentExtruderMotor();
#else
if(DISABLE_E) Extruder::disableCurrentExtruderMotor();
#endif
}
else waitRelax--;
stepperWait = 0; // Importent becaus of optimization in asm at begin
OCR1A = 65500; // Wait for next move
}
DEBUG_MEMORY;
insideTimer1 = 0;
}
#if !defined(HEATER_PWM_SPEED)
#define HEATER_PWM_SPEED 0
#endif
#if HEATER_PWM_SPEED < 0
#define HEATER_PWM_SPEED 0
#endif
#if HEATER_PWM_SPEED > 3
#define HEATER_PWM_SPEED 3
#endif
#if HEATER_PWM_SPEED == 0
#define HEATER_PWM_STEP 1
#define HEATER_PWM_MASK 255
#elif HEATER_PWM_SPEED == 1
#define HEATER_PWM_STEP 2
#define HEATER_PWM_MASK 254
#elif HEATER_PWM_SPEED == 2
#define HEATER_PWM_STEP 4
#define HEATER_PWM_MASK 252
#else
#define HEATER_PWM_STEP 4
#define HEATER_PWM_MASK 252
#endif
#define pulseDensityModulate( pin, density,error,invert) {uint8_t carry;carry = error + (invert ? 255 - density : density); WRITE(pin, (carry < error)); error = carry;}
/**
This timer is called 3906 timer per second. It is used to update pwm values for heater and some other frequent jobs.
*/
ISR(PWM_TIMER_VECTOR)
{
static uint8_t pwm_count = 0;
static uint8_t pwm_count_heater = 0;
static uint8_t pwm_pos_set[NUM_EXTRUDER + 3];
static uint8_t pwm_cooler_pos_set[NUM_EXTRUDER];
PWM_OCR += 64;
if(pwm_count_heater == 0 && !PDM_FOR_EXTRUDER)
{
#if defined(EXT0_HEATER_PIN) && EXT0_HEATER_PIN > -1
if((pwm_pos_set[0] = (pwm_pos[0] & HEATER_PWM_MASK)) > 0) WRITE(EXT0_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN > -1 && NUM_EXTRUDER > 1
if((pwm_pos_set[1] = (pwm_pos[1] & HEATER_PWM_MASK)) > 0) WRITE(EXT1_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN > -1 && NUM_EXTRUDER > 2
if((pwm_pos_set[2] = (pwm_pos[2] & HEATER_PWM_MASK)) > 0) WRITE(EXT2_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN > -1 && NUM_EXTRUDER > 3
if((pwm_pos_set[3] = (pwm_pos[3] & HEATER_PWM_MASK)) > 0) WRITE(EXT3_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN > -1 && NUM_EXTRUDER > 4
if((pwm_pos_set[4] = (pwm_pos[4] & HEATER_PWM_MASK)) > 0) WRITE(EXT4_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN > -1 && NUM_EXTRUDER > 5
if((pwm_pos_set[5] = (pwm_pos[5] & HEATER_PWM_MASK)) > 0) WRITE(EXT5_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
#if HEATED_BED_HEATER_PIN > -1 && HAVE_HEATED_BED
if((pwm_pos_set[NUM_EXTRUDER] = pwm_pos[NUM_EXTRUDER]) > 0) WRITE(HEATED_BED_HEATER_PIN,!HEATER_PINS_INVERTED);
#endif
}
if(pwm_count == 0 && !PDM_FOR_COOLER)
{
#if defined(EXT0_HEATER_PIN) && EXT0_HEATER_PIN > -1 && EXT0_EXTRUDER_COOLER_PIN > -1
if((pwm_cooler_pos_set[0] = extruder[0].coolerPWM) > 0) WRITE(EXT0_EXTRUDER_COOLER_PIN, 1);
#endif
#if !SHARED_COOLER && defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN > -1 && NUM_EXTRUDER > 1
#if EXT1_EXTRUDER_COOLER_PIN > -1 && EXT1_EXTRUDER_COOLER_PIN != EXT0_EXTRUDER_COOLER_PIN
if((pwm_cooler_pos_set[1] = extruder[1].coolerPWM) > 0) WRITE(EXT1_EXTRUDER_COOLER_PIN, 1);
#endif
#endif
#if !SHARED_COOLER && defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN > -1 && NUM_EXTRUDER > 2
#if EXT2_EXTRUDER_COOLER_PIN > -1
if((pwm_cooler_pos_set[2] = extruder[2].coolerPWM) > 0) WRITE(EXT2_EXTRUDER_COOLER_PIN, 1);
#endif
#endif
#if !SHARED_COOLER && defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN > -1 && NUM_EXTRUDER > 3
#if EXT3_EXTRUDER_COOLER_PIN > -1
if((pwm_cooler_pos_set[3] = extruder[3].coolerPWM) > 0) WRITE(EXT3_EXTRUDER_COOLER_PIN, 1);
#endif
#endif
#if !SHARED_COOLER && defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN > -1 && NUM_EXTRUDER > 4
#if EXT4_EXTRUDER_COOLER_PIN > -1
if((pwm_cooler_pos_set[4] = pwm_pos[4].coolerPWM) > 0) WRITE(EXT4_EXTRUDER_COOLER_PIN, 1);
#endif
#endif
#if !SHARED_COOLER && defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN > -1 && NUM_EXTRUDER > 5
#if EXT5_EXTRUDER_COOLER_PIN > -1
if((pwm_cooler_pos_set[5] = extruder[5].coolerPWM) > 0) WRITE(EXT5_EXTRUDER_COOLER_PIN, 1);
#endif
#endif
#if FAN_BOARD_PIN > -1
if((pwm_pos_set[NUM_EXTRUDER + 1] = pwm_pos[NUM_EXTRUDER + 1]) > 0) WRITE(FAN_BOARD_PIN,1);
#endif
#if FAN_PIN > -1 && FEATURE_FAN_CONTROL
if((pwm_pos_set[NUM_EXTRUDER + 2] = pwm_pos[NUM_EXTRUDER + 2]) > 0) WRITE(FAN_PIN,1);
#endif
}
#if defined(EXT0_HEATER_PIN) && EXT0_HEATER_PIN > -1
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT0_HEATER_PIN, pwm_pos[0], pwm_pos_set[0], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[0] == pwm_count_heater && pwm_pos_set[0] != HEATER_PWM_MASK) WRITE(EXT0_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if EXT0_EXTRUDER_COOLER_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(EXT0_EXTRUDER_COOLER_PIN, extruder[0].coolerPWM, pwm_cooler_pos_set[0], false);
#else
if(pwm_cooler_pos_set[0] == pwm_count && pwm_cooler_pos_set[0] != 255) WRITE(EXT0_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if defined(EXT1_HEATER_PIN) && EXT1_HEATER_PIN > -1 && NUM_EXTRUDER > 1
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT1_HEATER_PIN, pwm_pos[1], pwm_pos_set[1], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[1] == pwm_count_heater && pwm_pos_set[1] != HEATER_PWM_MASK) WRITE(EXT1_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if !SHARED_COOLER && defined(EXT1_EXTRUDER_COOLER_PIN) && EXT1_EXTRUDER_COOLER_PIN > -1 && EXT1_EXTRUDER_COOLER_PIN != EXT0_EXTRUDER_COOLER_PIN
#if PDM_FOR_COOLER
pulseDensityModulate(EXT1_EXTRUDER_COOLER_PIN, extruder[1].coolerPWM, pwm_cooler_pos_set[1], false);
#else
if(pwm_cooler_pos_set[1] == pwm_count && pwm_cooler_pos_set[1] != 255) WRITE(EXT1_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if defined(EXT2_HEATER_PIN) && EXT2_HEATER_PIN>-1 && NUM_EXTRUDER>2
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT2_HEATER_PIN, pwm_pos[2], pwm_pos_set[2], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[2] == pwm_count_heater && pwm_pos_set[2]!=HEATER_PWM_MASK) WRITE(EXT2_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if !SHARED_COOLER && EXT2_EXTRUDER_COOLER_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(EXT2_EXTRUDER_COOLER_PIN, extruder[2].coolerPWM, pwm_cooler_pos_set[2], false);
#else
if(pwm_cooler_pos_set[2] == pwm_count && pwm_cooler_pos_set[2] != 255) WRITE(EXT2_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if defined(EXT3_HEATER_PIN) && EXT3_HEATER_PIN>-1 && NUM_EXTRUDER>3
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT3_HEATER_PIN, pwm_pos[3], pwm_pos_set[3], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[3] == pwm_count_heater && pwm_pos_set[3] != HEATER_PWM_MASK) WRITE(EXT3_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if !SHARED_COOLER && EXT3_EXTRUDER_COOLER_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(EXT3_EXTRUDER_COOLER_PIN, extruder[3].coolerPWM, pwm_cooler_pos_set[3], false);
#else
if(pwm_cooler_pos_set[3] == pwm_count && pwm_cooler_pos_set[3] != 255) WRITE(EXT3_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if defined(EXT4_HEATER_PIN) && EXT4_HEATER_PIN>-1 && NUM_EXTRUDER>4
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT4_HEATER_PIN, pwm_pos[4], pwm_pos_set[4], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[4] == pwm_count_heater && pwm_pos_set[4] != HEATER_PWM_MASK) WRITE(EXT4_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if !SHARED_COOLER && EXT4_EXTRUDER_COOLER_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(EXT4_EXTRUDER_COOLER_PIN, extruder[4].coolerPWM, pwm_cooler_pos_set[4], false);
#else
if(pwm_cooler_pos_set[4] == pwm_count && pwm_cooler_pos_set[4]!=255) WRITE(EXT4_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if defined(EXT5_HEATER_PIN) && EXT5_HEATER_PIN>-1 && NUM_EXTRUDER>5
#if PDM_FOR_EXTRUDER
pulseDensityModulate(EXT5_HEATER_PIN, pwm_pos[5], pwm_pos_set[5], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[5] == pwm_count_heater && pwm_pos_set[5] != HEATER_PWM_MASK) WRITE(EXT5_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#if !SHARED_COOLER && EXT5_EXTRUDER_COOLER_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(EXT5_EXTRUDER_COOLER_PIN, extruder[5].coolerPWM, pwm_cooler_pos_set[5], false);
#else
if(pwm_cooler_pos_set[5] == pwm_count && pwm_cooler_pos_set[5] != 255) WRITE(EXT5_EXTRUDER_COOLER_PIN,0);
#endif
#endif
#endif
#if FAN_BOARD_PIN > -1
#if PDM_FOR_COOLER
pulseDensityModulate(FAN_BOARD_PIN, pwm_pos[NUM_EXTRUDER + 1], pwm_pos_set[NUM_EXTRUDER + 1], false);
#else
if(pwm_pos_set[NUM_EXTRUDER + 1] == pwm_count && pwm_pos_set[NUM_EXTRUDER + 1] != 255) WRITE(FAN_BOARD_PIN,0);
#endif
#endif
#if FAN_PIN > -1 && FEATURE_FAN_CONTROL
#if PDM_FOR_COOLER
pulseDensityModulate(FAN_PIN, pwm_pos[NUM_EXTRUDER + 2], pwm_pos_set[NUM_EXTRUDER + 2], false);
#else
if(pwm_pos_set[NUM_EXTRUDER + 2] == pwm_count && pwm_pos_set[NUM_EXTRUDER + 2] != 255) WRITE(FAN_PIN,0);
#endif
#endif
#if HEATED_BED_HEATER_PIN > -1 && HAVE_HEATED_BED
#if PDM_FOR_EXTRUDER
pulseDensityModulate(HEATED_BED_HEATER_PIN, pwm_pos[NUM_EXTRUDER], pwm_pos_set[NUM_EXTRUDER], HEATER_PINS_INVERTED);
#else
if(pwm_pos_set[NUM_EXTRUDER] == pwm_count_heater && pwm_pos_set[NUM_EXTRUDER] != HEATER_PWM_MASK) WRITE(HEATED_BED_HEATER_PIN,HEATER_PINS_INVERTED);
#endif
#endif
HAL::allowInterrupts();
counterPeriodical++; // Appxoimate a 100ms timer
if(counterPeriodical >= (int)(F_CPU/40960))
{
counterPeriodical = 0;
executePeriodical = 1;
}
// read analog values
#if ANALOG_INPUTS > 0
if((ADCSRA & _BV(ADSC)) == 0) // Conversion finished?
{
osAnalogInputBuildup[osAnalogInputPos] += ADCW;
if(++osAnalogInputCounter[osAnalogInputPos]>=_BV(ANALOG_INPUT_SAMPLE))
{
#if ANALOG_INPUT_BITS + ANALOG_INPUT_SAMPLE < 12
osAnalogInputValues[osAnalogInputPos] =
osAnalogInputBuildup[osAnalogInputPos] <<
(12-ANALOG_INPUT_BITS-ANALOG_INPUT_SAMPLE);
#endif
#if ANALOG_INPUT_BITS + ANALOG_INPUT_SAMPLE > 12
osAnalogInputValues[osAnalogInputPos] =
osAnalogInputBuildup[osAnalogInputPos] >>
(ANALOG_INPUT_BITS+ANALOG_INPUT_SAMPLE-12);
#endif
#if ANALOG_INPUT_BITS + ANALOG_INPUT_SAMPLE == 12
osAnalogInputValues[osAnalogInputPos] =
osAnalogInputBuildup[osAnalogInputPos];
#endif
osAnalogInputBuildup[osAnalogInputPos] = 0;
osAnalogInputCounter[osAnalogInputPos] = 0;
// Start next conversion
if(++osAnalogInputPos>=ANALOG_INPUTS) osAnalogInputPos = 0;
uint8_t channel = pgm_read_byte(&osAnalogInputChannels[osAnalogInputPos]);
#if defined(ADCSRB) && defined(MUX5)
if(channel & 8) // Reading channel 0-7 or 8-15?
ADCSRB |= _BV(MUX5);
else
ADCSRB &= ~_BV(MUX5);
#endif
ADMUX = (ADMUX & ~(0x1F)) | (channel & 7);
}
ADCSRA |= _BV(ADSC); // start next conversion
}
#endif
UI_FAST; // Short timed user interface action
pwm_count++;
pwm_count_heater += HEATER_PWM_STEP;
}
#if USE_ADVANCE
static int8_t extruderLastDirection = 0;
void HAL::resetExtruderDirection() {
extruderLastDirection = 0;
}
/** \brief Timer routine for extruder stepper.
Several methods need to move the extruder. To get a optima result,
all methods update the printer_state.extruderStepsNeeded with the
number of additional steps needed. During this interrupt, one step
is executed. This will keep the extruder moving, until the total
wanted movement is achieved. This will be done with the maximum
allowable speed for the extruder.
*/
ISR(EXTRUDER_TIMER_VECTOR)
{
uint8_t timer = EXTRUDER_OCR;
if(!Printer::isAdvanceActivated()) return; // currently no need
if(Printer::extruderStepsNeeded > 0 && extruderLastDirection != 1)
{
Extruder::setDirection(true);
extruderLastDirection = 1;
timer += 40; // Add some more wait time to prevent blocking
}
else if(Printer::extruderStepsNeeded < 0 && extruderLastDirection != -1)
{
Extruder::setDirection(false);
extruderLastDirection = -1;
timer += 40; // Add some more wait time to prevent blocking
}
else if(Printer::extruderStepsNeeded != 0)
{
Extruder::step();
Printer::extruderStepsNeeded -= extruderLastDirection;
Printer::insertStepperHighDelay();
Extruder::unstep();
}
EXTRUDER_OCR = timer + Printer::maxExtruderSpeed;
}
#endif
#ifndef EXTERNALSERIAL
// Implement serial communication for one stream only!
/*
HardwareSerial.h - Hardware serial library for Wiring
Copyright (c) 2006 Nicholas Zambetti. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Modified 28 September 2010 by Mark Sproul
Modified to use only 1 queue with fixed length by Repetier
*/
ring_buffer rx_buffer = { { 0 }, 0, 0};
ring_buffer_tx tx_buffer = { { 0 }, 0, 0};
inline void rf_store_char(unsigned char c, ring_buffer *buffer)
{
uint8_t i = (buffer->head + 1) & SERIAL_BUFFER_MASK;
// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
if (i != buffer->tail)
{
buffer->buffer[buffer->head] = c;
buffer->head = i;
}
}
#if !defined(USART0_RX_vect) && defined(USART1_RX_vect)
// do nothing - on the 32u4 the first USART is USART1
#else
void rfSerialEvent() __attribute__((weak));
void rfSerialEvent() {}
#define serialEvent_implemented
#if defined(USART_RX_vect)
SIGNAL(USART_RX_vect)
#elif defined(USART0_RX_vect)
SIGNAL(USART0_RX_vect)
#else
#if defined(SIG_USART0_RECV)
SIGNAL(SIG_USART0_RECV)
#elif defined(SIG_UART0_RECV)
SIGNAL(SIG_UART0_RECV)
#elif defined(SIG_UART_RECV)
SIGNAL(SIG_UART_RECV)
#else
#error "Don't know what the Data Received vector is called for the first UART"
#endif
#endif
{
#if defined(UDR0)
unsigned char c = UDR0;
#elif defined(UDR)
unsigned char c = UDR;
#else
#error UDR not defined
#endif
rf_store_char(c, &rx_buffer);
}
#endif
#if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect)
// do nothing - on the 32u4 the first USART is USART1
#else
#if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect)
#error "Don't know what the Data Register Empty vector is called for the first UART"
#else
#if defined(UART0_UDRE_vect)
ISR(UART0_UDRE_vect)
#elif defined(UART_UDRE_vect)
ISR(UART_UDRE_vect)
#elif defined(USART0_UDRE_vect)
ISR(USART0_UDRE_vect)
#elif defined(USART_UDRE_vect)
ISR(USART_UDRE_vect)
#endif
{
if (tx_buffer.head == tx_buffer.tail)
{
// Buffer empty, so disable interrupts
#if defined(UCSR0B)
bit_clear(UCSR0B, UDRIE0);
#else
bit_clear(UCSRB, UDRIE);
#endif
}
else
{
// There is more data in the output buffer. Send the next byte
uint8_t c = tx_buffer.buffer[tx_buffer.tail];
tx_buffer.tail = (tx_buffer.tail + 1) & SERIAL_TX_BUFFER_MASK;
#if defined(UDR0)
UDR0 = c;
#elif defined(UDR)
UDR = c;
#else
#error UDR not defined
#endif
}
}
#endif
#endif
// Constructors ////////////////////////////////////////////////////////////////
RFHardwareSerial::RFHardwareSerial(ring_buffer *rx_buffer, ring_buffer_tx *tx_buffer,
volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
volatile uint8_t *udr,
uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x)
{
_rx_buffer = rx_buffer;
_tx_buffer = tx_buffer;
_ubrrh = ubrrh;
_ubrrl = ubrrl;
_ucsra = ucsra;
_ucsrb = ucsrb;
_udr = udr;
_rxen = rxen;
_txen = txen;
_rxcie = rxcie;
_udrie = udrie;
_u2x = u2x;
}
// Public Methods //////////////////////////////////////////////////////////////
void RFHardwareSerial::begin(unsigned long baud)
{
uint16_t baud_setting;
bool use_u2x = true;
#if F_CPU == 16000000UL
// hardcoded exception for compatibility with the bootloader shipped
// with the Duemilanove and previous boards and the firmware on the 8U2
// on the Uno and Mega 2560.
if (baud == 57600)
{
use_u2x = false;
}
#endif
try_again:
if (use_u2x)
{
*_ucsra = 1 << _u2x;
baud_setting = (F_CPU / 4 / baud - 1) / 2;
}
else
{
*_ucsra = 0;
baud_setting = (F_CPU / 8 / baud - 1) / 2;
}
if ((baud_setting > 4095) && use_u2x)
{
use_u2x = false;
goto try_again;
}
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
*_ubrrh = baud_setting >> 8;
*_ubrrl = baud_setting;
bit_set(*_ucsrb, _rxen);
bit_set(*_ucsrb, _txen);
bit_set(*_ucsrb, _rxcie);
bit_clear(*_ucsrb, _udrie);
}
void RFHardwareSerial::end()
{
// wait for transmission of outgoing data
while (_tx_buffer->head != _tx_buffer->tail)
;
bit_clear(*_ucsrb, _rxen);
bit_clear(*_ucsrb, _txen);
bit_clear(*_ucsrb, _rxcie);
bit_clear(*_ucsrb, _udrie);
// clear a ny received data
_rx_buffer->head = _rx_buffer->tail;
}
int RFHardwareSerial::available(void)
{
return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) & SERIAL_BUFFER_MASK;
}
int RFHardwareSerial::outputUnused(void)
{
return SERIAL_TX_BUFFER_SIZE-(unsigned int)((SERIAL_TX_BUFFER_SIZE + _tx_buffer->head - _tx_buffer->tail) & SERIAL_TX_BUFFER_MASK);
}
int RFHardwareSerial::peek(void)
{
if (_rx_buffer->head == _rx_buffer->tail)
{
return -1;
}
return _rx_buffer->buffer[_rx_buffer->tail];
}
int RFHardwareSerial::read(void)
{
// if the head isn't ahead of the tail, we don't have any characters
if (_rx_buffer->head == _rx_buffer->tail)
{
return -1;
}
unsigned char c = _rx_buffer->buffer[_rx_buffer->tail];
_rx_buffer->tail = (_rx_buffer->tail + 1) & SERIAL_BUFFER_MASK;
return c;
}
void RFHardwareSerial::flush()
{
while (_tx_buffer->head != _tx_buffer->tail)
;
}
#ifdef COMPAT_PRE1
void
#else
size_t
#endif
RFHardwareSerial::write(uint8_t c)
{
uint8_t i = (_tx_buffer->head + 1) & SERIAL_TX_BUFFER_MASK;
// If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to empty it a bit
while (i == _tx_buffer->tail)
;
_tx_buffer->buffer[_tx_buffer->head] = c;
_tx_buffer->head = i;
bit_set(*_ucsrb, _udrie);
#ifndef COMPAT_PRE1
return 1;
#endif
}
// Preinstantiate Objects //////////////////////////////////////////////////////
#if defined(UBRRH) && defined(UBRRL)
RFHardwareSerial RFSerial(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X);
#elif defined(UBRR0H) && defined(UBRR0L)
RFHardwareSerial RFSerial(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0);
#elif defined(USBCON)
// do nothing - Serial object and buffers are initialized in CDC code
#else
#error no serial port defined (port 0)
#endif
#endif