PropWare  3.0.0.229
C++ objects and CMake build system for Parallax Propeller
i2cmaster.h
Go to the documentation of this file.
1 
27 #pragma once
28 
29 #include <PropWare/gpio/pin.h>
30 
31 namespace PropWare {
32 
64 class I2CMaster {
65  public:
66  static const Pin::Mask DEFAULT_SCL_MASK = Pin::Mask::P28;
67  static const Pin::Mask DEFAULT_SDA_MASK = Pin::Mask::P29;
68  static const unsigned int DEFAULT_FREQUENCY = 400000;
69 
70  public:
78  I2CMaster (const Pin::Mask sclMask = DEFAULT_SCL_MASK, const Pin::Mask sdaMask = DEFAULT_SDA_MASK,
79  const unsigned int frequency = DEFAULT_FREQUENCY)
80  : m_scl(sclMask, Pin::Dir::IN),
81  m_sda(sdaMask, Pin::Dir::IN) {
82  this->set_frequency(frequency);
83 
84  //Set outputs low
85  this->m_scl.clear();
86  this->m_sda.clear();
87  }
88 
94  void set_frequency (const unsigned int frequency) {
95  this->m_clockDelay = CLKFREQ / (frequency << 1);
96  }
97 
101  void start () const {
102  this->half_clock();
103  this->m_scl.set();
104  this->m_sda.set();
105  this->m_scl.set_dir_out();
106  this->m_sda.set_dir_out();
107 
108  this->half_clock();
109  this->m_sda.clear();
110  this->half_clock();
111  this->m_scl.clear();
112  }
113 
117  void stop () const {
118  this->m_sda.clear();
119  this->m_scl.clear();
120 
121  this->half_clock();
122  this->m_scl.set_dir_in();
123  this->half_clock();
124  this->m_sda.set_dir_in();
125  }
126 
134  bool send_byte (const uint8_t byte) const {
135  int result = 0;
136  int dataMask = 0;
137  int nextCnt = 0;
138  int temp = 0;
139 
140  __asm__ volatile(
141  FC_START("PutByteStart", "PutByteEnd")
142  /* Setup for transmit loop */
143  " mov %[_dataMask], #256 \n\t" /* 0x100 */
144  " mov %[_result], #0 \n\t"
145  " mov %[_nextCnt], cnt \n\t"
146  " add %[_nextCnt], %[_clockDelay] \n\t"
147 
148  /* Transmit Loop (8x) */
149  //Output bit of byte
150  "PutByteLoop%=: "
151  " shr %[_dataMask], #1 \n\t" // Set up mask
152  " and %[_dataMask], %[_dataByte] wz,nr \n\t" // Move the bit into Z flag
153  " muxz dira, %[_SDAMask] \n\t"
154 
155  //Pulse clock
156  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
157  " andn dira, %[_SCLMask] \n\t" // Set SCL high
158  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
159  " or dira, %[_SCLMask] \n\t" // Set SCL low
160 
161  //Return for more bits
162  " djnz %[_dataMask], #" FC_ADDR("PutByteLoop%=", "PutByteStart") " nr \n\t"
163 
164  // Get ACK
165  " andn dira, %[_SDAMask] \n\t" // Float SDA high (release SDA)
166  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
167  " andn dira, %[_SCLMask] \n\t" // SCL high (by float)
168  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
169  " mov %[_temp], ina \n\t" //Sample input
170  " and %[_SDAMask], %[_temp] wz,nr \n\t" // If != 0, ack'd, else nack
171  " muxz %[_result], #1 \n\t" // Set result to equal to Z flag (aka, 1 if ack'd)
172  " or dira, %[_SCLMask] \n\t" // Set scl low
173  " or dira, %[_SDAMask] \n\t" // Set sda low
174 
175  FC_END("PutByteEnd")
176  : // Outputs
177  [_dataMask] "=&r"(dataMask),
178  [_result] "=&r"(result),
179  [_nextCnt] "=&r"(nextCnt),
180  [_temp] "=&r"(temp)
181  : // Inputs
182  [_SDAMask] "r"(this->m_sda.get_mask()),
183  [_SCLMask] "r"(this->m_scl.get_mask()),
184  [_dataByte] "r"(byte),
185  [_clockDelay] "r"(m_clockDelay));
186 
187  return (bool) result;
188  }
189 
197  uint8_t read_byte (const bool acknowledge) const {
198  uint32_t result;
199  uint32_t dataMask;
200  uint32_t nextCnt;
201  uint32_t temp;
202 
203  __asm__ volatile(
204  FC_START("GetByteStart", "GetByteEnd")
205  // Setup for receive loop
206  " andn dira, %[_SDAMask] \n\t"
207  " mov %[_dataMask], #256 \n\t" /* 0x100 */
208  " mov %[_result], #0 \n\t"
209  " mov %[_nextCnt], cnt \n\t"
210  " add %[_nextCnt], %[_clockDelay] \n\t"
211 
212  // Receive Loop (8x)
213  //Get bit of byte
214  "GetByteLoop%=: "
215 
216  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
217  " shr %[_dataMask], #1 \n\t" // Set up mask
218 
219  //Pulse clock
220  " andn dira, %[_SCLMask] \n\t" // Set SCL high
221  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
222  " mov %[_temp], ina \n\t" //Sample the input
223  " and %[_temp], %[_SDAMask] nr,wz \n\t"
224  " muxnz %[_result], %[_dataMask] \n\t"
225  " or dira, %[_SCLMask] \n\t" // Set SCL low
226 
227  //Return for more bits
228  " djnz %[_dataMask], #" FC_ADDR("GetByteLoop%=", "GetByteStart") " nr \n\t"
229 
230  // Put ACK
231 
232  " and %[_acknowledge], #1 nr,wz \n\t" //Output ACK
233 
234  " muxnz dira, %[_SDAMask] \n\t"
235  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
236  " andn dira, %[_SCLMask] \n\t" // SCL high (by float)
237  " waitcnt %[_nextCnt], %[_clockDelay] \n\t"
238 
239  " or dira, %[_SCLMask] \n\t" // Set scl low
240  " or dira, %[_SDAMask] \n\t" // Set sda low
241  FC_END("GetByteEnd")
242  : // Outputs
243  [_dataMask] "=&r"(dataMask),
244  [_result] "=&r"(result),
245  [_temp] "=&r"(temp),
246  [_nextCnt] "=&r"(nextCnt)
247 
248  : // Inputs
249  [_SDAMask] "r"(this->m_sda.get_mask()),
250  [_SCLMask] "r"(this->m_scl.get_mask()),
251  [_acknowledge] "r"(acknowledge),
252  [_clockDelay] "r"(m_clockDelay));
253 
254  return (uint8_t) result;
255 
256  }
257 
272  bool ping (const uint8_t device) const {
273  this->start();
274  bool result = this->send_byte(device);
275  this->stop();
276  return result;
277  }
278 
295  template<typename T>
296  bool put (const uint8_t device, const T address, const uint8_t byte) const {
297  bool result;
298 
299  this->start();
300  result = this->send_byte(device);
301  if (result) {
302  result = this->send_address(address);
303  if (result)
304  result = this->send_byte(byte);
305  }
306 
307  this->stop();
308  return result;
309  }
310 
326  template<typename T>
327  uint8_t get (const uint8_t device, const T address) const {
328  bool result;
329  uint8_t dataByte = static_cast<uint8_t>(-1);
330 
331  this->start();
332  result = this->send_byte(device);
333  if (result) {
334  result &= this->send_address(address);
335  if (result) {
336  this->start();
337  result &= this->send_byte((uint8_t) (device | BIT_0)); //Set read bit
338 
339  if (result)
340  dataByte = this->read_byte(false);
341  }
342  }
343 
344  this->stop();
345  return dataByte;
346  }
347 
366  template<typename T>
367  bool put (const uint8_t device, const T address, const uint8_t bytes[], const size_t size) const {
368  bool result;
369  this->start();
370  result = this->send_byte(device);
371  if (result) {
372  result = this->send_address(address);
373 
374  for (unsigned int i = 0; i < size; ++i) {
375  result = this->send_byte(bytes[i]);
376  if (!result) {
377  this->stop();
378  return false;
379  }
380  };
381  }
382 
383  this->stop();
384  return result;
385  }
386 
407  template<typename T>
408  bool get (const uint8_t device, const T address, uint8_t bytes[], const size_t size) const {
409  bool result;
410  this->start();
411  result = this->send_byte(device);
412  if (result) {
413  result = this->send_address(address);
414  if (result) {
415  this->start();
416  result = this->send_byte((uint8_t) (device | 0x01));
417 
418  if (result) {
419  unsigned int i = 0;
420  for (; i < size - 1; ++i)
421  bytes[i] = this->read_byte(true); //Send true to keep on reading bytes
422  bytes[i] = this->read_byte(false); //Trailing NAK
423  }
424  }
425  }
426 
427  this->stop();
428  return result;
429  }
430 
450  bool put (const uint8_t device, const uint8_t byte) const {
451  //Warning: this method is not unit tested! (it's run, but the MS5611 does
452  //not have a register that can be written to and read from).
453 
454  this->start();
455  bool result = this->send_byte(device);
456  if (result)
457  result = this->send_byte(byte);
458  this->stop();
459 
460  return result;
461  }
462 
483  bool get (const uint8_t device, uint8_t bytes[], const size_t size) const {
484  this->start();
485  bool result = this->send_byte((uint8_t) (device | 0x01));
486 
487  unsigned int i;
488  for (i = 0; i < size - 1; ++i)
489  bytes[i] = this->read_byte(true);
490  bytes[i] = this->read_byte(false);
491 
492  this->stop();
493  return result;
494  }
495 
496  private:
500  bool send_address (const uint8_t address) const {
501  return this->send_byte(address);
502  }
503 
507  bool send_address (const uint16_t address) const {
508  bool result = this->send_byte((const uint8_t) (address >> 8));
509  return result && this->send_byte((const uint8_t) address);
510  }
511 
519  void half_clock () const {
520 #ifdef __PROPELLER_CMM__
521  if (500 > m_clockDelay)
522  __asm__ volatile ("nop;");
523  else
524  waitcnt(m_clockDelay + CNT);
525 #else
526  waitcnt(this->m_clockDelay + CNT);
527 #endif
528  }
529 
530  private:
531  const Pin m_scl;
532  const Pin m_sda;
533  unsigned int m_clockDelay;
534 };
535 
536 }
537 
PropWare::Port::set_dir_out
void set_dir_out() const
Set the port for output.
Definition: port.h:210
PropWare::I2CMaster::set_frequency
void set_frequency(const unsigned int frequency)
Set the bus frequency.
Definition: i2cmaster.h:94
PropWare::I2CMaster::stop
void stop() const
Output a stop condition on the I2C bus.
Definition: i2cmaster.h:117
PropWare::I2CMaster::put
bool put(const uint8_t device, const T address, const uint8_t byte) const
Put a single byte with the following format:
Definition: i2cmaster.h:296
PropWare::Port::Mask
Mask
Definition: port.h:43
pwI2c
PropWare::I2CMaster pwI2c
Global I2C instance for easy and shared use by Propeller applications (not thread safe!...
Definition: i2cmaster.cpp:30
PropWare::I2CMaster::send_byte
bool send_byte(const uint8_t byte) const
Output a byte on the I2C bus.
Definition: i2cmaster.h:134
PropWare::Port::set_dir_in
void set_dir_in() const
Set the port for input.
Definition: port.h:217
PropWare::Pin
Utility class to handle general purpose I/O pins.
Definition: pin.h:36
PropWare::I2CMaster::read_byte
uint8_t read_byte(const bool acknowledge) const
Get a byte from the bus.
Definition: i2cmaster.h:197
PropWare::I2CMaster::get
bool get(const uint8_t device, uint8_t bytes[], const size_t size) const
Get multiple bytes, no register address.
Definition: i2cmaster.h:483
PropWare::I2CMaster::start
void start() const
Output a start condition on the I2C bus.
Definition: i2cmaster.h:101
CLKFREQ
#define CLKFREQ
Returns the current clock frequency.
Definition: propeller.h:46
PropWare::I2CMaster
Basic I2C driver.
Definition: i2cmaster.h:64
PropWare::I2CMaster::I2CMaster
I2CMaster(const Pin::Mask sclMask=DEFAULT_SCL_MASK, const Pin::Mask sdaMask=DEFAULT_SDA_MASK, const unsigned int frequency=DEFAULT_FREQUENCY)
Create a basic I2C instance.
Definition: i2cmaster.h:78
PropWare::Port::set
void set() const
Set selected output port high (set all pins to 1)
Definition: port.h:226
PropWare::I2CMaster::put
bool put(const uint8_t device, const uint8_t byte) const
Put a single byte, no register address, on the bus.
Definition: i2cmaster.h:450
PropWare::I2CMaster::put
bool put(const uint8_t device, const T address, const uint8_t bytes[], const size_t size) const
Put multiple bytes with the following format:
Definition: i2cmaster.h:367
waitcnt
#define waitcnt(a)
Wait until system counter reaches a value.
Definition: propeller.h:176
PropWare::I2CMaster::get
uint8_t get(const uint8_t device, const T address) const
Get a single byte with the following format:
Definition: i2cmaster.h:327
CNT
#define CNT
The system clock count.
Definition: propeller1.h:151
PropWare::I2CMaster::ping
bool ping(const uint8_t device) const
Test for the Acknowledge of a device by sending start and the slave address.
Definition: i2cmaster.h:272
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
PropWare::I2CMaster::get
bool get(const uint8_t device, const T address, uint8_t bytes[], const size_t size) const
Read multiple bytes.
Definition: i2cmaster.h:408