Commit d98af659 authored by Marcus Greiff's avatar Marcus Greiff

Created tests for the SysLed item and refactor code

parent 55f09f2d
""" """
Lowest form of communication with the GPIO pins. The available pins are Lowest form of communication with the GPIO pins. The available pins are
listed in the "channel" parameter, and appear as directories in /sys/class/gpio. listed in the "channel" parameter, and appear as directories in /sys/class/gpio
after being exported.
Any of these GPIOs can be run in various ways, with any operation specified by an
Int32 value.
For instance, to setup a GPIO on "gpio112", configure it as an output pin and set For instance, to setup a GPIO on "gpio112", configure it as an output pin and set
it to high, the following code would be used. it to high, the following code would be used.
gpio = GPIO(1) gpio = GPIO(1)
write!(gpio, (2,"out")) write!(gpio, (2,"out"))
write!(gpio, (1, "1")) write!(gpio, (1, "1"))
The operation of reading the current output value of the GPIO is done by The operation of reading the current output value of the GPIO is done by
read(gpio, 1) read(gpio, 1)
See the test/BeagleBone/GPIO_test.jl for more examples.
""" """
type GPIO <: IO_Object type GPIO <: IO_Object
i::Int32 i::Int32
basedir::String basedir::String
filestreams::Array{IOStream,1} filestreams::Array{IOStream,1}
function GPIO(i::Int32) function GPIO(i::Int32)
(i <= 0 || i > length(channels)) && error("Invalid GPIO index: $i") (i <= 0 || i > length(gpio_channels)) && error("Invalid GPIO index: $i")
# If the tests re being run, a dummy filesystem is created # If the tests re being run, a dummy filesystem is created
if isdefined(:RUNNING_TESTS) basedir = export_gpio(i)
# Export a dummy file system for testing
basedir = "$(pwd())/testfilesystem/gpio"
try
println("$(basedir)/$(channels[i])")
mkpath("$(basedir)/$(channels[i])")
catch
error("Could not export the GPIO device for channel $(channels[i]) for testing as the directory $(basedir)/$(channels[i]) already exists.")
end
try
f = open("$(basedir)/$(channels[i])/value", "w"); write(f,"0"); close(f);
f = open("$(basedir)/$(channels[i])/direction", "w"); write(f,"0"); close(f);
f = open("$(basedir)/$(channels[i])/edge", "w"); write(f,"0"); close(f);
catch
error("Could not open the requested GPIO testfiles for channel $(channels[i]).")
end
else
basedir = "/sys/class/gpio"
# TODO Export for real-time use
end
# setup IOstreams # setup IOstreams
value_filestream = open("$(basedir)/$(channels[i])/value","r+") value_filestream = open("$(basedir)/$(gpio_channels[i])/value","r+")
direction_filestream = open("$(basedir)/$(channels[i])/direction","r+") direction_filestream = open("$(basedir)/$(gpio_channels[i])/direction","r+")
edge_filestream = open("$(basedir)/$(channels[i])/edge","r") edge_filestream = open("$(basedir)/$(gpio_channels[i])/edge","r")
# Initialize object # Initialize object
return new(i, basedir, [value_filestream, direction_filestream, edge_filestream]) return new(i, basedir, [value_filestream, direction_filestream, edge_filestream])
end end
end end
writeOperations = [["1", "0"],["in", "out"],["none","rising","falling","both"]]
channels =[
"gpio112"
"gpio114"
"gpio115"
"gpio116"
"gpio14"
"gpio15"
"gpio2"
"gpio20"
"gpio22"
"gpio23"
"gpio26"
"gpio27"
"gpio3"
"gpio30"
"gpio31"
"gpio4"
"gpio44"
"gpio45"
"gpio46"
"gpio47"
"gpio48"
"gpio49"
"gpio5"
"gpio50"
"gpio51"
"gpio60"
"gpio61"
"gpio65"
"gpio66"
"gpio67"
"gpio68"
"gpio69"
"gpio7"
]
""" """
write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false) write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false)
Writes an entry to an operation on a GPIO, of the form args = (operation, entry). Writes an entry to an operation on a GPIO, of the form args = (operation, entry).
...@@ -101,10 +44,12 @@ Writes an entry to an operation on a GPIO, of the form args = (operation, entry) ...@@ -101,10 +44,12 @@ Writes an entry to an operation on a GPIO, of the form args = (operation, entry)
function write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false) function write!(gpio::GPIO, args::Tuple{Int32,String}, debug::Bool=false)
debug && return debug && return
operation, entry = args[1], args[2] operation, entry = args[1], args[2]
(operation < 1 || operation > length(gpio.filestreams)) && error("Invalid GPIO operation: $operation") # Only filestreams 1 and 2 are writable
if entry in writeOperations[operation] operation [1,2] && error("Invalid GPIO operation $operation for writing")
write(gpio.filestreams[operation], entry) if entry in gpio_operations[operation]
seekstart(gpio.filestreams[operation]) seekstart(gpio.filestreams[operation])
write(gpio.filestreams[operation], "$entry\n")
flush(gpio.filestreams[operation])
else else
error("Invalid entry for GPIO operation $(operation): $(entry)") error("Invalid entry for GPIO operation $(operation): $(entry)")
end end
...@@ -116,9 +61,10 @@ Reads the current value from an operation on a GPIO. ...@@ -116,9 +61,10 @@ Reads the current value from an operation on a GPIO.
""" """
function read(gpio::GPIO, operation::Int32, debug::Bool=false) function read(gpio::GPIO, operation::Int32, debug::Bool=false)
debug && return debug && return
(operation < 1 || operation > length(gpio.filestreams)) && error("Invalid GPIO operation: $operation") # Filestreams 1, 2 and 3 are readable
l = readline(gpio.filestreams[operation]) operation [1,2,3] && error("Invalid GPIO operation: $operation for reading")
seekstart(gpio.filestreams[operation]) seekstart(gpio.filestreams[operation])
l = readline(gpio.filestreams[operation])
return l return l
end end
...@@ -139,16 +85,44 @@ function teardown(gpio::GPIO, debug::Bool=false) ...@@ -139,16 +85,44 @@ function teardown(gpio::GPIO, debug::Bool=false)
# Remove the dummy file system for testing # Remove the dummy file system for testing
basedir = "$(pwd())/testfilesystem/gpio" basedir = "$(pwd())/testfilesystem/gpio"
try try
rm("$(gpio.basedir)/$(channels[gpio.i])"; recursive=true) rm("$(gpio.basedir)/$(gpio_channels[gpio.i])"; recursive=true)
catch catch
error("Could not remove the requested GPIO testfiles for channel $(channels[i]).") error("Could not remove the requested GPIO testfiles for channel $(gpio_channels[i]).")
end end
else else
# Remove the file system # Remove the file system
filename = "/sys/class/gpio/unexport" filename = "/sys/class/gpio/unexport"
#TODO Verify if this is the correct command to send to unexport... #TODO Verify if this is the correct command to send to unexport
write(filename, channels[gpio.i]) write(filename, gpio_channels[gpio.i])
end
end
"""
export_gpio(i::Int32, debug::Bool=false)
Export the GPIO file system, either for real-time or testing usecases.
"""
function export_gpio(i::Int32)
if isdefined(:RUNNING_TESTS)
# Export a dummy file system for testing
basedir = "$(pwd())/testfilesystem/gpio"
try
#println("$(basedir)/$(gpio_channels[i])")
mkpath("$(basedir)/$(gpio_channels[i])")
catch
error("Could not export the GPIO device for channel $(gpio_channels[i]) for testing as the directory $(basedir)/$(gpio_channels[i]) already exists.")
end
try
f = open("$(basedir)/$(gpio_channels[i])/value", "w"); write(f,"0"); close(f);
f = open("$(basedir)/$(gpio_channels[i])/direction", "w"); write(f,"0"); close(f);
f = open("$(basedir)/$(gpio_channels[i])/edge", "w"); write(f,"0"); close(f);
catch
error("Could not open the requested GPIO testfiles for channel $(gpio_channels[i]).")
end
else
basedir = "/sys/class/gpio"
# TODO Export for real-time use
end end
return basedir
end end
""" """
......
"""Define abstract type for pins/LEDS on the BeagleBone""" """
Define abstract type for pins/LEDS on the BeagleBone
"""
abstract type IO_Object end abstract type IO_Object end
"""
GPIO interfaces
"""
const gpio_operations = [
["1", "0"],
["in", "out"],
["none","rising","falling","both"]
]
const gpio_channels =[
"gpio112"
"gpio114"
"gpio115"
"gpio116"
"gpio14"
"gpio15"
"gpio2"
"gpio20"
"gpio22"
"gpio23"
"gpio26"
"gpio27"
"gpio3"
"gpio30"
"gpio31"
"gpio4"
"gpio44"
"gpio45"
"gpio46"
"gpio47"
"gpio48"
"gpio49"
"gpio5"
"gpio50"
"gpio51"
"gpio60"
"gpio61"
"gpio65"
"gpio66"
"gpio67"
"gpio68"
"gpio69"
"gpio7"
]
...@@ -5,12 +5,17 @@ i ∈ [1,2,3,4]. ...@@ -5,12 +5,17 @@ i ∈ [1,2,3,4].
""" """
type SysLED <: IO_Object type SysLED <: IO_Object
i::Int32 i::Int32
basedir::String
filestream::IOStream filestream::IOStream
function SysLED(i::Int32) function SysLED(i::Int32)
i [1,2,3,4] && error("Invalid SysLED index: $i") i [1,2,3,4] && error("Invalid SysLED index: $i")
#Note, in the future we should interface to config and retrieve IOStream from there
brightness_filestream = open("/sys/class/leds/beaglebone:green:usr$(i-1)/brightness","r+") # Export system for testing
return new(i, brightness_filestream) basedir = export_led(i)
# open filestream
brightness_filestream = open("$(basedir)/beaglebone:green:usr$(i-1)/brightness","r+")
return new(i, basedir, brightness_filestream)
end end
end end
...@@ -20,9 +25,10 @@ Turns the LED 'SysLed' on/off for val = true/false respectively. ...@@ -20,9 +25,10 @@ Turns the LED 'SysLed' on/off for val = true/false respectively.
""" """
function write!(led::SysLED, entry::String, debug::Bool=false) function write!(led::SysLED, entry::String, debug::Bool=false)
debug && return debug && return
entry ["0", "1"] && error("Invalid SysLED entry $(entry), valid options are 0 and 1 (string)") entry ["0", "1"] && error("Invalid SysLED entry $(entry), valid options are 0 and 1 ::String")
write(led.filestream, entry)
seekstart(led.filestream) seekstart(led.filestream)
write(led.filestream, "$entry\n")
flush(led.filestream)
end end
""" """
...@@ -31,10 +37,8 @@ Reads the current brightness value from the LED 'SysLED'. ...@@ -31,10 +37,8 @@ Reads the current brightness value from the LED 'SysLED'.
""" """
function read(led::SysLED, debug::Bool=false) function read(led::SysLED, debug::Bool=false)
debug && return debug && return
l = read(filestream, Char)
(l != '1' && l != '0') && error("Invalid value \"$l\" read from SysLed")
seekstart(led.filestream) seekstart(led.filestream)
return l return readline(led.filestream)
end end
""" """
...@@ -44,4 +48,35 @@ Closes all open filestreams for the SysLED 'led'. ...@@ -44,4 +48,35 @@ Closes all open filestreams for the SysLED 'led'.
function teardown(led::SysLED, debug::Bool=false) function teardown(led::SysLED, debug::Bool=false)
debug && return debug && return
close(led.filestream) close(led.filestream)
if isdefined(:RUNNING_TESTS)
# Remove the dummy file system for testing
try
#println("$(led.basedir)/beaglebone:green:usr$(led.i-1)")
rm("$(led.basedir)/beaglebone:green:usr$(led.i-1)"; recursive=true)
catch
error("Could not remove the requested LED testfiles for channel beaglebone:green:usr$(led.i-1).")
end
end
end
function export_led(i::Int32)
if isdefined(:RUNNING_TESTS)
# Export a dummy file system for testing
basedir = "$(pwd())/testfilesystem/leds"
try
#println("$(basedir)/beaglebone:green:usr$(i-1)")
mkpath("$(basedir)/beaglebone:green:usr$(i-1)")
catch
error("Could not export the LED device for beaglebone:green:usr$(i-1)) for testing as the directory $(basedir)/beaglebone:green:usr$(i-1) already exists.")
end
try
f = open("$(basedir)/beaglebone:green:usr$(i-1)/brightness", "w"); write(f,"0"); close(f);
catch
error("Could not open the requested LED testfiles for beaglebone:green:usr$(i-1)/brightness.")
end
else
basedir = "/sys/class/leds"
end
return basedir
end end
using LabConnections.BeagleBone using LabConnections.BeagleBone
import LabConnections.BeagleBone: initdev, listdev, closedev, printdev, write!, read! import LabConnections.BeagleBone: initdev, listdev, closedev, printdev, write!, read, gpio_channels
using Base.Test using Base.Test
@testset "GPIO Inialization and termination" begin @testset "GPIO tests" begin
# Initialize three devices
initdev("gpio", 1)
@test sum(listdev()) == 1
initdev("gpio", 3) @testset "Inialization/Termination" begin
@test sum(listdev()) == 2 # Initialize three devices
initdev("gpio", 1)
@test sum(listdev()) == 1
initdev("gpio", 5) initdev("gpio", 3)
@test sum(listdev()) == 3 @test sum(listdev()) == 2
#printdev("gpio", 3) initdev("gpio", 5)
@test sum(listdev()) == 3
# Attempt to initialize a device which has already been initialized #printdev("gpio", 3)
@test_throws ErrorException initdev("gpio", 1)
@test_throws ErrorException initdev("gpio", 3)
@test_throws ErrorException initdev("gpio", 5)
# Attempt to initialize a device with a very high index (no matching channel) # Attempt to initialize a device which has already been initialized
@test_throws ErrorException initdev("gpio", 1000) @test_throws ErrorException initdev("gpio", 1)
@test_throws ErrorException initdev("gpio", 3)
@test_throws ErrorException initdev("gpio", 5)
# Attempt to remove devices which have not been initialized # Attempt to initialize a device with a very high index (no matching channel)
@test_throws ErrorException closedev("gpio", 2) @test_throws ErrorException initdev("gpio", 1000)
@test_throws ErrorException closedev("gpio", 4)
@test_throws ErrorException closedev("gpio", 6)
#printdev("gpio", 3) # Attempt to remove devices which have not been initialized
@test_throws ErrorException closedev("gpio", 2)
@test_throws ErrorException closedev("gpio", 4)
@test_throws ErrorException closedev("gpio", 6)
# Remove devices from TOC #printdev("gpio", 3)
closedev("gpio", 1)
@test sum(listdev()) == 2
closedev("gpio", 3) # Remove devices from TOC
@test sum(listdev()) == 1 closedev("gpio", 1)
@test sum(listdev()) == 2
closedev("gpio", 5) closedev("gpio", 3)
@test sum(listdev()) == 0 @test sum(listdev()) == 1
end closedev("gpio", 5)
@test sum(listdev()) == 0
@testset "GPIO Read and write" begin
# Fixture
device = initdev("gpio", 1)
# Test that an exception is thrown when an invalid operation is given
# supported operations are 1,2,3
@test_throws ErrorException write!(device, (0, "something"))
@test_throws ErrorException write!(device, (4, "something"))
# Test that exceptions are thrown for each individual operation
@test_throws ErrorException write!(device, (1, "bad_entry"))
@test_throws ErrorException write!(device, (2, "bad_entry"))
@test_throws ErrorException write!(device, (3, "bad_entry"))
# Test operation 1
sleep(0.001);write!(device, (1, "1"))
sleep(0.001);@test read(device, 1) == "1"
sleep(0.001);write!(device, (1, "0"))
sleep(0.001);@test read(device, 1) == "0"
sleep(0.001);write!(device, (1, "1"))
sleep(0.001);@test read(device, 1) == "1"
sleep(0.001);write!(device, (1, "0"))
sleep(0.001);@test read(device, 1) == "0"
if false
# TODO fix write function to clear files before writing otherwise writing "xy" when a file containt "abcd" results in "xycd" and a system failure
# Test operation 2
sleep(0.001);write!(device, (2, "in"))
sleep(0.001);@test read(device, 2) == "in"
sleep(0.001);write!(device, (2, "out"))
sleep(0.001);@test read(device, 2) == "out"
sleep(0.001);write!(device, (2, "in"))
sleep(0.001);@test read(device, 2) == "in"
sleep(0.001);write!(device, (2, "out"))
sleep(0.001);@test read(device, 2) == "out"
# Test operation 3
sleep(0.001);write!(device, (3, "none"))
sleep(0.001);@test read(device, 3) == "none"
sleep(0.001);write!(device, (3, "rising"))
sleep(0.001);@test read(device, 3) == "rising"
sleep(0.001);write!(device, (3, "falling"))
sleep(0.001);@test read(device, 3) == "falling"
sleep(0.001);write!(device, (3, "both"))
sleep(0.001);@test read(device, 3) == "both"
end end
# Close Gpio @testset "Read/Write" begin
closedev("gpio", 1)
end # Fixture
device = initdev("gpio", 1)
@testset "IO Communication" begin
# Instanciate all possible leds and perform 10 read/write commands # Test that an exception is thrown when an invalid operation is given
# with the set high/low operation ("value") # supported operations are 1,2,3
@test_throws ErrorException write!(device, (0, "something"))
@test_throws ErrorException write!(device, (4, "something"))
# Test that exceptions are thrown for each individual operation
@test_throws ErrorException write!(device, (1, "bad_entry"))
@test_throws ErrorException write!(device, (2, "bad_entry"))
@test_throws ErrorException write!(device, (3, "bad_entry"))
# Test operation 1
write!(device, (1, "1"))
@test read(device, 1) == "1"
write!(device, (1, "0"))
@test read(device, 1) == "0"
write!(device, (1, "1"))
@test read(device, 1) == "1"
write!(device, (1, "0"))
@test read(device, 1) == "0"
write!(device, (2, "in"))
@test read(device, 2) == "in"
write!(device, (2, "out"))
@test read(device, 2) == "out"
write!(device, (2, "in"))
@test read(device, 2) == "in"
write!(device, (2, "out"))
@test read(device, 2) == "out"
# Configure the GPIO for output usage # Test operation 3
device = initdev("gpio", 1) @test_throws ErrorException write!(device, (3, "none"))
@test_throws ErrorException write!(device, (3, "rising"))
# Operation 2 -> in/out, set out @test_throws ErrorException write!(device, (3, "falling"))
write!(device, (2, "out")) @test_throws ErrorException write!(device, (3, "both"))
# Configure the GPIO for output usage # Close Gpio
for i = 1:10 closedev("gpio", 1)
state = "$(i%2)" end
sleep(0.10);write!(device, (1, state))
sleep(0.001);@test read(device, 1) == state @testset "All channels" begin
# Instanciate all possible leds and perform 10 read/write commands
# with the set high/low operation ("value")
# Configure the GPIO for output usage
devices = []
for ii = 1:length(gpio_channels)
device = initdev("gpio", ii)
# Operation 2 -> in/out, set out
write!(device, (2, "out"))
# Append to list
append!(devices, [device])
end
# Sets all available GPIO pins high/low in an alternating pattern
for i = 1:10
for ii = 1:length(gpio_channels)
state = "$(i%2)"
write!(devices[ii], (1, state))
@test read(devices[ii], 1) == state
end
sleep(0.10)
end
# Closes all devices
for ii = 1:length(gpio_channels)
closedev("gpio", ii)
end
end end
closedev("gpio", 1)
end end
using Base.Test using Base.Test
# TODO write tests for PWM @testset "PWM tests" begin
@testset "PWM Inialization and termination" begin @testset "Inialization and termination" begin
@test 1 == 1 @test 1 == 1
end end
@testset "PWM Read and write" begin @testset "Read and write" begin
@test 1 == 1 @test 1 == 1
end
end end
using LabConnections.BeagleBone using LabConnections.BeagleBone
import LabConnections.BeagleBone: getdev, write!, closedev, read, initdev import LabConnections.BeagleBone: getdev, write!, closedev, read, initdev, printdev, listdev
using Base.Test using Base.Test
@testset "SYS LED Tests" begin @testset "SYS LED Tests" begin
@testset "Inialization/Termination" begin
# Initialize three devices
initdev("sysled", 1)
@test sum(listdev()) == 1
initdev("sysled", 3)
@test sum(listdev()) == 2
# Attempt to initialize a device which has already been initialized
@test_throws ErrorException initdev("sysled", 1)
@test_throws ErrorException initdev("sysled", 3)
# Attempt to initialize a device with a very high index (no matching channel)
@test_throws ErrorException initdev("sysled", 1000)
# Attempt to remove devices which have not been initialized
@test_throws ErrorException closedev("sysled", 2)
@test_throws ErrorException closedev("sysled", 4)
# Remove devices from TOC
closedev("sysled", 1)
@test sum(listdev()) == 1
closedev("sysled", 3)
@test sum(listdev()) == 0
end
@testset "Error Handling" begin @testset "Error Handling" begin
device = initdev("sysled", 1) device = initdev("sysled", 1)
...@@ -17,20 +45,27 @@ using Base.Test ...@@ -17,20 +45,27 @@ using Base.Test
@testset "IO Communication" begin @testset "IO Communication" begin
# Instanciate all possible leds and perform 10 read/write commands # Instanciate all possible leds and perform 10 read/write commands
println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
device1 = initdev("sysled", 1) device1 = initdev("sysled", 1)
device2 = initdev("sysled", 2) device2 = initdev("sysled", 2)
device3 = initdev("sysled", 3) device3 = initdev("sysled", 3)
device4 = initdev("sysled", 4) device4 = initdev("sysled", 4)
for i = 1:10 for i = 1:10
state = stateA = "$(i%2)"
write!(device1, "$(i%2)") stateB = "$((i+1)%2)"
write!(device2, "$((i+1)%2)") write!(device1, stateA)
write!(device3, "$(i%2)") @test read(device1) == stateA
write!(device4, "$((i+1)%2)") write!(device2, stateB)
@test read(device2) == stateB
write!(device3, stateA)
@test read(device3) == stateA
write!(device4, stateB)
@test read(device4) == stateB
sleep(0.1) sleep(0.1)
end end
println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
closedev("sysled", 1) closedev("sysled", 1)
closedev("sysled", 2) closedev("sysled", 2)
closedev("sysled", 3) closedev("sysled", 3)
......
...@@ -2,7 +2,8 @@ using Base.Test ...@@ -2,7 +2,8 @@ using Base.Test
# This flag is enabled if a dummy filesystem should be used for testing (for online testing) # This flag is enabled if a dummy filesystem should be used for testing (for online testing)
# disabling the flag allows the BBB to be run in the loop, in this case blinking LEDS # disabling the flag allows the BBB to be run in the loop, in this case blinking LEDS
RUNNING_TESTS = true #RUNNING_TESTS = true
include("BeagleBone/SYS_LED_test.jl")
include("BeagleBone/GPIO_test.jl") include("BeagleBone/GPIO_test.jl")
include("BeagleBone/PWM_test.jl") include("BeagleBone/PWM_test.jl")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment