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 +4/−4
- System/RaspberryPi/GPIO.hs +94/−13
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]