Kiibohd Controller
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

i2c.c 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. /*
  2. * Copyright ( C ) 2014 Jan Rychter
  3. * Modifications ( C ) 2015 Jacob Alexander
  4. *
  5. * Permission is hereby granted, free of charge, to any person obtaining a copy
  6. * of this software and associated documentation files ( the "Software" ), to deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * copies of the Software, and to permit persons to whom the Software is
  10. * furnished to do so, subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be included in all
  13. * copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. * SOFTWARE.
  22. */
  23. // ----- Includes ----
  24. // Compiler Includes
  25. #include <Lib/ScanLib.h>
  26. // Project Includes
  27. #include <print.h>
  28. // Local Includes
  29. #include "i2c.h"
  30. // ----- Variables -----
  31. volatile I2C_Channel i2c_channels[1];
  32. // ----- Functions -----
  33. inline void i2c_setup( )
  34. {
  35. // Enable I2C internal clock
  36. SIM_SCGC4 |= SIM_SCGC4_I2C0; // Bus 0
  37. // External pull-up resistor
  38. PORTB_PCR0 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
  39. PORTB_PCR1 = PORT_PCR_ODE | PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(2);
  40. // SCL Frequency Divider
  41. // 1.8 MBaud ( likely higher than spec )
  42. // 0x82 -> 36 MHz / (4 * 3) = 2.25 MBaud
  43. // 0x80 => mul(4)
  44. // 0x05 => ICL(5)
  45. I2C0_F = 0x84;
  46. I2C0_FLT = 4;
  47. I2C0_C1 = I2C_C1_IICEN;
  48. I2C0_C2 = I2C_C2_HDRS; // High drive select
  49. // Enable I2C Interrupt
  50. NVIC_ENABLE_IRQ( IRQ_I2C0 );
  51. }
  52. uint8_t i2c_busy()
  53. {
  54. volatile I2C_Channel *channel = &( i2c_channels[0] );
  55. if ( channel->status == I2C_BUSY )
  56. {
  57. return 1;
  58. }
  59. return 0;
  60. }
  61. // These are here for readability and correspond to bit 0 of the address byte.
  62. #define I2C_WRITING 0
  63. #define I2C_READING 1
  64. int32_t i2c_send_sequence(
  65. uint16_t *sequence,
  66. uint32_t sequence_length,
  67. uint8_t *received_data,
  68. void ( *callback_fn )( void* ),
  69. void *user_data
  70. ) {
  71. volatile I2C_Channel *channel = &( i2c_channels[0] );
  72. int32_t result = 0;
  73. uint8_t status;
  74. if ( channel->status == I2C_BUSY )
  75. {
  76. return -1;
  77. }
  78. // Debug
  79. /*
  80. for ( uint8_t c = 0; c < sequence_length; c++ )
  81. {
  82. printHex( sequence[c] );
  83. print(" ");
  84. }
  85. print(NL);
  86. */
  87. channel->sequence = sequence;
  88. channel->sequence_end = sequence + sequence_length;
  89. channel->received_data = received_data;
  90. channel->status = I2C_BUSY;
  91. channel->txrx = I2C_WRITING;
  92. channel->callback_fn = callback_fn;
  93. channel->user_data = user_data;
  94. // reads_ahead does not need to be initialized
  95. // Acknowledge the interrupt request, just in case
  96. I2C0_S |= I2C_S_IICIF;
  97. I2C0_C1 = ( I2C_C1_IICEN | I2C_C1_IICIE );
  98. // Generate a start condition and prepare for transmitting.
  99. I2C0_C1 |= ( I2C_C1_MST | I2C_C1_TX );
  100. status = I2C0_S;
  101. if ( status & I2C_S_ARBL )
  102. {
  103. warn_print("Arbitration lost");
  104. result = -1;
  105. goto i2c_send_sequence_cleanup;
  106. }
  107. // Write the first (address) byte.
  108. I2C0_D = *channel->sequence++;
  109. // Everything is OK.
  110. return result;
  111. i2c_send_sequence_cleanup:
  112. I2C0_C1 &= ~( I2C_C1_IICIE | I2C_C1_MST | I2C_C1_TX );
  113. channel->status = I2C_ERROR;
  114. return result;
  115. }
  116. void i2c0_isr()
  117. {
  118. volatile I2C_Channel* channel = &i2c_channels[0];
  119. uint8_t element;
  120. uint8_t status;
  121. status = I2C0_S;
  122. // Acknowledge the interrupt request
  123. I2C0_S |= I2C_S_IICIF;
  124. // Arbitration problem
  125. if ( status & I2C_S_ARBL )
  126. {
  127. warn_print("Arbitration error");
  128. I2C0_S |= I2C_S_ARBL;
  129. goto i2c_isr_error;
  130. }
  131. if ( channel->txrx == I2C_READING )
  132. {
  133. switch( channel->reads_ahead )
  134. {
  135. // All the reads in the sequence have been processed ( but note that the final data register read still needs to
  136. // be done below! Now, the next thing is either a restart or the end of a sequence. In any case, we need to
  137. // switch to TX mode, either to generate a repeated start condition, or to avoid triggering another I2C read
  138. // when reading the contents of the data register.
  139. case 0:
  140. I2C0_C1 |= I2C_C1_TX;
  141. // Perform the final data register read now that it's safe to do so.
  142. *channel->received_data++ = I2C0_D;
  143. // Do we have a repeated start?
  144. if ( ( channel->sequence < channel->sequence_end ) && ( *channel->sequence == I2C_RESTART ) )
  145. {
  146. // Generate a repeated start condition.
  147. I2C0_C1 |= I2C_C1_RSTA;
  148. // A restart is processed immediately, so we need to get a new element from our sequence. This is safe, because
  149. // a sequence cannot end with a RESTART: there has to be something after it. Note that the only thing that can
  150. // come after a restart is an address write.
  151. channel->txrx = I2C_WRITING;
  152. channel->sequence++;
  153. element = *channel->sequence;
  154. I2C0_D = element;
  155. }
  156. else
  157. {
  158. goto i2c_isr_stop;
  159. }
  160. break;
  161. case 1:
  162. // do not ACK the final read
  163. I2C0_C1 |= I2C_C1_TXAK;
  164. *channel->received_data++ = I2C0_D;
  165. break;
  166. default:
  167. *channel->received_data++ = I2C0_D;
  168. break;
  169. }
  170. channel->reads_ahead--;
  171. }
  172. // channel->txrx == I2C_WRITING
  173. else
  174. {
  175. // First, check if we are at the end of a sequence.
  176. if ( channel->sequence == channel->sequence_end )
  177. goto i2c_isr_stop;
  178. // We received a NACK. Generate a STOP condition and abort.
  179. if ( status & I2C_S_RXAK )
  180. {
  181. warn_print("NACK Received");
  182. goto i2c_isr_error;
  183. }
  184. // check next thing in our sequence
  185. element = *channel->sequence;
  186. // Do we have a restart? If so, generate repeated start and make sure TX is on.
  187. if ( element == I2C_RESTART )
  188. {
  189. I2C0_C1 |= I2C_C1_RSTA | I2C_C1_TX;
  190. // A restart is processed immediately, so we need to get a new element from our sequence.
  191. // This is safe, because a sequence cannot end with a RESTART: there has to be something after it.
  192. channel->sequence++;
  193. element = *channel->sequence;
  194. // Note that the only thing that can come after a restart is a write.
  195. I2C0_D = element;
  196. }
  197. else
  198. {
  199. if ( element == I2C_READ ) {
  200. channel->txrx = I2C_READING;
  201. // How many reads do we have ahead of us ( not including this one )?
  202. // For reads we need to know the segment length to correctly plan NACK transmissions.
  203. // We already know about one read
  204. channel->reads_ahead = 1;
  205. while (
  206. ( ( channel->sequence + channel->reads_ahead ) < channel->sequence_end ) &&
  207. ( *( channel->sequence + channel->reads_ahead ) == I2C_READ )
  208. ) {
  209. channel->reads_ahead++;
  210. }
  211. // Switch to RX mode.
  212. I2C0_C1 &= ~I2C_C1_TX;
  213. // do not ACK the final read
  214. if ( channel->reads_ahead == 1 )
  215. {
  216. I2C0_C1 |= I2C_C1_TXAK;
  217. }
  218. // ACK all but the final read
  219. else
  220. {
  221. I2C0_C1 &= ~( I2C_C1_TXAK );
  222. }
  223. // Dummy read comes first, note that this is not valid data!
  224. // This only triggers a read, actual data will come in the next interrupt call and overwrite this.
  225. // This is why we do not increment the received_data pointer.
  226. *channel->received_data = I2C0_D;
  227. channel->reads_ahead--;
  228. }
  229. // Not a restart, not a read, must be a write.
  230. else
  231. {
  232. I2C0_D = element;
  233. }
  234. }
  235. }
  236. channel->sequence++;
  237. return;
  238. i2c_isr_stop:
  239. // Generate STOP ( set MST=0 ), switch to RX mode, and disable further interrupts.
  240. I2C0_C1 &= ~( I2C_C1_MST | I2C_C1_IICIE | I2C_C1_TXAK );
  241. channel->status = I2C_AVAILABLE;
  242. // Call the user-supplied callback function upon successful completion (if it exists).
  243. if ( channel->callback_fn )
  244. {
  245. // Delay 10 microseconds before starting linked function
  246. delayMicroseconds(10);
  247. ( *channel->callback_fn )( channel->user_data );
  248. }
  249. return;
  250. i2c_isr_error:
  251. // Generate STOP and disable further interrupts.
  252. I2C0_C1 &= ~( I2C_C1_MST | I2C_C1_IICIE );
  253. channel->status = I2C_ERROR;
  254. return;
  255. }