/*! * \file gpio-ioe.h * * \brief IO expander driver implementation (based on the sx1509) * * \copyright Revised BSD License, see section \ref LICENSE. * * \code * ______ _ * / _____) _ | | * ( (____ _____ ____ _| |_ _____ ____| |__ * \____ \| ___ | (_ _) ___ |/ ___) _ \ * _____) ) ____| | | || |_| ____( (___| | | | * (______/|_____)_|_|_| \__)_____)\____)_| |_| * (C)2013-2017 Semtech * * \endcode * * \author Miguel Luis ( Semtech ) * * \author Gregory Cristian ( Semtech ) */ #include <stdlib.h> #include <stdbool.h> #include "gpio-ioe.h" #include "sx1509.h" static Gpio_t *GpioIrq[16]; void GpioIoeInit( Gpio_t *obj, PinNames pin, PinModes mode, PinConfigs config, PinTypes type, uint32_t value ) { uint8_t regAdd = 0; uint8_t regVal = 0; uint8_t tempVal = 0; SX1509Init( ); obj->pin = pin; obj->pinIndex = ( 0x01 << pin % 16 ); if( ( obj->pin % 16 ) > 0x07 ) { regAdd = RegDirB; obj->pinIndex = ( obj->pinIndex >> 8 ) & 0x00FF; } else { regAdd = RegDirA; obj->pinIndex = ( obj->pinIndex ) & 0x00FF; } SX1509Read( regAdd, ®Val ); if( mode == PIN_OUTPUT ) { regVal = regVal & ~obj->pinIndex; } else { regVal = regVal | obj->pinIndex; } SX1509Write( regAdd, regVal ); if( ( obj->pin % 16 ) > 0x07 ) { SX1509Read( RegOpenDrainB, &tempVal ); if( config == PIN_OPEN_DRAIN ) { SX1509Write( RegOpenDrainB, tempVal | obj->pinIndex ); } else { SX1509Write( RegOpenDrainB, tempVal & ~obj->pinIndex ); } regAdd = RegDataB; } else { SX1509Read( RegOpenDrainA, &tempVal ); if( config == PIN_OPEN_DRAIN ) { SX1509Write( RegOpenDrainA, tempVal | obj->pinIndex ); } else { SX1509Write( RegOpenDrainA, tempVal & ~obj->pinIndex ); } regAdd = RegDataA; } SX1509Read( regAdd, ®Val ); // Sets initial output value if( value == 0 ) { regVal = regVal & ~obj->pinIndex; } else { regVal = regVal | obj->pinIndex; } SX1509Write( regAdd, regVal ); } void GpioIoeSetContext( Gpio_t *obj, void* context ) { obj->Context = context; } void GpioIoeSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler ) { uint8_t regAdd = 0; uint8_t regVal = 0; uint8_t i = 0; uint16_t tempVal = 0; uint8_t val = 0; if( irqHandler == NULL ) { return; } obj->IrqHandler = irqHandler; if( ( obj->pin % 16 ) > 0x07 ) { regAdd = RegInterruptMaskB; } else { regAdd = RegInterruptMaskA; } SX1509Read( regAdd, ®Val ); regVal = regVal & ~( obj->pinIndex ); SX1509Write( regAdd, regVal ); if( irqMode == IRQ_RISING_EDGE ) { val = 0x01; } else if( irqMode == IRQ_FALLING_EDGE ) { val = 0x02; } else // IRQ_RISING_FALLING_EDGE { val = 0x03; } tempVal = 0x0000; i = 0; while( tempVal != obj->pinIndex ) { tempVal = 0x01 << i; i++; } if( i < 4 ) { regAdd = RegSenseLowA; } else if( i < 9 ) { regAdd = RegSenseHighA; } else if( i < 13 ) { regAdd = RegSenseLowB; } else { regAdd = RegSenseHighB; } SX1509Read( regAdd, ®Val ); switch( i ) { case 1: case 5: case 9: case 13: regVal = ( regVal & REG_SENSE_PIN_MASK_1 ) | val; break; case 2: case 6: case 10: case 14: regVal = ( regVal & REG_SENSE_PIN_MASK_2 ) | ( val << 2 ); break; case 3: case 7: case 11: case 15: regVal = ( regVal & REG_SENSE_PIN_MASK_3 ) | ( val << 4 ); break; case 4: case 8: case 12: case 16: regVal = ( regVal & REG_SENSE_PIN_MASK_4 ) | ( val << 6 ); break; } SX1509Write( regAdd, regVal ); GpioIrq[obj->pin & 0x0F] = obj; } void GpioIoeRemoveInterrupt( Gpio_t *obj ) { uint8_t regAdd = 0; uint8_t regVal = 0; uint8_t i = 0; uint16_t tempVal = 0; // Clear callback before changing pin mode GpioIrq[obj->pin & 0x0F] = NULL; if( ( obj->pin % 16 ) > 0x07 ) { regAdd = RegInterruptMaskB; } else { regAdd = RegInterruptMaskA; } SX1509Read( regAdd, ®Val ); regVal = regVal | obj->pinIndex; SX1509Write( regAdd, regVal ); tempVal = 0x0000; i = 0; while( tempVal != obj->pinIndex ) { tempVal = 0x01 << i; i++; } if( i < 4 ) { regAdd = RegSenseLowA; } else if( i < 9 ) { regAdd = RegSenseHighA; } else if( i < 13 ) { regAdd = RegSenseLowB; } else { regAdd = RegSenseHighB; } SX1509Read( regAdd, ®Val ); switch( i ) { case 1: case 5: case 9: case 13: regVal = ( regVal & REG_SENSE_PIN_MASK_1 ); break; case 2: case 6: case 10: case 14: regVal = ( regVal & REG_SENSE_PIN_MASK_2 ); break; case 3: case 7: case 11: case 15: regVal = ( regVal & REG_SENSE_PIN_MASK_3 ); break; case 4: case 8: case 12: case 16: regVal = ( regVal & REG_SENSE_PIN_MASK_4 ); break; } SX1509Write( regAdd, regVal ); } void GpioIoeWrite( Gpio_t *obj, uint32_t value ) { uint8_t regAdd = 0; uint8_t regVal = 0; if( ( obj->pin % 16 ) > 0x07 ) { regAdd = RegDataB; } else { regAdd = RegDataA; } SX1509Read( regAdd, ®Val ); // Sets initial output value if( value == 0 ) { regVal = regVal & ~obj->pinIndex; } else { regVal = regVal | obj->pinIndex; } SX1509Write( regAdd, regVal ); } void GpioIoeToggle( Gpio_t *obj ) { GpioIoeWrite( obj, GpioIoeRead( obj ) ^ 1 ); } uint32_t GpioIoeRead( Gpio_t *obj ) { uint8_t regAdd = 0; uint8_t regVal = 0; if( ( obj->pin % 16 ) > 0x07 ) { regAdd = RegDataB; } else { regAdd = RegDataA; } SX1509Read( regAdd, ®Val ); if( ( regVal & obj->pinIndex ) == 0x00 ) { return 0; } else { return 1; } } void GpioIoeInterruptHandler( void ) { uint8_t irqLsb = 0; uint8_t irqMsb = 0; uint16_t irq = 0; SX1509Read( RegInterruptSourceA, &irqLsb ); SX1509Read( RegInterruptSourceB, &irqMsb ); irq = ( irqMsb << 8 ) | irqLsb; if( irq != 0x00 ) { for( uint16_t mask = 0x0001, pinIndex = 0; mask != 0x000; mask <<= 1, pinIndex++ ) { if( ( irq & mask ) != 0 ) { if( ( GpioIrq[pinIndex] != NULL ) && ( GpioIrq[pinIndex]->IrqHandler != NULL ) ) { GpioIrq[pinIndex]->IrqHandler( GpioIrq[pinIndex]->Context ); } } } } // Clear all interrupts/events SX1509Write( RegInterruptSourceA, 0xFF ); SX1509Write( RegInterruptSourceB, 0xFF ); SX1509Write( RegEventStatusB, 0xFF ); SX1509Write( RegEventStatusA, 0xFF ); }