machine_i2c.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. /*
  2. * This file is part of the MicroPython project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2016-2018 Damien P. George
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include "py/runtime.h"
  29. #include "py/mphal.h"
  30. #include "py/mperrno.h"
  31. #include "extmod/machine_i2c.h"
  32. #include "i2c.h"
  33. #if MICROPY_HW_ENABLE_HW_I2C
  34. STATIC const mp_obj_type_t machine_hard_i2c_type;
  35. #if defined(STM32F0) || defined(STM32F4) || defined(STM32F7)
  36. typedef struct _machine_hard_i2c_obj_t {
  37. mp_obj_base_t base;
  38. i2c_t *i2c;
  39. mp_hal_pin_obj_t scl;
  40. mp_hal_pin_obj_t sda;
  41. } machine_hard_i2c_obj_t;
  42. STATIC const machine_hard_i2c_obj_t machine_hard_i2c_obj[] = {
  43. #if defined(MICROPY_HW_I2C1_SCL)
  44. {{&machine_hard_i2c_type}, I2C1, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA},
  45. #else
  46. {{NULL}, NULL, NULL, NULL},
  47. #endif
  48. #if defined(MICROPY_HW_I2C2_SCL)
  49. {{&machine_hard_i2c_type}, I2C2, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA},
  50. #else
  51. {{NULL}, NULL, NULL, NULL},
  52. #endif
  53. #if defined(MICROPY_HW_I2C3_SCL)
  54. {{&machine_hard_i2c_type}, I2C3, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA},
  55. #else
  56. {{NULL}, NULL, NULL, NULL},
  57. #endif
  58. #if defined(MICROPY_HW_I2C4_SCL)
  59. {{&machine_hard_i2c_type}, I2C4, MICROPY_HW_I2C4_SCL, MICROPY_HW_I2C4_SDA},
  60. #else
  61. {{NULL}, NULL, NULL, NULL},
  62. #endif
  63. };
  64. STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
  65. machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
  66. #if defined(STM32F4)
  67. uint32_t freq = self->i2c->CR2 & 0x3f;
  68. uint32_t ccr = self->i2c->CCR;
  69. if (ccr & 0x8000) {
  70. // Fast mode, assume duty cycle of 16/9
  71. freq = freq * 40000 / (ccr & 0xfff);
  72. } else {
  73. // Standard mode
  74. freq = freq * 500000 / (ccr & 0xfff);
  75. }
  76. mp_printf(print, "I2C(%u, scl=%q, sda=%q, freq=%u)",
  77. self - &machine_hard_i2c_obj[0] + 1,
  78. mp_hal_pin_name(self->scl), mp_hal_pin_name(self->sda),
  79. freq);
  80. #else
  81. uint32_t timingr = self->i2c->TIMINGR;
  82. uint32_t presc = timingr >> 28;
  83. uint32_t sclh = timingr >> 8 & 0xff;
  84. uint32_t scll = timingr & 0xff;
  85. uint32_t freq = HAL_RCC_GetPCLK1Freq() / (presc + 1) / (sclh + scll + 2);
  86. mp_printf(print, "I2C(%u, scl=%q, sda=%q, freq=%u, timingr=0x%08x)",
  87. self - &machine_hard_i2c_obj[0] + 1,
  88. mp_hal_pin_name(self->scl), mp_hal_pin_name(self->sda),
  89. freq, timingr);
  90. #endif
  91. }
  92. void machine_hard_i2c_init(machine_hard_i2c_obj_t *self, uint32_t freq, uint32_t timeout) {
  93. (void)timeout;
  94. i2c_init(self->i2c, self->scl, self->sda, freq);
  95. }
  96. int machine_hard_i2c_readfrom(mp_obj_base_t *self_in, uint16_t addr, uint8_t *dest, size_t len, bool stop) {
  97. machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
  98. return i2c_readfrom(self->i2c, addr, dest, len, stop);
  99. }
  100. int machine_hard_i2c_writeto(mp_obj_base_t *self_in, uint16_t addr, const uint8_t *src, size_t len, bool stop) {
  101. machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
  102. return i2c_writeto(self->i2c, addr, src, len, stop);
  103. }
  104. #else
  105. // No hardware I2C driver for this MCU so use the software implementation
  106. typedef mp_machine_soft_i2c_obj_t machine_hard_i2c_obj_t;
  107. STATIC machine_hard_i2c_obj_t machine_hard_i2c_obj[] = {
  108. #if defined(MICROPY_HW_I2C1_SCL)
  109. {{&machine_hard_i2c_type}, 1, 500, MICROPY_HW_I2C1_SCL, MICROPY_HW_I2C1_SDA},
  110. #else
  111. {{NULL}, 0, 0, NULL, NULL},
  112. #endif
  113. #if defined(MICROPY_HW_I2C2_SCL)
  114. {{&machine_hard_i2c_type}, 1, 500, MICROPY_HW_I2C2_SCL, MICROPY_HW_I2C2_SDA},
  115. #else
  116. {{NULL}, 0, 0, NULL, NULL},
  117. #endif
  118. #if defined(MICROPY_HW_I2C3_SCL)
  119. {{&machine_hard_i2c_type}, 1, 500, MICROPY_HW_I2C3_SCL, MICROPY_HW_I2C3_SDA},
  120. #else
  121. {{NULL}, 0, 0, NULL, NULL},
  122. #endif
  123. #if defined(MICROPY_HW_I2C4_SCL)
  124. {{&machine_hard_i2c_type}, 1, 500, MICROPY_HW_I2C4_SCL, MICROPY_HW_I2C4_SDA},
  125. #else
  126. {{NULL}, 0, 0, NULL, NULL},
  127. #endif
  128. };
  129. STATIC void machine_hard_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
  130. machine_hard_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
  131. mp_printf(print, "I2C(%u, scl=%q, sda=%q, freq=%u, timeout=%u)",
  132. self - &machine_hard_i2c_obj[0] + 1,
  133. self->scl->name, self->sda->name, 500000 / self->us_delay, self->us_timeout);
  134. }
  135. STATIC void machine_hard_i2c_init(machine_hard_i2c_obj_t *self, uint32_t freq, uint32_t timeout) {
  136. // set parameters
  137. if (freq >= 1000000) {
  138. // allow fastest possible bit-bang rate
  139. self->us_delay = 0;
  140. } else {
  141. self->us_delay = 500000 / freq;
  142. if (self->us_delay == 0) {
  143. self->us_delay = 1;
  144. }
  145. }
  146. self->us_timeout = timeout;
  147. // init pins
  148. mp_hal_pin_open_drain(self->scl);
  149. mp_hal_pin_open_drain(self->sda);
  150. }
  151. #define machine_hard_i2c_readfrom mp_machine_soft_i2c_readfrom
  152. #define machine_hard_i2c_writeto mp_machine_soft_i2c_writeto
  153. #endif
  154. /******************************************************************************/
  155. /* MicroPython bindings for machine API */
  156. mp_obj_t machine_hard_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
  157. // parse args
  158. enum { ARG_id, ARG_scl, ARG_sda, ARG_freq, ARG_timeout };
  159. static const mp_arg_t allowed_args[] = {
  160. { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_OBJ },
  161. { MP_QSTR_scl, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
  162. { MP_QSTR_sda, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
  163. { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 400000} },
  164. { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} },
  165. };
  166. mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
  167. mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
  168. // work out i2c bus
  169. int i2c_id = 0;
  170. if (MP_OBJ_IS_STR(args[ARG_id].u_obj)) {
  171. const char *port = mp_obj_str_get_str(args[ARG_id].u_obj);
  172. if (0) {
  173. #ifdef MICROPY_HW_I2C1_NAME
  174. } else if (strcmp(port, MICROPY_HW_I2C1_NAME) == 0) {
  175. i2c_id = 1;
  176. #endif
  177. #ifdef MICROPY_HW_I2C2_NAME
  178. } else if (strcmp(port, MICROPY_HW_I2C2_NAME) == 0) {
  179. i2c_id = 2;
  180. #endif
  181. #ifdef MICROPY_HW_I2C3_NAME
  182. } else if (strcmp(port, MICROPY_HW_I2C3_NAME) == 0) {
  183. i2c_id = 3;
  184. #endif
  185. } else {
  186. nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
  187. "I2C(%s) doesn't exist", port));
  188. }
  189. } else {
  190. i2c_id = mp_obj_get_int(args[ARG_id].u_obj);
  191. if (i2c_id < 1 || i2c_id > MP_ARRAY_SIZE(machine_hard_i2c_obj)
  192. || machine_hard_i2c_obj[i2c_id - 1].base.type == NULL) {
  193. nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
  194. "I2C(%d) doesn't exist", i2c_id));
  195. }
  196. }
  197. // get static peripheral object
  198. machine_hard_i2c_obj_t *self = (machine_hard_i2c_obj_t*)&machine_hard_i2c_obj[i2c_id - 1];
  199. // here we would check the scl/sda pins and configure them, but it's not implemented
  200. if (args[ARG_scl].u_obj != MP_OBJ_NULL || args[ARG_sda].u_obj != MP_OBJ_NULL) {
  201. mp_raise_ValueError("explicit choice of scl/sda is not implemented");
  202. }
  203. // initialise the I2C peripheral
  204. machine_hard_i2c_init(self, args[ARG_freq].u_int, args[ARG_timeout].u_int);
  205. return MP_OBJ_FROM_PTR(self);
  206. }
  207. STATIC const mp_machine_i2c_p_t machine_hard_i2c_p = {
  208. .readfrom = machine_hard_i2c_readfrom,
  209. .writeto = machine_hard_i2c_writeto,
  210. };
  211. STATIC const mp_obj_type_t machine_hard_i2c_type = {
  212. { &mp_type_type },
  213. .name = MP_QSTR_I2C,
  214. .print = machine_hard_i2c_print,
  215. .make_new = machine_hard_i2c_make_new,
  216. .protocol = &machine_hard_i2c_p,
  217. .locals_dict = (mp_obj_dict_t*)&mp_machine_soft_i2c_locals_dict,
  218. };
  219. #endif // MICROPY_HW_ENABLE_HW_I2C