/* FtduinoSimple.cpp - Library for ftduino (c) 2017-2021 by Till Harbaum A very simple io library for the ftduino only supporting diginal IO but without and interrupt handlers and with a small footprint. If you need analog io, ultrasonic IO and fast counters use the full feature version. */ #include "Arduino.h" #include #include #define IN_FTDUINO_SIMPLE_LIB #include "FtduinoSimple.h" Ftduino ftduino; void Ftduino::cd4051_init() { // PC.6 = ING, PF.0 = A, PF1 = B, PD.5 = C // make all 4051 control signals outputs DDRC |= (1<<6); // INHIBIT PORTC |= (1<<6); // pull it high by default, disabling the 4051 DDRF |= (1<<0); // A PORTF &= ~(1<<0); // = low DDRF |= (1<<1); // B PORTF &= ~(1<<1); // = low DDRD |= (1<<5); // C PORTD &= ~(1<<5); // = low } void Ftduino::cd4051_set(char mode) { // enable/disable pullup // 0..7 = on for I1..I8, 8 = off if(mode & 8) PORTC |= (1<<6); // activate INH pin else { PORTC &= ~(1<<6); // de-activate INH pin if(mode & 1) PORTF |= (1<<0); // A=1 else PORTF &= ~(1<<0); // A=0 if(mode & 2) PORTF |= (1<<1); // B=1 else PORTF &= ~(1<<1); // B=0 if(mode & 4) PORTD |= (1<<5); // C=1 else PORTD &= ~(1<<5); // C=0 } } void Ftduino::input_init() { cd4051_init(); // the digital counterparts of ADC4-11 are distributed all over the chip // ADC4-7 are PF.4-PF.7 DDRF &= ~((1<<7)|(1<<6)|(1<<5)|(1<<4)); // ADC8,9,10 are PD.4,PD.6 and PD.7 DDRD &= ~((1<<7)|(1<<6)|(1<<4)); // ADC11 is PB.4 DDRB &= ~(1<<4); } bool Ftduino::input_get(uint8_t ch) { static char cd4051_mode = -1; bool rval = false; // this enables the pullup for the given channel and returns // its digital state // enable pullup if necessary if(cd4051_mode != ch) { cd4051_set(ch); _delay_us(100); cd4051_mode = ch; } switch(ch) { case Ftduino::I1: rval = PINF & (1<<4); break; case Ftduino::I2: rval = PINF & (1<<5); break; case Ftduino::I3: rval = PINF & (1<<6); break; case Ftduino::I4: rval = PINF & (1<<7); break; case Ftduino::I5: rval = PIND & (1<<4); break; case Ftduino::I6: rval = PIND & (1<<6); break; case Ftduino::I7: rval = PIND & (1<<7); break; case Ftduino::I8: rval = PINB & (1<<4); break; } // return true if pin is grounded return !rval; } #if defined(OUTPUT_DRIVER_TLE94108EL) || defined(OUTPUT_DRIVER_AUTO) #define HB_ACT_1_CTRL 0b10000011 #define HB_ACT_2_CTRL 0b11000011 #endif #if defined(OUTPUT_DRIVER_DRV8908) || defined(OUTPUT_DRIVER_AUTO) #define OP_CTRL_1 0x08 #define OP_CTRL_2 0x09 #define OLD_CTRL_1 0x1f #define OLD_CTRL_2 0x20 #endif void Ftduino::output_init() { uint8_t i; // configure /SS, SCK and MOSI as output // drive /SS it high DDRB |= (1<<0) | (1<<1) | (1<<2); PORTB |= (1<<0); // enable SPI, no interrupts, MSB first, Master mode, // mode 1 (SCK low when idle, data valid on falling edge) // and clock = FCPU/16 = 1Mhz SPCR = (1<>= 8; while(!(SPSR & (1< both drivers off, // mode = 1 -> highside on, mode = 2 -> lowside on if(mode == 1) { state &= ~(1 << (2*port)); // clear lowside driver state |= (2 << (2*port)); // set highside driver } else if(mode == 2) { state |= (1 << (2*port)); // set lowside driver state &= ~(2 << (2*port)); // clear highside driver } else { state &= ~(3 << (2*port)); // clear both drivers } // and write state into registers write_spi_reg(HB_ACT_1_CTRL, state); write_spi_reg(HB_ACT_2_CTRL, state >> 8); } #endif #if defined(OUTPUT_DRIVER_DRV8908) || defined(OUTPUT_DRIVER_AUTO) #if defined(OUTPUT_DRIVER_DRV8908) void Ftduino::output_set(uint8_t port, uint8_t mode) #endif #if defined(OUTPUT_DRIVER_AUTO) void Ftduino::output_set_drv8908(uint8_t port, uint8_t mode) #endif { // DEV8908 pins are mapped straight 0-7 to O1-O8 // mode = 0 -> both drivers off, // mode = 1 -> highside on, mode = 2 -> lowside on if(mode == 1) { state &= ~(1 << (2*port)); // clear lowside driver state |= (2 << (2*port)); // set highside driver } else if(mode == 2) { state |= (1 << (2*port)); // set lowside driver state &= ~(2 << (2*port)); // clear highside driver } else { state &= ~(3 << (2*port)); // clear both drivers } // and write state into registers write_spi_reg(OP_CTRL_1, state); write_spi_reg(OP_CTRL_2, state >> 8); } #endif #if defined(OUTPUT_DRIVER_AUTO) void Ftduino::output_set(uint8_t port, uint8_t mode) { if(driver_chip == CHIP_MC33879) output_set_mc33879a(port, mode); else if(driver_chip == CHIP_TLE94108) output_set_tle94108el(port, mode); else if(driver_chip == CHIP_DRV8908) output_set_drv8908(port, mode); } #endif void Ftduino::motor_set(uint8_t port, uint8_t mode) { // map from motor to output port uint8_t o = (2 * (port - Ftduino::M1)) + Ftduino::O1; switch(mode) { case Ftduino::OFF: // both outputs open output_set(o , Ftduino::OFF); output_set(o+1, Ftduino::OFF); break; case Ftduino::LEFT: // both outputs driven low/hi output_set(o , Ftduino::LO); output_set(o+1, Ftduino::HI); break; case Ftduino::RIGHT: // both outputs driven low/hi output_set(o , Ftduino::HI); output_set(o+1, Ftduino::LO); break; case Ftduino::BRAKE: // both outputs driven low output_set(o , Ftduino::LO); output_set(o+1, Ftduino::LO); break; } } // control pull-down on c1 to trigger distance sensor void Ftduino::pulldown_c1_init() { DDRE |= (1<<2); // pulldown is controlled by PE.2 (#HWB) PORTE &= ~(1<<2); // de-activate by default } void Ftduino::pulldown_c1_enable(char on) { if(on) PORTE |= (1<<2); else PORTE &= ~(1<<2); } bool Ftduino::counter_get_state(uint8_t ch) { if(ch == Ftduino::C1) return !(PIND & (1<<2)); if(ch == Ftduino::C2) return !(PIND & (1<<3)); if(ch == Ftduino::C3) return !(PINB & (1<<5)); if(ch == Ftduino::C4) return !(PINB & (1<<6)); return false; } void Ftduino::counter_init(void) { // C1/C2 are PD.2/PD.3 and C3/C4 are on PB.5/PB.6 DDRD &= ~((1 << 2) | (1 << 3)); // counter ports C1 and C2 are inputs PORTD &= ~((1 << 2) | (1 << 3)); // disable internal pullup, we use external DDRB &= ~((1 << 5) | (1 << 6)); // counter ports C3 and C4 are inputs PORTB &= ~((1 << 5) | (1 << 6)); // disable internal pullup, we use external } Ftduino::Ftduino() { // --------------------------------------------------------- // prepare digital input system fot I1..I8 input_init(); // init the pulldown for the ultrasonic. However, no // support for the sensor in the simple lib pulldown_c1_init(); // prepare spi service for M1..M4/O1..O8 // No PWM in the simple lib output_init(); // the simple lib only uses the counters as digital inouts counter_init(); }