-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | An interface to the Silicon Labs Si5351 clock chip -- -- An experimental interface to the Silicon Labs I2C-programmable -- any-frequency CMOS clock generator and VCXO. (tested with the Si5351 -- cip). @package si-clock @version 0.1.0 -- | I2C related functions. module Hardware.SiClock.I2C type Device = Fd -- | Synth a is the monad for Si PPL synthesizers. type SynthT env m a = ReaderT (env, Device) m a -- | Run an IO action with an I2C device and an I2C address. runI2CWith :: FilePath -> Addr -> (Device -> IO a) -> IO a -- | Lift an IO action on an I2C device to Synth. deviceIO :: MonadIO m => (Device -> IO a) -> SynthT env m a -- | Dump the content of the IC registers. (For testing) dumpRegisters :: MonadIO m => SynthT env m () writeByteData :: MonadIO m => Command -> Word8 -> SynthT env m () writeI2CBlockData :: MonadIO m => Command -> ByteString -> SynthT env m () -- | This module contains utility functions for fractional PLL divers. Most -- arduino Si5351 packages use dividers with a fixed large denominator, -- which seens to be against the spirit of the Si5351 design. I use -- continued fractions to compute the best fractional approximation. -- (This may be an overkill but why not.) module Hardware.SiClock.Divider -- | Compute the pvalues for a rational divider. Any denominator is -- pemissible here. The function uses the best approximation. dividerToPVal :: Rational -> (Word32, Word32, Word32) -- | Approximate the a,b,c values of a divider. dividerToABC :: Rational -> (Integer, Integer, Integer) -- | Compute a list of approximations of a rational number. approximations :: Rational -> [Rational] -- | continued fraction stuff. toContinuedFraction :: Rational -> (Integer, [Integer]) -- | continued fraction stuff. fromContinuedFraction :: (Integer, [Integer]) -> Rational -- | This is the main API. module Hardware.SiClock type Frequency = Rational type Divider = Rational data Config Config :: FilePath -> Word8 -> Frequency -> Frequency -> Config [_I2CDevice] :: Config -> FilePath [_I2CAddress] :: Config -> Word8 [_XtalFrequency] :: Config -> Frequency [_maxPLLFrequency] :: Config -> Frequency -- | The defaultConfig if no environment variables are set. defaultConfig :: Config -- | Check that defaultConfigEnv matches your hardware before you use it. -- Do not run any SiPLL code on a wrong i2c-bus, i.e. an internal I2C bus -- of your PC. (it might confuse and or wreck) your hardware. You can -- overwrite config values with the following ENV variables: -- SI_CLOCK_I2C_DEVICE SI_CLOCK_I2C_ADDRESS SI_CLOCK_XTAL_FREQUENCY -- SI_CLOCK_MAX_PLL_FREQUENCY defaultConfigEnv :: HasCallStack => IO Config -- | Quick test for the I2C connection (with default config). testIO :: IO () type Synth a = forall m. MonadIO m => SynthT Config m a -- | Run the Synth monad with the config from defaultConfigEnv. | .i.e. -- reading Env runSynth :: HasCallStack => SynthT Config IO a -> IO a -- | Run the Synth monad with a custom configuration. runSynthWith :: HasCallStack => Config -> SynthT Config IO a -> IO a askXtalFrequency :: Synth Frequency askMaxPLLFrequency :: Synth Frequency -- | An IC has PLL_A and PLL_B. data PLL PLL_A :: PLL PLL_B :: PLL -- | An IC has up to 8 clocks. (CLK_0..CLK_7). data CLK CLK_0 :: CLK CLK_1 :: CLK CLK_2 :: CLK CLK_3 :: CLK CLK_4 :: CLK CLK_5 :: CLK CLK_6 :: CLK CLK_7 :: CLK -- | Reset (both?) PLLs pllReset :: Synth () -- | Turn on CLK_0 output. clk0_On :: Synth () -- | Turn off CLK_0 output. clk0_Off :: Synth () data DividerPair DividerPair :: Divider -> Divider -> DividerPair [_pllDivider] :: DividerPair -> Divider [_clkDivider] :: DividerPair -> Divider -- | Set PLL and Clock dividers for a frequency. setDividers :: PLL -> CLK -> Frequency -> Synth DividerPair -- | Compute a pair of good default pll and clk dividers. (clk divider is -- an integer) defaultDividers :: Frequency -> Synth DividerPair -- | Set a PLL fractional divider setPLLDivider :: PLL -> Divider -> Synth () -- | Short for setPLLDivider PLL_A setPLLDivider_A :: Divider -> Synth () -- | Short for setPLLDivider PLL_B setPLLDivider_B :: Divider -> Synth () -- | Setup a Clock divider. The rfield is passed as a plain Word8. (ToDo -- high level API for rfields). setCLKDivider :: CLK -> Word8 -> Divider -> Synth () -- | Bits in the clock control registers. data CLK_Control_bits CLK_on :: CLK_Control_bits CLK_off :: CLK_Control_bits CLK_fractional :: CLK_Control_bits CLK_integer :: CLK_Control_bits CLK_multiPLLA :: CLK_Control_bits CLK_multiPLLB :: CLK_Control_bits CLK_inverted :: CLK_Control_bits CLK_XTAL :: CLK_Control_bits CLK_CLKin :: CLK_Control_bits CLK_multi :: CLK_Control_bits CLK_DRV2 :: CLK_Control_bits CLK_DRV4 :: CLK_Control_bits CLK_DRV6 :: CLK_Control_bits CLK_DRV8 :: CLK_Control_bits setCLKControl :: CLK -> [CLK_Control_bits] -> Synth () setCLKControlRaw :: CLK -> Word8 -> Synth () controlBitsToWord8 :: [CLK_Control_bits] -> Word8 -- | A DividerConf is basically the bytestring that configures a fractional -- divider. newtype DividerConf DividerConf :: ByteString -> DividerConf [unDividerConf] :: DividerConf -> ByteString -- | Mangle a Divider and a rval into a DividerConf. This can be used to -- pre-compute all the math and to get the bits that define a divider. toDividerConf :: Word8 -> Divider -> DividerConf -- | Setup some fractional divider with a pre-computed config. Using a -- pre-computed config might be faster or more convenient. setDividerRaw :: DividerAddr hw => hw -> DividerConf -> Synth () -- | Get address of the fractional divider. class DividerAddr a toDividerAddr :: DividerAddr a => a -> Word8 -- | Address of a PLL divider. -- | Address of a Clock divider. -- | Generic Address of fractional divider. _SI_CLK0_CONTROL :: Word8 _SI_CLK1_CONTROL :: Word8 _SI_CLK2_CONTROL :: Word8 _SI_SYNTH_PLL_A :: Word8 _SI_SYNTH_PLL_B :: Word8 _SI_PLL_RESET :: Word8 _SI_CLOCK_I2C_DEVICE :: String _SI_CLOCK_I2C_ADDRESS :: String _SI_CLOCK_XTAL_FREQUENCY :: String _SI_CLOCK_MAX_PLL_FREQUENCY :: String instance GHC.Classes.Eq Hardware.SiClock.DividerConf instance GHC.Show.Show Hardware.SiClock.DividerConf instance GHC.Classes.Eq Hardware.SiClock.CLK_Control_bits instance GHC.Show.Show Hardware.SiClock.CLK_Control_bits instance GHC.Show.Show Hardware.SiClock.DividerPair instance GHC.Enum.Enum Hardware.SiClock.CLK instance GHC.Classes.Ord Hardware.SiClock.CLK instance GHC.Classes.Eq Hardware.SiClock.CLK instance GHC.Show.Show Hardware.SiClock.CLK instance GHC.Classes.Eq Hardware.SiClock.PLL instance GHC.Show.Show Hardware.SiClock.PLL instance GHC.Classes.Eq Hardware.SiClock.Config instance GHC.Show.Show Hardware.SiClock.Config instance Hardware.SiClock.DividerAddr Hardware.SiClock.PLL instance Hardware.SiClock.DividerAddr Hardware.SiClock.CLK instance Hardware.SiClock.DividerAddr GHC.Word.Word8 -- | Some experiments for transmitting FSK signals. TODO : clean up module Hardware.SiClock.FSK clkOff :: Synth () -- | A message in Baudot code. One start and 2 Stop bits someMsgBaudot :: String -- | 45 Baud speed. symbolTime45 :: DiffTime -- | RTTY is basically Baudot code + fsk2. rtty :: DiffTime -> Frequency -> String -> Synth () timedFrequencyHopping :: Frequency -> [(DiffTime, Frequency)] -> Synth () waitUntil :: DiffTime -> IO Int -- | This Module contains an example for transmitting a JT65 message. The -- message is a hardcoded 'hello world'. This Module does NOT contain an -- implementation of the JT65 codec. (The codec is in the jt65Codec -- package.) module Hardware.SiClock.JT65Test -- | Send a JT65 'hello world' on a given frequency. The transmission -- starts at beginning of the next full minute. jt65SendHelloWorld :: Frequency -> Synth () -- | The list of symbols of the 'hello world' message. (acutally not -- needed.) hello_world_symbols :: [Word8] -- | The list of pre computed frequencies for the 'hello world' message. hello_world_frequencies :: [Frequency] -- | Symbol start times relative to the full minute. jt65SymbolStartTimes :: [DiffTime] -- | Sending Morse code. Don't use it for real (hard key clicks). module Hardware.SiClock.MorseKeyer -- | Quick test. test :: IO () -- | Some Message in Morse Code. someMsg :: String -- | Some frequency for testing (10m Band) someFreq :: Frequency -- | Main time unit in mico-seconds. ditLen :: Int -- | Send a message. sendMsg :: Frequency -> String -> Synth () -- | Send a symbol. (either '.', '-' or ' ') sendSym :: Char -> Synth () ditDelay :: Synth () daDelay :: Synth () -- | Hard key on. rfOn :: Synth () -- | Hard key off. rfOff :: Synth () -- | This module collects some examples and tests. Unless stated otherwise -- the output pin is CLK_0. module Hardware.SiClock.Examples -- | Set Clk_0 to frequency f and turn on clock output. testSynth :: Frequency -> IO () -- | Turn off CLK_0. clkOff :: IO () -- | Read the chip registers. testI2CReading :: IO () someHopFrequencies :: [Frequency] -- | One example for frequency hopping. Here the PLL stays at one frequency -- and the clock divider is modified. testHopping :: [Frequency] -> IO () -- | Send a JT65 hello world message. testJT65 :: Frequency -> IO () -- | Send some message in morse code. testMorse :: Frequency -> IO () -- | Send some RTTY message testRTTY :: Frequency -> IO () -- | Switching the PLL causes some loud clicks (unwanted). testClicks1 :: Frequency -> IO () -- | No clicks here. I don't know why ? noClicks2 :: Frequency -> IO () module Hardware.SiClock.Utils -- | Sample an interval from start to end with steps. fromToSteps :: Rational -> Rational -> Integer -> [Rational] -- | Set the SI_CLOCK_I2C_DEVICE environment variable. Useful with ghci. setEnvI2CDevice :: String -> IO () -- | Set the SI_CLOCK_XTAL_FREQUENCY environment variable. Useful with -- ghci. setEnvXtalFrequency :: Integer -> IO () -- | setEnv name value sets the specified environment variable to -- value. -- -- On Windows setting an environment variable to the empty string -- removes that environment variable from the environment. For the sake -- of compatibility we adopt that behavior. In particular -- --
--   setEnv name ""
--   
-- -- has the same effect as -- --
--   unsetEnv name
--   
-- -- If you don't care about Windows support and want to set an environment -- variable to the empty string use System.Posix.Env.setEnv from -- the unix package instead. -- -- Throws IOException if name is the empty string or -- contains an equals sign. setEnv :: String -> String -> IO ()