ini
[aversive.git] / modules / comm / spi / spi.c
1 /*  
2  *  Copyright Droids Corporation (2008)
3  * 
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  */
19
20 /*
21  * Author : Julien LE GUEN - jlg@jleguen.info
22  */
23
24 #include <aversive.h>
25 #include <aversive/parts.h>
26 #include <aversive/error.h>
27
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <spi.h>
32 #include <spi_config.h>
33
34 /* internal structure to store SS pins */
35 typedef struct _ss_pin {
36         volatile uint8_t *port;
37         uint8_t bitnum;
38 } ss_pin_t;
39
40
41 /* global vars */
42 static volatile ss_pin_t g_ss_lines[SPI_MAX_SLAVES+1];
43 static volatile uint8_t g_ss_number;
44 static volatile spi_mode_t g_spi_mode;
45 static volatile uint8_t g_slave_selected;
46
47
48
49
50 /* 
51  * Register a pin as SS line
52  * Returns a unique identifier, or -1 on error
53  * There is always the physical SS line registered as 0
54  */
55 int8_t spi_register_ss_line(volatile uint8_t *port, uint8_t bitnum)
56 {
57         DEBUG(E_SPI, "Trying to register new SS line: port 0x%x, bitnum %d", port, bitnum);
58         /* too much SS lines (try to change SPI_MAX_SLAVES) */
59         if (g_ss_number >= SPI_MAX_SLAVES+1)
60                 return -1;
61         
62         g_ss_lines[g_ss_number].port = port;
63         g_ss_lines[g_ss_number].bitnum = bitnum;
64         *(port-1) |= _BV(bitnum); // was DDR(port) |= _BV(bitnum);
65         /* Unselected at first */
66         *port |= (_BV(bitnum));
67
68         NOTICE(E_SPI, "New Slave Line registered: %d", g_ss_number);
69
70         return g_ss_number++;
71 }
72
73
74 /*
75  *      Set data order (default: MSB first)
76  */
77 inline void spi_set_data_order(uint8_t order)
78 {
79         if (order == SPI_LSB_FIRST)
80                 SPCR |= _BV(DORD);
81         else
82                 SPCR &= ~(_BV(DORD));
83 }
84
85 /*
86  *      Get data order
87  */
88 inline uint8_t spi_get_data_order(void)
89 {
90         if (SPCR & _BV(DORD))
91                 return SPI_LSB_FIRST;
92         return SPI_MSB_FIRST;
93 }
94
95
96 /*
97  *      Initialize SPI
98  */
99 void spi_init(spi_mode_t mode, spi_format_t format, spi_clk_rate_t clk_rate)
100 {
101         NOTICE(E_SPI, "Init SPI: mode %d, format %d, clk_rate %d",
102                mode, format, clk_rate);
103
104         /* Configure SPI pins */
105         DDR(SCK_PORT) |= _BV(SCK_BIT);
106         DDR(MOSI_PORT) |= _BV(MOSI_BIT);
107         DDR(MISO_PORT) &= ~(_BV(MISO_BIT));
108         /* SS pin is not driven by SPI hardware 
109          * This is taken care of by spi_register_ss_line()
110          * EVEN for the "default" SS line */
111         g_ss_number = 0;
112         g_slave_selected = FALSE;
113
114         /* Registers init */
115 #ifdef PRR0
116         PRR0 &= ~(_BV(PRSPI));          /* Clear power reduction bit */
117 #endif
118         SPCR =  0;
119         SPSR =  0;
120
121         SPCR |= _BV(MSTR);              /* XXX Master only for now ! */
122         SPCR |= (uint8_t)format;        /* Clock polarity and phase */
123         
124         /* clockrate: SPR0, SPR1, SPI2X */
125         if (clk_rate & 0x01)
126                 SPR0_REG |= _BV(SPR0);
127         else
128                 SPR0_REG &= ~(_BV(SPR0));
129         if (clk_rate & 0x02)
130                 SPR1_REG |= _BV(SPR1);
131         else
132                 SPR1_REG &= ~(_BV(SPR1));
133         if (clk_rate & 0x10)
134                 SPI2X_REG |= _BV(SPI2X);
135         else
136                 SPI2X_REG &= ~(_BV(SPI2X));
137         
138         SPCR |= _BV(SPE);               /* Enable SPI */
139
140         g_spi_mode = SPI_MODE_MASTER;
141         NOTICE(E_SPI, "Init done");
142 }
143
144 /*
145  *      Returns the state of SPI
146  */
147 inline spi_mode_t spi_get_mode(void)
148 {
149         return g_spi_mode;
150 }
151
152 /*
153  *      Send a byte (and receive one)
154  *      Returns the received byte
155  */
156 uint8_t spi_send_and_receive_byte(uint8_t byte)
157 {
158         //DEBUG(E_SPI, "Sending 0x%x", byte);
159         /* Start transmission */
160         SPDR = byte;
161
162         /* Wait for transmission complete 
163          * Timings are VERY important, do not bypass this ! */
164         while(!(SPSR & (1<<SPIF)))
165                 ;
166         /* Return received byte */
167         return SPDR;
168 }
169
170 /*
171  *      Send a byte, discard the result
172  */
173 inline void spi_send_byte(uint8_t byte)
174 {
175         spi_send_and_receive_byte(byte);
176 }
177
178 /*
179  *      Receives a byte (sends a NULL)
180  */
181 uint8_t spi_receive_byte(void)
182 {
183         return spi_send_and_receive_byte(0x00);
184 }
185
186 /*
187  *      Activates the selected SS line
188  */
189 uint8_t spi_slave_select(uint8_t slave)
190 {
191         if (g_slave_selected) {
192                 ERROR(E_SPI, "A slave is already selected !");
193                 return EBUSY;
194         }
195
196         *(g_ss_lines[slave].port) &= ~(_BV(g_ss_lines[slave].bitnum));
197         g_slave_selected = TRUE;
198         return ESUCCESS;
199 }
200
201 /*
202  *      Desactivates the selected SS line
203  */
204 void spi_slave_deselect(uint8_t slave)
205 {
206         *(g_ss_lines[slave].port) |= (_BV(g_ss_lines[slave].bitnum));
207         g_slave_selected = FALSE;
208 }
209
210 /*
211  *      Display SS lines
212  */
213 void spi_display_ss_lines(void)
214 {
215         uint8_t i;
216         for (i = 0; i < g_ss_number; i++) {
217                 DEBUG(E_SPI, "SS%d: adr 0x%x bitnum %d value 0x%x",
218                       i,
219                       g_ss_lines[i].port,
220                       g_ss_lines[i].bitnum,
221                       *(g_ss_lines[i].port));
222         }
223 }