| //////////////////////////////////////////////////////////////////////// |
| // Copyright (c) 2000-2017 Ericsson Telecom AB |
| // All rights reserved. This program and the accompanying materials |
| // are made available under the terms of the Eclipse Public License v2.0 |
| // which accompanies this distribution, and is available at |
| // https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html |
| // |
| // Contributors: |
| // Gyorgy Rethy |
| // version R2A |
| //////////////////////////////////////////////////////////////////////// |
| // Library to handle Raspberry Pi GPIO port common utility functions |
| // Dependencies |
| // - TCCFileIO_Functions.ttcn and TCCFileIO.cc from |
| // titan.Libraries.TCCUsefulFunctions |
| // - TitanLoggerControl.ttcn from the Titan package ($TTCN3_DIR/include) |
| // Works with port array definitions only! Your component type shall |
| // extend GPIO_Base! (see details in GPIOPinPort.ttcn) |
| // Some checks and logging is done only, when DEBUG is set in the [LOGGING] |
| // section of the runtime configuration file, but then automatically. |
| //////////////////////////////////////////////////////////////////////// |
| |
| module GPIO_Common{ |
| |
| import from GPIOPinPort all; |
| import from TCCFileIO_Functions all; |
| import from TitanLoggerControl all; |
| |
| //used only for debugging GPIO pin initialization |
| modulepar charstring gpio_pin_dir_base := "/sys/class/gpio/gpio"; |
| //Titan logger used |
| //DON'T CHANGE unless you are absolutelly sure, other logger is used! |
| modulepar charstring tsp_logger:="LegacyLogger"; |
| //time to wait RPi to create directories, write values etc. |
| //value depends on the speed of your SD card, but if too small, |
| //you risk fatal error during port initialization |
| modulepar float shortWait := 0.08; |
| |
| //GPIO direction and value constants |
| const GPIO_PIN_DIRECTION pin_direction_OUT := OUT; |
| const GPIO_PIN_DIRECTION pin_direction_IN := IN; |
| const GPIO_PIN_VALUE pin_value_HIGH := HIGH; |
| const GPIO_PIN_VALUE pin_value_LOW := LOW; |
| |
| type record of integer GPIO_PinList; |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Maps and initializes GPIO ports of the same direction |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_direction Direction for all pins to be initialized (pin_direction_OUT or pin_direction_IN) |
| // @par p_pinList List of pin numbers to be initialized ({<pin>,<pin>,...}) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_init ( |
| GPIO_PIN_DIRECTION p_direction, |
| GPIO_PinList p_pinList |
| ) runs on GPIO_Base { |
| var integer i; |
| |
| //Checking if DEBUG logging is set in config file |
| var boolean isDebugSet := f_GPIO_isDebug(); |
| |
| for (i:= 0; i<lengthof(p_pinList); i := i+1){ |
| //mapping pin ports |
| map(self:gpio[p_pinList[i]],system:gpio[p_pinList[i]]); |
| //Leave time for raspbian to create the pin dir |
| f_GPIO_wait(shortWait); |
| if(isDebugSet){f_GPIO_checkPinDirectory(p_pinList[i])} |
| //setting direction |
| gpio[p_pinList[i]].send(p_direction); |
| //Leave time for raspbian to create and write the direction file |
| f_GPIO_wait(shortWait); |
| if(isDebugSet){f_GPIO_checkPinDirection(p_pinList[i],p_direction)} |
| //if direction is OUT setting pin value to LOW |
| if(p_direction == pin_direction_OUT){ |
| gpio[p_pinList[i]].send(pin_value_LOW); |
| //Leave time for raspbian to create and write the value file |
| f_GPIO_wait(shortWait); |
| if(isDebugSet){f_GPIO_checkPinValue(p_pinList[i],pin_value_LOW)} |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Unmaping GPIO ports |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pinList List of pin numbers to be uninitialized ({<pin>,<pin>,...}) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_cleanUp( |
| GPIO_PinList p_pinList |
| ) runs on GPIO_Base { |
| //Checking if DEBUG logging is set in config file |
| var boolean isDebugSet := f_GPIO_isDebug(); |
| for (var integer i:= 0; i<lengthof(p_pinList); i := i+1){ |
| if(f_GPIO_getPinDirection(p_pinList[i]) == pin_direction_OUT){ |
| gpio[p_pinList[i]].send(pin_value_LOW); |
| //Leave time for raspbian to create and write the value file |
| f_GPIO_wait(shortWait); |
| if(isDebugSet){f_GPIO_checkPinValue(p_pinList[i],pin_value_LOW)} |
| } |
| unmap(self:gpio[p_pinList[i]],system:gpio[p_pinList[i]]); |
| //Leave time for raspbian to delete the pin directory |
| f_GPIO_wait(shortWait); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Checking if pin directory is created |
| // |
| // STOPS EXECUTION IF PIN DIRECTORY DOES NOT EXISTS |
| // CHECK CAUSE IN THE FILE SYSTEM IN THIS CASE! |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be checked |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_checkPinDirectory( |
| integer p_pin |
| ) return boolean { |
| var boolean result := true; |
| if (f_FIO_fileOrDirExists(gpio_pin_dir_base & int2str(p_pin))) { |
| setverdict (pass,"Export directory for pin ",p_pin," has been created successfully"); |
| } else { |
| setverdict (fail,"Failed to create export directory for pin ",p_pin," CHECK CAUSE IN THE FILE SYSTEM!"); |
| mtc.stop |
| } |
| return result |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Set pin direction |
| // |
| // using gpio[<pin_no>].send(<new direction>) in user code |
| // is equally well, it's a matter of taste... |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be set |
| // @par p_direction Direction of pin to be set (pin_direction_OUT or pin_direction_IN) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_setPinDirection( |
| integer p_pin, |
| GPIO_PIN_DIRECTION p_direction |
| ) runs on GPIO_Base { |
| |
| gpio[p_pin].send(p_direction); |
| |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Get pin direction |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin, direction of which to be returned |
| // @return Returns actually set pin direction (pin_direction_OUT or pin_direction_IN) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_getPinDirection( |
| integer p_pin |
| ) return GPIO_PIN_DIRECTION { |
| var charstring direction; |
| var GPIO_PIN_DIRECTION ret_direction; |
| |
| var integer fd := f_FIO_open_rdonly(gpio_pin_dir_base & int2str(p_pin) & "/direction"); |
| var integer ret := f_FIO_read_text_until(fd, direction, "\n"); |
| f_FIO_close(fd); |
| |
| select (direction) { |
| case ("out") { ret_direction := pin_direction_OUT } |
| case ("in") { ret_direction := pin_direction_IN } |
| } |
| return ret_direction |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Checking if pin direction is written correctly |
| // |
| // STOPS EXECUTION IF ACTUAL DIRECTION DIFFERS FROM THE EXPECTED ONE |
| // TO PREVENT HW DAMAGE OF RPi GPIO CHIP! |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be checked |
| // @par p_direction Expected direction of the pin (pin_direction_OUT or pin_direction_IN) |
| // @return Returns true if direction is set as expected (STOPS EXECUTION OTHERWISE!) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_checkPinDirection( |
| integer p_pin, |
| GPIO_PIN_DIRECTION p_direction |
| ) return boolean { |
| var GPIO_PIN_DIRECTION actualDirection := f_GPIO_getPinDirection(p_pin); |
| var boolean result := true; |
| |
| if (actualDirection == p_direction) { |
| setverdict (pass,"Direction of pin ",p_pin," has been set to ",p_direction," successfully"); |
| } else { |
| setverdict (fail,"Direction of pin ",p_pin," is not set to ",p_direction,", but is ", actualDirection); |
| mtc.stop |
| } |
| return result |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Set pin value to low or high |
| // |
| // using gpio[<pin_no>].send(<new value>) in user code |
| // is equally well, it's a matter of taste... |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be set |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_setPinValue( |
| integer p_pin, |
| GPIO_PIN_VALUE p_value |
| ) runs on GPIO_Base { |
| gpio[p_pin].send(p_value); |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Get pin value |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin, value of which to be returned |
| // @return Returns actually set pin value (pin_value_HIGH or pin_value_LOW) |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_getPinValue( |
| integer p_pin |
| ) return GPIO_PIN_VALUE { |
| |
| var charstring value_; |
| var GPIO_PIN_VALUE ret_value; |
| |
| var integer fd := f_FIO_open_rdonly(gpio_pin_dir_base & int2str(p_pin) & "/value"); |
| var integer ret := f_FIO_read_text_until(fd, value_, "\n"); |
| f_FIO_close(fd); |
| |
| select (value_) { |
| case ("1") { ret_value := pin_value_HIGH } |
| case ("0") { ret_value := pin_value_LOW } |
| } |
| return ret_value |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Checking if pin value is written correctly |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be checked |
| // @par p_value Expected value of the pin (pin_value_HIGH or pin_value_LOW) |
| // @return true if value as expected, false otherwise |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_checkPinValue( |
| integer p_pin, |
| GPIO_PIN_VALUE p_value |
| ) return boolean { |
| var boolean result := true; |
| var GPIO_PIN_VALUE actualValue := f_GPIO_getPinValue(p_pin); |
| |
| if (actualValue == p_value) { |
| log("Value of pin ",p_pin," has been set to ", p_value, " successfully"); |
| setverdict (pass); |
| } else { |
| log("Value of pin ",p_pin," is not set to ",p_value," but is ", actualValue); |
| result := false; |
| } |
| return result |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Waiting |
| // |
| // Note: this function causes high processor load, use for short delays only! |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_duration Time to wait |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_wait( |
| float p_duration |
| ) { |
| timer t_short := p_duration; |
| t_short.start; |
| alt { [] t_short.timeout {} |
| [else] {repeat} //to disable any activated defaults |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Sets debug level |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @return true if DEBUG is set in [LOGGING] session of the runtime config file |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_isDebug() return boolean { |
| var Severities current_filemask := get_file_mask(tsp_logger); |
| for (var integer i:= 0; i<lengthof(current_filemask); i := i+1){ |
| if(current_filemask[i]==DEBUG_ENCDEC or |
| current_filemask[i]==DEBUG_TESTPORT or |
| current_filemask[i]==DEBUG_USER or |
| current_filemask[i]==DEBUG_FRAMEWORK or |
| current_filemask[i]==DEBUG_UNQUALIFIED |
| ){return true} |
| } |
| return false |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Sets pin to high and after a while to low |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be switched |
| // @par p_duration Time the pin shall be in high |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_pinOnOff( |
| integer p_pin, |
| float p_duration |
| )runs on GPIO_Base { |
| //Checking if DEBUG logging is set in config file |
| var boolean isDebugSet := f_GPIO_isDebug(); |
| |
| f_GPIO_setPinValue(p_pin,pin_value_HIGH); |
| if(isDebugSet){f_GPIO_checkPinValue(p_pin,pin_value_HIGH)} |
| |
| f_GPIO_wait(p_duration); |
| |
| f_GPIO_setPinValue(p_pin,pin_value_LOW); |
| if(isDebugSet){ |
| f_GPIO_wait(shortWait); |
| f_GPIO_checkPinValue(p_pin,pin_value_LOW) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Sets pin to low and after a while to high |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to be switched |
| // @par p_duration Time the pin shall be in low |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_pinOffOn( |
| integer p_pin, |
| float p_wait |
| ) runs on GPIO_Base { |
| //Checking if DEBUG logging is set in config file |
| var boolean isDebugSet := f_GPIO_isDebug(); |
| |
| f_GPIO_setPinValue(p_pin,pin_value_LOW); |
| if(isDebugSet){ |
| f_GPIO_wait(shortWait); |
| f_GPIO_checkPinValue(p_pin,pin_value_LOW) |
| } |
| |
| f_GPIO_wait(p_wait); |
| |
| f_GPIO_setPinValue(p_pin,pin_value_HIGH); |
| if(isDebugSet){ |
| f_GPIO_wait(shortWait); |
| f_GPIO_checkPinValue(p_pin,pin_value_HIGH) |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Swaps pin value |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @par p_pin Number of pin to swap the value of |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_swapPinValue( |
| integer p_pin |
| ) runs on GPIO_Base { |
| //Checking if DEBUG logging is set in config file |
| var boolean isDebugSet := f_GPIO_isDebug(); |
| |
| var GPIO_PIN_VALUE actualValue := f_GPIO_getPinValue(p_pin); |
| select (actualValue) { |
| case (pin_value_LOW) { |
| f_GPIO_setPinValue(p_pin,pin_value_HIGH); |
| if(isDebugSet){ |
| f_GPIO_wait(shortWait); |
| f_GPIO_checkPinValue(p_pin,pin_value_HIGH) |
| } |
| } |
| case (pin_value_HIGH) { |
| f_GPIO_setPinValue(p_pin,pin_value_LOW); |
| if(isDebugSet){ |
| f_GPIO_wait(shortWait); |
| f_GPIO_checkPinValue(p_pin,pin_value_LOW) |
| } |
| } |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////// |
| // |
| // Modulates pin with a binary code |
| // |
| //////////////////////////////////////////////////////////////////////// |
| // @purpose Modulates pin with a binary code; between two code sequences |
| // a synchronization signal of half wide is sent. Wide of the |
| // code impulse is determined by the frequency parameter. Can |
| // be used for PWM, modulating a LED with a code etc. With |
| // default settings suitable for PWM. |
| // PIN SHALL BE INITIALIZED TO 'OUT' BEFORE USING THIS FUNCTION |
| // NO DEFAULT SHALL BE ACTIVATED WHEN CALLING THIS FUNCTION. |
| // (e.g, started it on a new PTC) |
| // @par p_pin Number of pin to be modulated |
| // @par p_code Code modulating the pin output |
| // @status verified |
| //////////////////////////////////////////////////////////////////////// |
| function f_GPIO_modulatePin( |
| integer p_pin, |
| integer p_frequency:=10, |
| bitstring p_code:='10101010'B, |
| bitstring p_sync:=''B |
| )runs on GPIO_Base { |
| var integer i, codeLenght:=lengthof(p_code), syncLenght:=lengthof(p_sync); |
| var float wait:=1.0/int2float(p_frequency), wait_half:=wait/2.0; |
| var boolean loop:=true; |
| timer T_wait; |
| |
| while(loop){ |
| for(i:=0;i<codeLenght;i:=i+1){ |
| select(p_code[i]){ |
| case('1'B){f_GPIO_setPinValue(p_pin,pin_value_HIGH)} |
| case('0'B){f_GPIO_setPinValue(p_pin,pin_value_LOW)} |
| } |
| T_wait.start(wait);T_wait.timeout; |
| } |
| for(i:=0;i<syncLenght;i:=i+1){ |
| select(p_sync[i]){ |
| case('1'B){f_GPIO_setPinValue(p_pin,pin_value_HIGH)} |
| case('0'B){f_GPIO_setPinValue(p_pin,pin_value_LOW)} |
| } |
| T_wait.start(wait_half);T_wait.timeout; |
| } |
| } |
| } |
| |
| }//end module |