PropWare  3.0.0.229
C++ objects and CMake build system for Parallax Propeller
i2cslave.h
Go to the documentation of this file.
1 
27 #pragma once
28 
29 #include <PropWare/gpio/pin.h>
32 
33 namespace PropWare {
34 
47 class I2CSlave: public Runnable {
48  public:
49  static const Pin::Mask DEFAULT_SCL_MASK = Pin::Mask::P28;
50  static const Pin::Mask DEFAULT_SDA_MASK = Pin::Mask::P29;
51 
52  public:
66  template<size_t BUFFER_SIZE, size_t STACK_SIZE>
67  I2CSlave (const uint8_t address, uint8_t (&buffer)[BUFFER_SIZE], const uint32_t (&stack)[STACK_SIZE],
68  const Pin::Mask sclMask = DEFAULT_SCL_MASK, const Pin::Mask sdaMask = DEFAULT_SDA_MASK)
69  : Runnable(stack),
70  m_slaveAddress(address),
71  m_scl(sclMask),
72  m_sda(sdaMask),
73  m_buffer(buffer),
74  m_bufferUpperBound(BUFFER_SIZE - 1),
75  m_bufferPtr(BUFFER_SIZE) {
76  }
77 
92  I2CSlave (const uint8_t address, uint8_t *buffer, const size_t bufferSize, const uint32_t *stack,
93  const size_t stackSize, const Pin::Mask sclMask = DEFAULT_SCL_MASK,
94  const Pin::Mask sdaMask = DEFAULT_SDA_MASK)
95  : Runnable(stack, stackSize),
96  m_slaveAddress(address),
97  m_scl(sclMask),
98  m_sda(sdaMask),
99  m_buffer(buffer),
100  m_bufferUpperBound(bufferSize - 1),
101  m_bufferPtr(bufferSize) {
102  }
103 
107  void run () {
108  this->m_scl.set_dir_in();
109  this->m_sda.set_dir_in();
110  this->m_scl.clear();
111  this->m_sda.clear();
112 
113  const uint_fast8_t slaveAddress = this->m_slaveAddress;
114 
115  while (true) {
116  this->await_start();
117  while (true) {
118  const uint_fast8_t address = this->read_address();
119  if ((address >> 1) == slaveAddress) { // Master is talking to us
120  this->send_ack(); // Tell master we are there
121  if (address & BIT_0) { // Master wants us to speak
122  this->m_requestEnded = false;
123  this->on_request();
124  break;
125  } else { // Master wants us to listen
126  const bool restart = this->read_to_end();
127  this->on_receive();
128  this->reset_receive_buffer();
129  if (!restart)
130  break; // received stop, go back to outer loop and await new start
131  }
132  } else // Master is talking to another slave
133  //The next thing that interests us now is the next start -> go to await_start();
134  break;
135  }
136  }
137  }
138 
144  size_t available () const {
145  return this->m_bufferUpperBound - this->m_bufferPtr + 1;
146  }
147 
153  int read () {
154  if (this->m_bufferPtr <= this->m_bufferUpperBound)
155  return this->m_buffer[this->m_bufferPtr++];
156  else
157  return -1;
158  }
159 
167  void write (const uint8_t data) {
168  if (!this->m_requestEnded) {
169  uint32_t dataMask; //Initialized by setting BIT_7 in asm below
170 
171  __asm__ volatile(
172  " mov %[_dataMask], #128 \n\t" // Initialize the mask that specifies the bit from the byte to send
173  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for the clock to be low first
174  " or dira, %[_SDAMask] \n\t" // Take SDA >after< clock is low (master has sda, since he is sending an ACK)
175 
176  "loop%=: "
177  " test %[_data], %[_dataMask] wz \n\t" // Test whether bit to send is 0 or 1
178  " muxnz outa, %[_SDAMask] \n\t" // Set the bit on the bus while the clock is low
179  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // Wait for the next clock cycle to start
180  " shr %[_dataMask], #1 wz \n\t" // Shift the mask one down to select the next lower bit
181  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for the clock cycle to end
182  "if_nz brs #loop%= \n\t" // Continue until dataMask is 0 (no bit left)
183 
184  //wait for ack
185  " andn dira, %[_SDAMask] \n\t" // Set SDA to input, because master has to pull it down
186  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // Wait for the ACK-Clock to begin
187  " test %[_SDAMask], ina wz \n\t" // Test whether master pulled SDA down or not
188  "if_z mov %[_requestEnded], #0 \n\t" // SDA low -> ACK
189  "if_nz mov %[_requestEnded], #1 \n\t" // SDA high -> NAK
190  : // Output
191  [_dataMask] "+r"(dataMask),
192  [_requestEnded] "+r"(this->m_requestEnded)
193  : // Input
194  [_SDAMask] "r"(this->m_sda.get_mask()),
195  [_SCLMask] "r"(this->m_scl.get_mask()),
196  [_data] "r"(data));
197  }
198  }
199 
200  protected:
209  virtual void on_request () = 0;
210 
218  virtual void on_receive () = 0;
219 
220  private:
224  void await_start () const {
225  __asm__ volatile(
226  "loop%=: "
227  " waitpeq %[_SDAMask], %[_SDAMask] \n\t" // Wait for sda to be high
228  " waitpne %[_SDAMask], %[_SDAMask] \n\t" // Wait for sda to get low
229  " test %[_SCLMask], ina wz \n\t" // If scl was high while sda got low...
230  "if_z brs #loop%= \n\t" // ... return, otherwise: start anew
231  : // Output
232  : // Input
233  [_SDAMask] "r"(this->m_sda.get_mask()),
234  [_SCLMask] "r"(this->m_scl.get_mask()));
235  }
236 
240  uint_fast8_t read_address () const {
241  uint32_t result;
242  uint32_t bitCounter;
243 
244  __asm__ volatile(
245  FC_START("ReadAddressStart", "ReadAddressEnd")
246  " mov %[_result], #0 \n\t"
247  " mov %[_bitCounter], #8 \n\t"
248  "nextBit%=: "
249  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for clock to get low (should already be low at this time)
250  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // Wait for clock to get high
251  " test %[_SDAMask], ina wc \n\t" // Read bit from bus ...
252  " rcl %[_result], #1 \n\t" // ... and store in result
253  " djnz %[_bitCounter], #" FC_ADDR("nextBit%=", "ReadAddressStart") " \n\t"
254  FC_END("ReadAddressEnd")
255  :[_result] "+r"(result),
256  [_bitCounter] "+r"(bitCounter)
257  :[_SDAMask] "r"(this->m_sda.get_mask()),
258  [_SCLMask] "r"(this->m_scl.get_mask())
259  );
260  return result;
261  }
262 
266  inline __attribute__((always_inline)) void send_ack () const {
267  //The code does not work anymore when removing inline and attribute always_inline. Why is this?
268  __asm__ volatile(
269  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for SCL to be low first
270  " or dira, %[_SDAMask] \n\t" // Take SDA and ...
271  " andn outa, %[_SDAMask] \n\t" // ... pull it down
272  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // Wait for SCL to go high ...
273  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // ... and wait for it to go low again
274  " andn dira, %[_SDAMask] \n\t" // Let go of SDA again (high by float)
275  : : // Inputs
276  [_SDAMask] "r"(this->m_sda.get_mask()),
277  [_SCLMask] "r"(this->m_scl.get_mask()));
278  }
279 
285  bool read_to_end () {
286  uint32_t result;
287  uint32_t bitCounter;
288  uint32_t isRestart;
289 
290  while (true) {
291  __asm__ volatile(
292  " mov %[_isRestart], #2 \n\t"
293  " mov %[_bitCounter], #7 \n\t"
294  " mov %[_result], #0 \n\t"
295  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for scl to be low first
296  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // Wait for scl to go high
297  " test %[_SDAMask], ina wc \n\t" // Read bit and...
298  " rcl %[_result], #1 \n\t" // ... store in result
299  "if_c brs #DetectRestart%= \n\t" // The first bit of a received byte may be b7, or a stop / restart
300  // If sda was high, it can only be a restart
301  "DetectStop%=: "
302  " test %[_SCLMask], ina wz \n\t" // scl went low -> no chance for a stop-condition to be detected ...
303  "if_z brs #loop%= \n\t" // ... continue receiving data bits
304  " test %[_SDAMask], ina wz \n\t"
305  "if_nz mov %[_isRestart], #0 \n\t" // stop detected. Set isRestart to false ...
306  "if_nz brs #ReceiveEnd%= \n\t" // ... and exit
307  " brs #DetectStop%= \n\t"
308 
309  "DetectRestart%=: "
310  " test %[_SCLMask], ina wz \n\t" // scl went low -> no chance for a (re)start-condition to be detected ...
311  "if_z brs #loop%= \n\t" // ... continue receiving data bits
312  " test %[_SDAMask], ina wz \n\t"
313  "if_z mov %[_isRestart], #1 \n\t" // restart detected. Set isRestart to true...
314  "if_z brs #ReceiveEnd%= \n\t" // ... and exit
315  " brs #DetectRestart%= \n\t"
316 
317  "loop%=: " // for(int i = 0; i < 8; ++i) {
318  " waitpne %[_SCLMask], %[_SCLMask] \n\t" // Wait for ...
319  " waitpeq %[_SCLMask], %[_SCLMask] \n\t" // ... next clock
320  " test %[_SDAMask], ina wc \n\t" // Read bit and...
321  " rcl %[_result], #1 \n\t" // ... store in result
322  " sub %[_bitCounter], #1 wz \n\t"
323  "if_nz brs #loop%= \n\t" // }
324 
325  "ReceiveEnd%=: "
326  : // Outputs
327  [_result] "+r"(result),
328  [_bitCounter] "+r"(bitCounter),
329  [_isRestart] "+r"(isRestart)
330  : // Inputs
331  [_SDAMask] "r"(this->m_sda.get_mask()),
332  [_SCLMask] "r"(this->m_scl.get_mask()));
333 
334  if (2 == isRestart) {
335  this->send_ack();
336  this->append_receive_buffer(static_cast<uint8_t>(result));
337  } else {
338  return static_cast<bool>(isRestart);
339  }
340  }
341  }
342 
346  void append_receive_buffer (const uint8_t data) {
347  if (this->m_bufferPtr)
348  this->m_buffer[--this->m_bufferPtr] = data;
349  }
350 
354  void reset_receive_buffer () {
355  this->m_bufferPtr = this->m_bufferUpperBound + 1;
356  }
357 
358  private:
359  const uint8_t m_slaveAddress;
360  const Pin m_scl;
361  const Pin m_sda;
362 
366  uint8_t *m_buffer;
370  uint32_t m_bufferUpperBound;
374  uint32_t m_bufferPtr;
375  bool m_requestEnded;
376 };
377 
378 }
runnable.h
PropWare::I2CSlave::available
size_t available() const
Get the amount of bytes in the receive buffer.
Definition: i2cslave.h:144
PropWare::I2CSlave::I2CSlave
I2CSlave(const uint8_t address, uint8_t *buffer, const size_t bufferSize, const uint32_t *stack, const size_t stackSize, const Pin::Mask sclMask=DEFAULT_SCL_MASK, const Pin::Mask sdaMask=DEFAULT_SDA_MASK)
Create an I2C slave object (Allows dynamic allocation of buffer and stack)
Definition: i2cslave.h:92
PropWare::Port::Mask
Mask
Definition: port.h:43
PropWare::I2CSlave::read
int read()
Read the next byte from the receiveBuffer.
Definition: i2cslave.h:153
PropWare::I2CSlave::write
void write(const uint8_t data)
Send the given byte of data on the bus during a request from the bus master.
Definition: i2cslave.h:167
PropWare::I2CSlave::I2CSlave
I2CSlave(const uint8_t address, uint8_t(&buffer)[BUFFER_SIZE], const uint32_t(&stack)[STACK_SIZE], const Pin::Mask sclMask=DEFAULT_SCL_MASK, const Pin::Mask sdaMask=DEFAULT_SDA_MASK)
Create an I2CSlave object (requires static allocation of buffer and stack)
Definition: i2cslave.h:67
PropWare::Port::set_dir_in
void set_dir_in() const
Set the port for input.
Definition: port.h:217
simpleport.h
PropWare::Runnable
Helper class for creating easy parallel applications.
Definition: runnable.h:75
PropWare::I2CSlave::run
void run()
Enter the loop that will watch and operate the bus.
Definition: i2cslave.h:107
PropWare::I2CSlave
Basic I2C slave driver.
Definition: i2cslave.h:47
pin.h
PropWare
Generic definitions and functions for the Parallax Propeller.
Definition: runnable.h:33
PropWare::Port::clear
void clear() const
Clear selected output port (set it to 0)
Definition: port.h:249