packages feed

HPi 0.4.0 → 0.5.0

raw patch · 2 files changed

+98/−17 lines, 2 filesdep ~basedep ~bytestring

Dependency ranges changed: base, bytestring

Files

HPi.cabal view
@@ -2,15 +2,15 @@ -- see http://haskell.org/cabal/users-guide/
 
 name:                HPi
-version:             0.4.0
-synopsis:            GPIO and I2C functions for the Raspberry Pi.
+version:             0.5.0
+synopsis:            GPIO, I2C and SPI functions for the Raspberry Pi.
 description:         This package is a FFI wrapper around the bcm2835 library by Mike McCauley, it also includes a few utility functions for easier use of the imported functions.
 homepage:            https://github.com/WJWH/HPi
 license:             BSD3
 license-file:        LICENSE
 author:              Wander Hillen	
 maintainer:          wjw.hillen@gmail.com
-copyright:           (c) 2013 Wander Hillen
+copyright:           (c) 2013-2015 Wander Hillen
 category:            System
 build-type:          Simple
 cabal-version:       >=1.8
@@ -18,7 +18,7 @@ library
   exposed-modules:     System.RaspberryPi.GPIO
   -- other-modules:       
-  build-depends:       base ==4.5.*, bytestring ==0.9.*
+  build-depends:    base < 5, bytestring < 0.11
   
 source-repository head
   type:     git
System/RaspberryPi/GPIO.hs view
@@ -8,6 +8,9 @@     PinMode(..),
     LogicLevel,
     Address,
+    SPIPin(..),
+    CPOL,
+    CPHA,
     -- *General functions
     withGPIO,
     -- *GPIO specific functions
@@ -20,14 +23,19 @@     setI2cBaudRate,
     writeI2C,
     readI2C,
-    writeReadI2C
+    writeReadRSI2C,
+    -- *SPI specific functions
+    withSPI,
+    chipSelectSPI,
+    setChipSelectPolaritySPI,
+    setDataModeSPI,
+    transferSPI,
+    transferManySPI
     ) where
 
 -- FFI wrapper over the I2C portions of the BCM2835 library by Mike McCauley, also some utility functions to
 -- make reading and writing simpler
 
---hook for the
-
 import Control.Applicative ((<$>))
 import Control.Exception
 import Foreign
@@ -40,7 +48,8 @@ 
 ------------------------------------------------------------------------------------------------------------------------------------
 --------------------------------------------- Data types ---------------------------------------------------------------------------
-
+-- |This describes the pins on the Raspberry Pi boards. Since the BCM2835 SOC internally uses different numbers (and these numbers 
+-- differ between versions, the library internally translates this pin number to the correct number.
 data Pin =  -- |Pins for the P1 connector of the V2 revision of the Raspberry Pi
             Pin03|Pin05|Pin07|Pin08|Pin10|Pin11|Pin12|Pin13|Pin15|Pin16|Pin18|Pin19|Pin21|Pin22|Pin23|Pin24|Pin26|
             -- |Pins for the P5 connector of the V2 revision of the Raspberry Pi
@@ -64,6 +73,15 @@ -- |Either high or low.
 type LogicLevel = Bool
 
+-- |This describes which Chip Select pins are asserted (used in SPI communications).
+data SPIPin = CS0 | CS1 | CSBOTH | CSNONE deriving (Eq, Show, Enum)
+
+-- |Clock polarity (CPOL) for SPI transmissions.
+type CPOL = Bool
+
+-- |Clock phase (CPHA) for SPI transmissions.
+type CPHA = Bool
+
 ------------------------------------------------------------------------------------------------------------------------------------
 ------------------------------------------ Foreign imports -------------------------------------------------------------------------
 
@@ -101,8 +119,27 @@ --read some bytes from the bus
 foreign import ccall unsafe "bcm2835.h bcm2835_i2c_read" c_readI2C :: CString -> CUShort -> IO CUChar
 --reads a certain register with the repeated start method
-foreign import ccall unsafe "bcm2835.h bcm2835_i2c_read_register_rs" c_writeReadI2C :: CString -> CString -> CUShort -> IO CUChar
+foreign import ccall unsafe "bcm2835.h bcm2835_i2c_write_read_rs" c_writeReadRSI2C :: CString -> CUInt -> CString -> CUInt -> IO CUChar
 
+------------------------------------------- SPI functions --------------------------------------------------------------------------
+--inits the SPI pins
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_begin" initSPI   :: IO ()
+--resets the SPI pins
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_end"   stopSPI   :: IO ()
+
+--Transfers one byte to and from the currently selected SPI slave
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_transfer"        c_transferSPI :: CUChar -> IO CUChar
+--Transfers multiple bytes to and from the currently selected SPI slave
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_transfern"      c_transferManySPI :: CString -> CUInt -> IO ()
+--Changes the chip select pins
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_chipSelect"      c_chipSelectSPI :: CUChar -> IO ()
+
+--Sets whether SPI Chip Select pulls pins high or low.
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_setChipSelectPolarity" c_setChipSelectPolarity :: CUChar -> CUChar -> IO ()
+--Sets the data mode used (phase/polarity)
+foreign import ccall unsafe "bcm2835.h bcm2835_spi_setDataMode"     c_setDataModeSPI :: CUChar -> IO ()
+
+
 ------------------------------------------------------------------------------------------------------------------------------------
 ------------------------------------------ Exportable functions --------------------------------------------------------------------
 
@@ -125,7 +162,16 @@                         stopI2C
                         f
 
---
+-- |Any IO computation that uses the SPI functionality using this library should be wrapped with this function; ie @withSPI $ do foo@.
+-- It prepares the relevant pins for use with the SPI protocol and makes sure everything is safely returned to normal if an exception
+-- occurs. If you only use the GPIO pins for SPI, you can do @withGPIO . withSPI $ do foo@ and it will work as expected. WARNING: 
+-- after this function returns, the SPI pins will be set to Input, so use 'setPinFunction' if you want to use them for output.
+withSPI :: IO a -> IO a
+withSPI f = bracket_    initSPI
+                        stopSPI
+                        f
+                        
+-- Possible error results for I2C functions.
 actOnResult :: CUChar -> CString -> IO BS.ByteString
 actOnResult rr buf = case rr of
     0x01 -> throwIO $ IOError Nothing IllegalOperation "I2C: " "Received an unexpected NACK." Nothing Nothing
@@ -207,7 +253,7 @@ setI2cBaudRate :: Word32 -> IO ()
 setI2cBaudRate a = c_setBaudRateI2C $ fromIntegral a
 
--- |Writes the data in the 'ByteString' to the specified adress. Throws an IOException if an error occurs.
+-- |Writes the data in the 'ByteString' to the specified I2C 'Address'. Throws an IOException if an error occurs.
 writeI2C :: Address -> BS.ByteString -> IO ()	--writes a bytestring to the specified address
 writeI2C address by = BS.useAsCString by $ \bs -> do
     setI2cAddress address
@@ -218,18 +264,53 @@         0x04 -> throwIO $ IOError Nothing IllegalOperation "I2C: " "Not all data was read." Nothing Nothing
         0x00 -> return ()
 
--- |Reads num bytes from the specified address. Throws an IOException if an error occurs.
+-- |Reads num bytes from the specified 'Address'. Throws an IOException if an error occurs.
 readI2C :: Address -> Int -> IO BS.ByteString --reads num bytes from the specified address
 readI2C address num = allocaBytes (num+1) $ \buf -> do --is the +1 necessary??
     setI2cAddress address
     readresult <- c_readI2C buf (fromIntegral num)
     actOnResult readresult buf
 
--- |Writes a 'ByteString' containing a register address to the specified address, then reads num bytes from
--- it, using the \"repeated start\" I2C method. Throws an IOException if an error occurs.
-writeReadI2C :: Address -> BS.ByteString -> Int -> IO BS.ByteString
-writeReadI2C address by num = BS.useAsCString by $ \bs -> do --marshall the register-containing bytestring
+-- |Writes the data in the 'ByteString' to the specified 'Address', then issues a "repeated start" (with no prior stop) and then 
+-- reads num bytes from the same 'Address'. Necessary for devices that require such behavior, such as the MLX90620.
+writeReadRSI2C :: Address -> BS.ByteString -> Int -> IO BS.ByteString
+writeReadRSI2C address by num = BS.useAsCString by $ \bs -> do --marshall the register-containing bytestring
     allocaBytes (num+1) $ \buf -> do	--allocate a buffer for the response
         setI2cAddress address
-        readresult <- c_writeReadI2C bs buf (fromIntegral num)
+        readresult <- c_writeReadRSI2C bs (fromIntegral $ BS.length by) buf (fromIntegral num)
         actOnResult readresult buf
+        
+-------------------------------------------- SPI functions -------------------------------------------------------------------------
+-- |Sets the chip select pin(s). When a transfer is made with 'transferSPI' or 'transferManySPI', the selected pin(s) will be 
+-- asserted during the transfer. 
+chipSelectSPI :: SPIPin -> IO ()
+chipSelectSPI pin = c_chipSelectSPI (fromIntegral . fromEnum $ pin)
+
+-- |Sets the chip select pin polarity for a given pin(s). When a transfer is made with 'transferSPI' or 'transferManySPI', the 
+-- currently selected chip select pin(s) will be asserted to the LogicLevel supplied. When transfers are not happening, the chip 
+-- select pin(s) return to the complement (inactive) value. 
+setChipSelectPolaritySPI :: SPIPin -> LogicLevel -> IO ()
+setChipSelectPolaritySPI pin level = c_setChipSelectPolarity (fromIntegral . fromEnum $ pin) (fromIntegral . fromEnum $ level)
+
+-- |Sets the SPI clock polarity and phase (ie, CPOL and CPHA)
+setDataModeSPI :: (CPOL,CPHA) -> IO ()
+setDataModeSPI (False,False) = c_setDataModeSPI 0
+setDataModeSPI (False,True)  = c_setDataModeSPI 1
+setDataModeSPI (True,False)  = c_setDataModeSPI 2
+setDataModeSPI (True,True)   = c_setDataModeSPI 3
+
+-- |Transfers one byte to and from the currently selected SPI slave. Asserts the currently selected CS pins (as previously set by 
+-- 'chipSelectSPI') during the transfer. Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. Returns the 
+-- read data byte from the slave.
+transferSPI :: Word8 -> IO Word8
+transferSPI input = fromIntegral <$> c_transferSPI (fromIntegral input)
+
+-- |Transfers any number of bytes to and from the currently selected SPI slave, one byte at a time. Asserts the currently selected 
+-- CS pins (as previously set by 'chipSelectSPI') during the transfer. Clocks 8 bit bytes out on MOSI, and simultaneously clocks in 
+-- data from MISO.
+transferManySPI :: [Word8] -> IO [Word8]
+transferManySPI input = BS.useAsCStringLen (BS.pack input) $ \(buf,len) -> do --convert input list to bytestring and from there to CString
+    --returns the read bytes in buf. Uses CStringLen because the responses might have zero bytes and this will influence the result if a
+    --normal CString is used
+    c_transferManySPI buf (fromIntegral len) --
+    (BS.packCStringLen (buf,len)) >>= return . BS.unpack -- translate back from a buffer to a bytestring to a [Word8]