initial revision
[ucgine.git] / examples / spi-flash / main.c
1 /*
2  * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <ucg_delay.h>
29
30 #include <stdio.h>
31 #include <inttypes.h>
32 #include <unistd.h>
33 #include <string.h>
34
35 #include <stm32f4xx.h>
36
37 #include "uart.h"
38
39 //#define debug_printf(args...) printf(args)
40 #define debug_printf(args...)
41
42 struct spi_state {
43         uint8_t cmd;             /* current command */
44 #define SPI_F_WREN 0x01          /* write enabled */
45         uint8_t status;          /* status flags */
46         uint32_t len;            /* number of rx/tx bytes since last reset */
47         uint32_t addr;           /* current rd/wr address */
48 };
49
50 static struct spi_state spi_state;
51
52 #define SPI_DATA_LEN 8192        /* must be a power of 2 */
53 static uint8_t spi_data[SPI_DATA_LEN];
54
55 static void led_on(void)
56 {
57         GPIOD->ODR |= (1 << 13);
58 }
59
60 static void led_off(void)
61 {
62         GPIOD->ODR &= (~(1 << 13));
63 }
64
65 static int spi_init(void)
66 {
67         SPI_InitTypeDef spi;
68         GPIO_InitTypeDef gpio;
69
70         /* Enable peripheral clock */
71         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
72
73         /* Enable the AHB1 peripheral clock for GPIOA. */
74         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
75         __asm("dsb");
76
77         /* connect the pins to the desired alternate function,
78          * configure them: */
79         GPIO_StructInit(&gpio);
80         gpio.GPIO_Speed = GPIO_Speed_25MHz; /* common */
81         gpio.GPIO_OType = GPIO_OType_PP;
82
83         /* SPI1_NSS: PA4 */
84         gpio.GPIO_Pin = GPIO_Pin_4;
85         gpio.GPIO_Mode = GPIO_Mode_IN; /* do not use the hw NSS */
86         GPIO_Init(GPIOA, &gpio);
87
88         /* SPI1_SCK: PA5 */
89         gpio.GPIO_Pin = GPIO_Pin_5;
90         gpio.GPIO_Mode = GPIO_Mode_AF;
91         GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
92         GPIO_Init(GPIOA, &gpio);
93
94         /* SPI1_MISO: PA6 */
95         gpio.GPIO_Pin = GPIO_Pin_6;
96         gpio.GPIO_Mode = GPIO_Mode_AF;
97         GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
98         GPIO_Init(GPIOA, &gpio);
99
100         /* SPI1_MOSI: PA7 */
101         gpio.GPIO_Pin = GPIO_Pin_7;
102         gpio.GPIO_Mode = GPIO_Mode_AF;
103         GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
104         GPIO_Init(GPIOA, &gpio);
105
106         /* Program the Polarity, Phase, First Data, Baud Rate Prescaler, Slave
107          * Management, Peripheral Mode and CRC Polynomial values */
108         SPI_StructInit(&spi);
109         spi.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
110         spi.SPI_Mode = SPI_Mode_Slave;
111         spi.SPI_DataSize = SPI_DataSize_8b;
112         spi.SPI_CPOL = SPI_CPOL_Low;   /* CK to 0 when idle */
113         spi.SPI_CPHA = SPI_CPHA_1Edge;  /* data on first transition */
114         spi.SPI_NSS = SPI_NSS_Soft;    /* manage nss by software */
115         spi.SPI_FirstBit = SPI_FirstBit_MSB;
116         SPI_Init(SPI1, &spi);
117
118         /* Enable the SPI */
119         SPI_Cmd(SPI1, ENABLE);
120
121         return 0;
122 }
123
124 static uint8_t spi_data_read(uint32_t addr)
125 {
126         addr &= (SPI_DATA_LEN - 1);
127         return spi_data[addr];
128 }
129
130 static void spi_data_write(uint32_t addr, uint8_t val)
131 {
132         addr &= (SPI_DATA_LEN - 1);
133         spi_data[addr] = val;
134 }
135
136 /* executed at startup */
137 static void spi_state_init(void)
138 {
139         memset(&spi_state, 0, sizeof(spi_state));
140 }
141
142 /* executed between each command when nss goes low */
143 static void spi_state_reset(void)
144 {
145         SPI_SendData(SPI1, 0);
146         spi_state.cmd = 0;
147         spi_state.len = 0;
148         spi_state.addr = 0;
149 }
150
151 static void spi_data_intr(void)
152 {
153         uint8_t rd_c;
154         uint8_t wr_c = 0;
155
156         rd_c = SPI_ReceiveData(SPI1);
157
158         /* first byte, set command */
159         if (spi_state.len == 0)
160                 spi_state.cmd = rd_c;
161
162         switch(spi_state.cmd) {
163         /* Read Memory, no dummy cycle */
164         case 0x03:
165                 if (spi_state.len == 0) {
166                         spi_state.addr = 0;
167                 } else if (spi_state.len <= 3) {
168                         spi_state.addr <<= 8;
169                         spi_state.addr |= rd_c;
170                 }
171                 /* send data */
172                 if (spi_state.len >= 3) {
173                         wr_c = spi_data_read(spi_state.addr);
174                         spi_state.addr++;
175                 }
176                 break;
177         /* Read Memory with dummy cycle */
178         case 0x0B:
179                 if (spi_state.len == 0) {
180                         spi_state.addr = 0;
181                 } else if (spi_state.len <= 3) {
182                         spi_state.addr <<= 8;
183                         spi_state.addr |= rd_c;
184                 } else {
185                         wr_c = spi_data_read(spi_state.addr);
186                         spi_state.addr++;
187                 }
188                 break;
189
190         /* Byte-Program (02H) */
191         case 0x02:
192                 if (spi_state.len == 0) {
193                         spi_state.addr = 0;
194                 } else if (spi_state.len <= 3) {
195                         spi_state.addr <<= 8;
196                         spi_state.addr |= rd_c;
197                 } else if (spi_state.len == 4 &&
198                         (spi_state.status & SPI_F_WREN)) {
199                         spi_data_write(spi_state.addr, rd_c);
200                 } else {
201                         /* ignore next bytes */
202                 }
203                 break;
204
205         /* Write-Enable (06H) */
206         case 0x06:
207                 if (spi_state.len == 0)
208                         spi_state.status |= SPI_F_WREN;
209                 /* ignore next bytes */
210                 break;
211
212         /* Write-Disable (04H) */
213         case 0x04:
214                 if (spi_state.len == 0)
215                         spi_state.status &= (~SPI_F_WREN);
216                 /* ignore next bytes */
217                 break;
218
219         /* Auto Address Increment Programming (ADH) */
220         case 0xAD:
221         /* Erase 4 KByte of memory array (20H) */
222         case 0x20:
223         /* Erase 32 KByte block of memory (52H) */
224         case 0x52:
225         /* Erase 64 KByte block of memory (D8H) */
226         case 0xD8:
227         /* Chip-Erase (60H) or (C7H) */
228         case 0x60:
229         case 0xC7:
230         /* Read-Status-Register (05H) */
231         case 0x05:
232         /* Enable-Write-Status-Register (50H) */
233         case 0x50:
234         /* Write-Status-Register (01H) */
235         case 0x01:
236         /* Read-ID (90H) or (ABH) */
237         case 0x90:
238         case 0xAB:
239         /* JEDEC-ID (9FH) */
240         case 0x9F:
241         /* EnableSOtooutputRY/BY# status during AAI programming (70H) */
242         case 0x70:
243         /* Disable SO as RY/BY# status during AAI programming (80H) */
244         case 0x80:
245
246         default:
247                 /* switch on LED on unknown command */
248                 led_on();
249                 break;
250         }
251
252         SPI_SendData(SPI1, wr_c);
253         spi_state.len++;
254         debug_printf("rx=0x%2.2x tx=0x%2.2x\n", rd_c, wr_c);
255 }
256
257 /* called on state transition on the NSS pin */
258 static void spi_nss_intr(uint8_t nss)
259 {
260         if (nss) {
261                 debug_printf("ss high\n");
262                 SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
263         } else {
264                 debug_printf("ss low\n");
265                 SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Reset);
266                 /* it's a new command, reset our spi_state structure */
267                 spi_state_reset();
268         }
269 }
270
271 int main(void)
272 {
273         unsigned i;
274         uint8_t prev_nss = 1, nss;
275
276         /* enable the clock to GPIOD, and stall instruction pipeline as per
277          * errata 2.1.13 "Delay after an RCC peripheral clock enabling" */
278         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
279         __asm("dsb");
280
281         /* set pin 13 to be general purpose output */
282         GPIOD->MODER = (1 << 26);
283
284         /* toggle the pin */
285         for (i = 0; i < 3; i++) {
286                 led_on();
287                 ucg_delay_ms(500);
288                 led_off();
289                 ucg_delay_ms(500);
290         }
291
292         uart_init();
293         uart_register_stdio();
294         printf("salut\n");
295
296         if (spi_init() < 0) {
297                 printf("SPI init failed\n");
298                 goto end;
299         }
300
301         spi_state_init();
302
303         /* init spi data */
304         memset(spi_data, 0, sizeof(spi_data));
305         strcpy((char *)&spi_data[0x1000], "Dr0idZ C0rp");
306
307         while (1) {
308                 /* inspect nss changes */
309                 nss = !!(GPIOA->IDR & (1 << 4));
310                 if (nss != prev_nss) {
311                         spi_nss_intr(nss);
312                         prev_nss = nss;
313                 }
314
315                 /* data received */
316                 if (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) != RESET)
317                         spi_data_intr();
318         }
319
320  end:
321         printf("end\n");
322         while (1);
323         return 0;
324 }