Contiki 3.x
lis3dh-arch.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014, Eistec AB.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holder nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  *
29  * This file is part of the Mulle platform port of the Contiki operating system.
30  *
31  */
32 
33 /**
34  * \file
35  * Platform specific functions for LIS3DH accelerometer on the Mulle platform.
36  * \author
37  * Joakim Gebart <joakim.gebart@eistec.se>
38  */
39 
40 #include "lis3dh.h"
41 #include "K60.h"
42 #include "interrupt.h"
43 #include "power-control.h"
44 #include <stdint.h>
45 #include <stdbool.h>
46 
47 #define LIS3DH_CHIP_SELECT_PIN 0
48 #define LIS3DH_CTAS 1
49 
50 /**
51  * Perform a one byte transfer over SPI.
52  *
53  * \param data The data to write to the slave.
54  * \param cont If set, keep asserting the chip select pin after the current byte transfer ends.
55  * \param blocking If set, wait until all bits have been transferred before returning.
56  * \return The byte received from the slave during the same transfer.
57  *
58  * \note There is no need for separate read and write functions, since SPI transfers work like a shift register (one bit out, one bit in.)
59  *
60  * \todo Make SPI abstraction standalone.
61  */
62 static uint8_t
63 spi_transfer(const uint8_t data, const bool cont, const bool blocking)
64 {
65  uint32_t spi_pushr;
66 
67  spi_pushr = SPI_PUSHR_TXDATA(data);
68  spi_pushr |= SPI_PUSHR_CTAS(LIS3DH_CTAS);
69  spi_pushr |= SPI_PUSHR_PCS((1 << LIS3DH_CHIP_SELECT_PIN));
70  if(cont) {
71  spi_pushr |= SPI_PUSHR_CONT_MASK;
72  }
73 
74  /* Clear transfer complete flag */
75  SPI0->SR |= SPI_SR_TCF_MASK;
76 
77  /* Shift a frame out/in */
78  SPI0->PUSHR = spi_pushr;
79 
80  if(blocking) {
81  /* Wait for transfer complete */
82  while(!(SPI0->SR & SPI_SR_TCF_MASK)) ;
83  }
84 
85  /* Pop the buffer */
86  return 0xFF & SPI0->POPR;
87 }
88 /**
89  * Write a single byte to the LIS3DH.
90  *
91  * \param addr The target register.
92  * \param value The value to write.
93  */
94 void
95 lis3dh_write_byte(const lis3dh_reg_addr_t addr, const uint8_t value)
96 {
97  MK60_ENTER_CRITICAL_REGION();
98  spi_transfer((addr & LIS3DH_SPI_ADDRESS_MASK) | LIS3DH_SPI_WRITE_MASK | LIS3DH_SPI_SINGLE_MASK, true, true); /* Write address */
99  spi_transfer(value, false, true); /* Write data */
100  MK60_LEAVE_CRITICAL_REGION();
101 }
102 /**
103  * Read a single byte from the LIS3DH.
104  *
105  * \param addr The source register.
106  * \return The value of the register.
107  */
108 uint8_t
110 {
111  uint8_t data;
112 
113  MK60_ENTER_CRITICAL_REGION();
114  spi_transfer((addr & LIS3DH_SPI_ADDRESS_MASK) | LIS3DH_SPI_READ_MASK | LIS3DH_SPI_SINGLE_MASK, true, true); /* Write address */
115  data = spi_transfer(0, false, true); /* Dummy write to get data */
116  MK60_LEAVE_CRITICAL_REGION();
117  return data;
118 }
119 /**
120  * Read a 16-bit integer from the LIS3DH.
121  *
122  * \param lsb_addr The lower address of the two source registers.
123  * \return The value of the register, byte order depends on the big/little endian setting of the LIS3DH.
124  * \note The BLE bit of CTRL_REG4 will affect the byte order of the return value.
125  */
126 int16_t
128 {
129  int16_t data;
130 
131  MK60_ENTER_CRITICAL_REGION();
132 
133  /*
134  * We will do a multi byte read from the LIS3DH.
135  * After the first read the address will be automatically increased by one.
136  */
137  /* Write address */
138  spi_transfer((lsb_addr & LIS3DH_SPI_ADDRESS_MASK) |
140 
141  /*
142  * Do not modify the following two statements into a single statement, the
143  * spi_transfer calls must be made in the right order.
144  * Google "sequence points".
145  */
146  data = spi_transfer(0xFF, true, true); /* Read LSB, keep holding chip select active. */
147  data |= (spi_transfer(0xFF, false, true) << 8); /* Read MSB, release chip select. */
148 
149  MK60_LEAVE_CRITICAL_REGION();
150  return (int16_t)data;
151 }
152 /**
153  * Read multiple bytes from the LIS3DH.
154  *
155  * \param start_address The lower address of the source registers.
156  * \param buffer A buffer to write the read values into.
157  * \param count Number of bytes to read.
158  */
159 void
161  uint8_t *buffer, uint8_t count)
162 {
163  /*
164  * This function must not be interrupted by anything using the SPI bus or
165  * it will mess up the transfer.
166  */
167  MK60_ENTER_CRITICAL_REGION();
168 
169  /*
170  * We will do a multi byte read from the LIS3DH.
171  * After the first read the address will be automatically increased by one
172  * for each subsequent transfer.
173  */
174  /* Write address */
175  spi_transfer((start_address & LIS3DH_SPI_ADDRESS_MASK) |
177 
178  while(count > 1) {
179  /* Read byte, keep holding chip select active. */
180  *buffer = spi_transfer(0xFF, true, true);
181  ++buffer;
182  --count;
183  }
184  /* Read last byte, release chip select. */
185  *buffer = spi_transfer(0xFF, false, true);
186 
187  MK60_LEAVE_CRITICAL_REGION();
188 }
189 /**
190  * Write multiple bytes to the LIS3DH.
191  *
192  * \param start_address The lower address of the target registers.
193  * \param buffer A buffer to read the values from.
194  * \param count Number of bytes to write.
195  */
196 void
198  const uint8_t *buffer, uint8_t count)
199 {
200  /*
201  * This function must not be interrupted by anything using the SPI bus or
202  * it will mess up the transfer.
203  */
204  MK60_ENTER_CRITICAL_REGION();
205 
206  /*
207  * We will do a multi byte write to the LIS3DH.
208  * After the first write the address will be automatically increased by one
209  * for each subsequent transfer.
210  */
211  /* Write address */
212  spi_transfer((start_address & LIS3DH_SPI_ADDRESS_MASK) |
213  LIS3DH_SPI_WRITE_MASK | LIS3DH_SPI_MULTI_MASK, true, true);
214 
215  while(count > 1) {
216  /* Write byte, keep holding chip select active. */
217  spi_transfer(*buffer, true, true);
218  ++buffer;
219  --count;
220  }
221  /* Write last byte, release chip select. */
222  spi_transfer(*buffer, false, true);
223 
224  MK60_LEAVE_CRITICAL_REGION();
225 }
226 /**
227  * Perform the platform specific part of the initialization process of the LIS3DH.
228  * This function is expected to set up the SPI module for the LIS3DH.
229  */
230 void
232 {
233  /* Enable clock gate on PTD (for SPI0) */
234  SIM->SCGC5 |= SIM_SCGC5_PORTD_MASK;
235  /* Note: Interrupts will need to enable clock gate on PTC as well */
236 
237  /* Enable clock gate for SPI0 module */
238  SIM->SCGC6 |= SIM_SCGC6_SPI0_MASK;
239 
240  /* Configure SPI0 */
241  /* Master mode */
242  /* all peripheral chip select signals are active low */
243  /* Disable TX,RX FIFO */
244  SPI0->MCR = SPI_MCR_MSTR_MASK | SPI_MCR_PCSIS(0x1F) | SPI_MCR_DIS_RXF_MASK | SPI_MCR_DIS_TXF_MASK; /* 0x803F3000; */
245 
246  /* 8 bit frame size */
247  /* Set up different delays and clock scalers */
248  /* TODO: These need tuning */
249  /* FIXME: Coordinate SPI0 parameters between different peripheral drivers */
250  /* IMPORTANT: Clock polarity is active low! */
251  SPI0->CTAR[LIS3DH_CTAS] = SPI_CTAR_FMSZ(7) | SPI_CTAR_CSSCK(2) | SPI_CTAR_ASC(2) | SPI_CTAR_DT(2) | SPI_CTAR_BR(4) | SPI_CTAR_CPOL_MASK | SPI_CTAR_CPHA_MASK; /*0x38002224; *//* TODO: Should be able to speed up */
252 
253  /* Mux SPI0 on port D */
254  PORTD->PCR[0] = PORT_PCR_MUX(2); /* SPI0_PCS0 */
255  PORTD->PCR[1] = PORT_PCR_MUX(2); /* SPI0_SCK */
256  PORTD->PCR[2] = PORT_PCR_MUX(2); /* SPI0_SOUT */
257  PORTD->PCR[3] = PORT_PCR_MUX(2); /* SPI0_SIN */
258 
259  return;
260 }
lis3dh_reg_addr_t
All LIS3DH hardware registers are enumerated here.
Definition: lis3dh.h:59
int16_t lis3dh_read_int16(const lis3dh_reg_addr_t lsb_addr)
Read a 16-bit integer from the LIS3DH.
Definition: lis3dh-arch.c:127
#define LIS3DH_SPI_MULTI_MASK
Multi byte transfers must assert this bit when writing the address.
Definition: lis3dh.h:463
void lis3dh_memcpy_from_device(const lis3dh_reg_addr_t start_address, uint8_t *buffer, uint8_t count)
Read multiple bytes from the LIS3DH.
Definition: lis3dh-arch.c:160
void lis3dh_memcpy_to_device(const lis3dh_reg_addr_t start_address, const uint8_t *buffer, uint8_t count)
Write multiple bytes to the LIS3DH.
Definition: lis3dh-arch.c:197
#define LIS3DH_SPI_ADDRESS_MASK
Mask of the address bits in the address byte during transfers.
Definition: lis3dh.h:471
Power control pins for the on board power switches on the Mulle board.
void lis3dh_arch_init()
Perform the platform specific part of the initialization process of the LIS3DH.
Definition: lis3dh-arch.c:231
#define SIM
Peripheral SIM base pointer.
Definition: MK60D10.h:7650
#define SPI0
Peripheral SPI0 base pointer.
Definition: MK60D10.h:7984
K60 hardware register header wrapper.
#define LIS3DH_SPI_SINGLE_MASK
Opposite of LIS3DH_SPI_MULTI_MASK.
Definition: lis3dh.h:467
Interface of LIS3DH SPI driver used in the Mulle platform.
#define LIS3DH_SPI_READ_MASK
The READ bit must be set when reading.
Definition: lis3dh.h:459
K60 interrupt save/restore macros.
#define PORTD
Peripheral PORTD base pointer.
Definition: MK60D10.h:6427
void lis3dh_write_byte(const lis3dh_reg_addr_t addr, const uint8_t value)
Write a single byte to the LIS3DH.
Definition: lis3dh-arch.c:95
uint8_t lis3dh_read_byte(const lis3dh_reg_addr_t addr)
Read a single byte from the LIS3DH.
Definition: lis3dh-arch.c:109