source: rtems/c/src/lib/libbsp/arm/stm32f4/i2c/i2c.c @ c499856

4.115
Last change on this file since c499856 was c499856, checked in by Chris Johns <chrisj@…>, on 03/20/14 at 21:10:47

Change all references of rtems.com to rtems.org.

  • Property mode set to 100644
File size: 7.6 KB
Line 
1/*
2 * Copyright (c) 2013 Christian Mauderer.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Obere Lagerstr. 30
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15/* The I2C-module can not run with libi2c. The reason for this is, that libi2c
16 * needs a possibility to generate a stop condition separately. This controller
17 * wants to generate the condition automatically when sending or receiving data.
18 */
19
20#include <bsp.h>
21#include <bsp/i2c.h>
22#include <bsp/rcc.h>
23#include <bsp/irq.h>
24#include <bsp/irq-generic.h>
25#include <assert.h>
26
27#define RTEMS_STATUS_CHECKS_USE_PRINTK
28
29#include <rtems/status-checks.h>
30
31#define STM32F4_I2C_INITIAL_BITRATE 100000
32
33#define I2C_RW_BIT 0x1
34
35stm32f4_rcc_index i2c_rcc_index [] = {
36  STM32F4_RCC_I2C1,
37  STM32F4_RCC_I2C2,
38};
39
40static stm32f4_rcc_index i2c_get_rcc_index(stm32f4_i2c_bus_entry *e)
41{
42  return i2c_rcc_index [e->index];
43}
44
45static uint32_t i2c_get_pclk(stm32f4_i2c_bus_entry *e)
46{
47  return STM32F4_PCLK1;
48}
49
50rtems_status_code stm32f4_i2c_set_bitrate(
51  stm32f4_i2c_bus_entry *e,
52  uint32_t br
53)
54{
55  volatile stm32f4_i2c *regs = e->regs;
56  uint32_t ccr;
57  uint32_t trise;
58  uint32_t pclk = i2c_get_pclk(e);
59
60  /* Make sure, that the module is disabled */
61  if((regs->cr1 & STM32F4_I2C_CR1_PE) != 0)
62  {
63    return RTEMS_RESOURCE_IN_USE;
64  }
65
66  /* Configure clock control register and rise time register */
67  ccr = regs->ccr;
68  trise = regs->trise;
69
70  if(br <= 100000)
71  {
72    uint32_t ccr_val = pclk / (2 * br);
73    /* according to datasheet, the rise time for standard mode is 1us -> 1MHz */
74    uint32_t trise_val = pclk / 1000000 + 1;
75    trise = STM32F4_I2C_TRISE_SET(trise, trise_val);
76
77    if(ccr_val > STM32F4_I2C_CCR_CCR_MAX)
78    {
79      return RTEMS_INVALID_NUMBER;
80    }
81
82    /* standard mode */
83    ccr &= ~STM32F4_I2C_CCR_FS;
84    ccr = STM32F4_I2C_CCR_CCR_SET(ccr, ccr_val);
85  }
86  else
87  {
88    /* FIXME: Implement speeds 100kHz < f <= 400kHz (fast mode) */
89    return RTEMS_NOT_IMPLEMENTED;
90  }
91
92  regs->ccr = ccr;
93  regs->trise = trise;
94
95  return RTEMS_SUCCESSFUL;
96}
97
98static void stm32f4_i2c_handler(void *arg)
99{
100  /* This handler implements the suggested read method from stm32f103xx
101   * reference manual if the handler is not the one with the highest priority */
102  stm32f4_i2c_bus_entry *e = arg;
103  volatile stm32f4_i2c *regs = e->regs;
104  uint32_t sr1 = regs->sr1;
105  uint8_t *data = e->data;
106  uint8_t *last = e->last;
107  bool read = e->read;
108  bool wake_task = false;
109  uint32_t cr1;
110
111  if(sr1 & STM32F4_I2C_SR1_SB) {
112    /* Start condition sent. */
113    regs->dr = e->addr_with_rw;
114  }
115
116  if(read) {
117    size_t len = e->len;
118
119    if(len == 1) {
120      /* special case for one single byte */
121      if(sr1 & STM32F4_I2C_SR1_ADDR) {
122        cr1 = regs->cr1;
123        cr1 &= ~STM32F4_I2C_CR1_ACK;
124        regs->cr1 = cr1;
125
126        /* Read sr2 to clear flag */
127        regs->sr2;
128
129        cr1 = regs->cr1;
130        cr1 |= STM32F4_I2C_CR1_STOP;
131        regs->cr1 = cr1;
132      } else if(sr1 & STM32F4_I2C_SR1_RxNE) {
133        *data = regs->dr;
134        wake_task = true;
135      }
136    } else if (len == 2) {
137      /* special case for two bytes */
138      if(sr1 & STM32F4_I2C_SR1_ADDR) {
139        /* Read sr2 to clear flag */
140        regs->sr2;
141
142        cr1 = regs->cr1;
143        cr1 &= ~STM32F4_I2C_CR1_ACK;
144        regs->cr1 = cr1;
145      } else if(sr1 & STM32F4_I2C_SR1_BTF) {
146        cr1 = regs->cr1;
147        cr1 |= STM32F4_I2C_CR1_STOP;
148        regs->cr1 = cr1;
149
150        *data = regs->dr;
151        ++data;
152        *data = regs->dr;
153        wake_task = true;
154      }
155    } else {
156      /* more than two bytes */
157      if(sr1 & STM32F4_I2C_SR1_ADDR) {
158        /* Read sr2 to clear flag */
159        regs->sr2;
160      } else if(sr1 & STM32F4_I2C_SR1_BTF && data == last - 2) {
161        cr1 = regs->cr1;
162        cr1 &= ~STM32F4_I2C_CR1_ACK;
163        regs->cr1 = cr1;
164
165        *data = regs->dr;
166        ++data;
167
168        cr1 = regs->cr1;
169        cr1 |= STM32F4_I2C_CR1_STOP;
170        regs->cr1 = cr1;
171
172        *data = regs->dr;
173        ++data;
174      } else if((sr1 & STM32F4_I2C_SR1_RxNE) && (data != last - 2)) {
175        *data = regs->dr;
176
177        if(data == last) {
178          wake_task = true;
179        } else {
180          ++data;
181        }
182      }
183    }
184  } else /* write */ {
185    if(sr1 & STM32F4_I2C_SR1_ADDR) {
186      /* Address sent */
187      regs->sr2;
188    }
189
190    if((sr1 & (STM32F4_I2C_SR1_ADDR | STM32F4_I2C_SR1_TxE)) && (data <= last)) {
191      regs->dr = *data;
192      ++data;
193    } else if(sr1 & STM32F4_I2C_SR1_BTF) {
194      uint32_t cr1 = regs->cr1;
195      cr1 |= STM32F4_I2C_CR1_STOP;
196      regs->cr1 = cr1;
197      wake_task = true;
198    }
199  }
200
201  e->data = data;
202
203  if(wake_task) {
204    bsp_interrupt_vector_disable(e->vector);
205    rtems_event_transient_send(e->task_id);
206  }
207}
208
209static rtems_status_code i2c_wait_done(stm32f4_i2c_bus_entry *e)
210{
211  rtems_status_code sc = RTEMS_SUCCESSFUL;
212
213  bsp_interrupt_vector_enable(e->vector);
214  e->task_id = rtems_task_self();
215  return rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
216}
217
218rtems_status_code stm32f4_i2c_init(stm32f4_i2c_bus_entry *e)
219{
220  rtems_status_code sc = RTEMS_SUCCESSFUL;
221  volatile stm32f4_i2c *regs = e->regs;
222  stm32f4_rcc_index rcc_index = i2c_get_rcc_index(e);
223  uint32_t pclk = i2c_get_pclk(e);
224  uint32_t cr1 = 0;
225  uint32_t cr2 = 0;
226
227  assert(pclk >= 2000000);
228
229  /* Create mutex */
230  sc = rtems_semaphore_create (
231    rtems_build_name ('I', '2', 'C', '1' + e->index),
232    0,
233    RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
234    0,
235    &e->mutex
236  );
237  RTEMS_CHECK_SC(sc, "create mutex");
238
239  /* Install interrupt handler and disable this vector */
240  sc = rtems_interrupt_handler_install(
241    e->vector,
242    "I2C",
243    RTEMS_INTERRUPT_UNIQUE,
244    stm32f4_i2c_handler,
245    e
246  );
247  RTEMS_CHECK_SC(sc, "install interrupt handler");
248  bsp_interrupt_vector_disable(e->vector);
249
250  /* Enable module clock */
251  stm32f4_rcc_set_clock(rcc_index, true);
252
253  /* Setup initial bit rate */
254  sc = stm32f4_i2c_set_bitrate(e, STM32F4_I2C_INITIAL_BITRATE);
255  RTEMS_CHECK_SC(sc, "set bitrate");
256
257  /* Set config registers */
258  cr2 = regs->cr2;
259  cr2 = STM32F4_I2C_CR2_FREQ_SET(cr2, pclk / 1000000);
260  cr2 |= STM32F4_I2C_CR2_ITEVTEN;
261  cr2 |= STM32F4_I2C_CR2_ITBUFEN;
262  regs->cr2 = cr2;
263
264  cr1 = regs->cr1;
265  cr1 |= STM32F4_I2C_CR1_PE;
266  regs->cr1 = cr1;
267
268  return RTEMS_SUCCESSFUL;
269}
270
271rtems_status_code stm32f4_i2c_process_message(
272  stm32f4_i2c_bus_entry *e,
273  stm32f4_i2c_message *msg
274)
275{
276  rtems_status_code sc = RTEMS_SUCCESSFUL;
277  rtems_status_code sc_return = RTEMS_SUCCESSFUL;
278  volatile stm32f4_i2c *regs = e->regs;
279  uint16_t max_7_bit_address = (1 << 7) - 1;
280  uint32_t cr1 = regs->cr1;
281
282  if(msg->addr > max_7_bit_address) {
283    return RTEMS_NOT_IMPLEMENTED;
284  }
285
286  if(msg->len == 0) {
287    return RTEMS_INVALID_SIZE;
288  }
289
290  sc = rtems_semaphore_obtain(e->mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT);
291  RTEMS_CHECK_SC(sc, "obtaining mutex");
292
293  e->data = msg->buf;
294  e->last = msg->buf + msg->len - 1;
295  e->len = msg->len;
296
297  e->addr_with_rw = msg->addr << 1;
298  if(msg->read) {
299    e->addr_with_rw |= I2C_RW_BIT;
300  }
301  e->read = msg->read;
302
303  /* Check if no stop is active. */
304  if(cr1 & STM32F4_I2C_CR1_STOP) {
305    return RTEMS_IO_ERROR;
306  }
307
308  /* Start */
309  cr1 = regs->cr1;
310  if(e->len == 2) {
311    cr1 |= STM32F4_I2C_CR1_POS;
312  } else {
313    cr1 &= ~STM32F4_I2C_CR1_POS;
314  }
315  cr1 |= STM32F4_I2C_CR1_ACK;
316  cr1 |= STM32F4_I2C_CR1_START;
317  regs->cr1 = cr1;
318
319  /* Wait for end of message */
320  sc = i2c_wait_done(e);
321
322  if(sc != RTEMS_SUCCESSFUL) {
323    sc_return = sc;
324  }
325
326  sc = rtems_semaphore_release(e->mutex);
327  RTEMS_CHECK_SC(sc, "releasing mutex");
328
329  return sc_return;
330}
331
Note: See TracBrowser for help on using the repository browser.