blob: d26d503708b644b806f03fca43240440af1bec23 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////
// Copyright (c) 2000-2018 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