SatCat5
cfgbus_spi.cc
1 // Copyright 2021-2024 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
6 #include <satcat5/cfgbus_spi.h>
7 #include <satcat5/log.h>
8 #include <satcat5/utils.h>
9 
10 namespace cfg = satcat5::cfg;
11 namespace util = satcat5::util;
12 
13 // Command opcodes
14 static inline u32 CMD_OPCODE(u32 c, u32 x)
15  {return ((c << 8) | (x & 0xFF));}
16 static inline u32 CMD_START(u32 x)
17  {return CMD_OPCODE(0, x);}
18 static inline u32 CMD_TXONLY(u32 x)
19  {return CMD_OPCODE(1, x);}
20 static inline u32 CMD_TXRX(u32 x)
21  {return CMD_OPCODE(2, x);}
22 static const u32 CMD_RXONLY = CMD_OPCODE(3, 0);
23 static const u32 CMD_STOP = CMD_OPCODE(4, 0);
24 
25 cfg::Spi::Spi(ConfigBus* cfg, unsigned devaddr)
26  : cfg::MultiSerial(cfg, devaddr, SATCAT5_SPI_MAXCMD,
27  m_txbuff, SATCAT5_SPI_TXBUFF,
28  m_rxbuff, SATCAT5_SPI_RXBUFF)
29 {
30  // No other initialization at this time.
31 }
32 
34  unsigned clkref_hz,
35  unsigned baud_hz,
36  unsigned mode)
37 {
38  u32 clkdiv = util::div_ceil_u32(clkref_hz, 2 * baud_hz);
39  m_ctrl[REGADDR_CFG] = (mode << 8) | (clkdiv);
40 }
41 
43 {
44  return !idle();
45 }
46 
48  u8 devidx, const u8* wrdata, u8 rwbytes,
49  cfg::SpiEventListener* callback)
50 {
51  // How many opcodes required for this command?
52  unsigned ncmd = 2 + rwbytes;
53 
54  // Can we queue this command now?
55  if (!write_check(ncmd, rwbytes)) return false;
56 
57  // Queue up each opcode.
58  m_tx.write_u16(CMD_START(devidx));
59  for (unsigned a = 0 ; a < rwbytes ; ++a)
60  m_tx.write_u16(CMD_TXRX(wrdata[a]));
61  m_tx.write_u16(CMD_STOP);
62 
63  // Finalize write and note metadata for later.
64  unsigned idx = write_finish();
65  m_callback[idx] = callback;
66  return true; // Success!
67 }
68 
70  u8 devidx, const u8* wrdata, u8 wrbytes, u8 rdbytes,
71  cfg::SpiEventListener* callback)
72 {
73  // How many opcodes required for this command?
74  unsigned ncmd = 2 + wrbytes + rdbytes;
75 
76  // Can we queue this command now?
77  if (!write_check(ncmd, rdbytes)) return false;
78 
79  // Queue up each opcode.
80  m_tx.write_u16(CMD_START(devidx));
81  for (unsigned a = 0 ; a < wrbytes ; ++a)
82  m_tx.write_u16(CMD_TXONLY(wrdata[a]));
83  for (unsigned a = 0 ; a < rdbytes ; ++a)
84  m_tx.write_u16(CMD_RXONLY);
85  m_tx.write_u16(CMD_STOP);
86 
87  // Finalize write and note metadata for later.
88  unsigned idx = write_finish();
89  m_callback[idx] = callback;
90  return true; // Success!
91 }
92 
93 void cfg::Spi::read_done(unsigned idx)
94 {
95  u8 rxbuff[SATCAT5_SPI_RXBUFF];
96  if (m_callback[idx]) {
97  // Copy data to working buffer; ignore the extraneous error flag.
98  unsigned nread = m_rx.get_read_ready();
99  if (nread > 1) m_rx.read_bytes(nread-1, rxbuff);
100  // Notify callback.
101  m_callback[idx]->spi_done(nread-1, rxbuff);
102  }
103 }
Generic ConfigBus API.
Definition: cfgbus_core.h:124
Partial driver for the multipurpose serial peripheral.
SPI Event Handler callback for use with SpiGeneric.
Definition: cfg_spi.h:15
void configure(unsigned clkref_hz, unsigned baud_hz, unsigned mode=3)
Configure or reconfigure the SPI controller.
Definition: cfgbus_spi.cc:33
void read_done(unsigned cidx)
Callback when each transaction is finished.
Definition: cfgbus_spi.cc:93
bool busy() override
Is the SPI controller currently busy?
Definition: cfgbus_spi.cc:42
bool exchange(u8 devidx, const u8 *wrdata, u8 rwbytes, satcat5::cfg::SpiEventListener *callback=0) override
Queue a bus transaction that reads and writes concurrently.
Definition: cfgbus_spi.cc:47
bool query(u8 devidx, const u8 *wrdata, u8 wrbytes, u8 rdbytes, satcat5::cfg::SpiEventListener *callback=0) override
Queue a bus transation that reads, then writes.
Definition: cfgbus_spi.cc:69
Spi(ConfigBus *cfg, unsigned devaddr)
Link driver to a specific ConfigBus address.
Definition: cfgbus_spi.cc:25
Diagnostic logging to UART and/or Ethernet ports.
Miscellaneous mathematical utility functions.