2 * Copyright 2015, Olivier MATZ <zer0@droids-corp.org>
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include <ucg_delay.h>
35 #include <stm32f4xx.h>
39 //#define debug_printf(args...) printf(args)
40 #define debug_printf(args...)
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 */
50 static struct spi_state spi_state;
52 #define SPI_DATA_LEN 8192 /* must be a power of 2 */
53 static uint8_t spi_data[SPI_DATA_LEN];
55 static void led_on(void)
57 GPIOD->ODR |= (1 << 13);
60 static void led_off(void)
62 GPIOD->ODR &= (~(1 << 13));
65 static int spi_init(void)
68 GPIO_InitTypeDef gpio;
70 /* Enable peripheral clock */
71 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
73 /* Enable the AHB1 peripheral clock for GPIOA. */
74 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
77 /* connect the pins to the desired alternate function,
79 GPIO_StructInit(&gpio);
80 gpio.GPIO_Speed = GPIO_Speed_25MHz; /* common */
81 gpio.GPIO_OType = GPIO_OType_PP;
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);
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);
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);
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);
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);
119 SPI_Cmd(SPI1, ENABLE);
124 static uint8_t spi_data_read(uint32_t addr)
126 addr &= (SPI_DATA_LEN - 1);
127 return spi_data[addr];
130 static void spi_data_write(uint32_t addr, uint8_t val)
132 addr &= (SPI_DATA_LEN - 1);
133 spi_data[addr] = val;
136 /* executed at startup */
137 static void spi_state_init(void)
139 memset(&spi_state, 0, sizeof(spi_state));
142 /* executed between each command when nss goes low */
143 static void spi_state_reset(void)
145 SPI_SendData(SPI1, 0);
151 static void spi_data_intr(void)
156 rd_c = SPI_ReceiveData(SPI1);
158 /* first byte, set command */
159 if (spi_state.len == 0)
160 spi_state.cmd = rd_c;
162 switch(spi_state.cmd) {
163 /* Read Memory, no dummy cycle */
165 if (spi_state.len == 0) {
167 } else if (spi_state.len <= 3) {
168 spi_state.addr <<= 8;
169 spi_state.addr |= rd_c;
172 if (spi_state.len >= 3) {
173 wr_c = spi_data_read(spi_state.addr);
177 /* Read Memory with dummy cycle */
179 if (spi_state.len == 0) {
181 } else if (spi_state.len <= 3) {
182 spi_state.addr <<= 8;
183 spi_state.addr |= rd_c;
185 wr_c = spi_data_read(spi_state.addr);
190 /* Byte-Program (02H) */
192 if (spi_state.len == 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);
201 /* ignore next bytes */
205 /* Write-Enable (06H) */
207 if (spi_state.len == 0)
208 spi_state.status |= SPI_F_WREN;
209 /* ignore next bytes */
212 /* Write-Disable (04H) */
214 if (spi_state.len == 0)
215 spi_state.status &= (~SPI_F_WREN);
216 /* ignore next bytes */
219 /* Auto Address Increment Programming (ADH) */
221 /* Erase 4 KByte of memory array (20H) */
223 /* Erase 32 KByte block of memory (52H) */
225 /* Erase 64 KByte block of memory (D8H) */
227 /* Chip-Erase (60H) or (C7H) */
230 /* Read-Status-Register (05H) */
232 /* Enable-Write-Status-Register (50H) */
234 /* Write-Status-Register (01H) */
236 /* Read-ID (90H) or (ABH) */
241 /* EnableSOtooutputRY/BY# status during AAI programming (70H) */
243 /* Disable SO as RY/BY# status during AAI programming (80H) */
247 /* switch on LED on unknown command */
252 SPI_SendData(SPI1, wr_c);
254 debug_printf("rx=0x%2.2x tx=0x%2.2x\n", rd_c, wr_c);
257 /* called on state transition on the NSS pin */
258 static void spi_nss_intr(uint8_t nss)
261 debug_printf("ss high\n");
262 SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);
264 debug_printf("ss low\n");
265 SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Reset);
266 /* it's a new command, reset our spi_state structure */
274 uint8_t prev_nss = 1, nss;
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);
281 /* set pin 13 to be general purpose output */
282 GPIOD->MODER = (1 << 26);
285 for (i = 0; i < 3; i++) {
293 uart_register_stdio();
296 if (spi_init() < 0) {
297 printf("SPI init failed\n");
304 memset(spi_data, 0, sizeof(spi_data));
305 strcpy((char *)&spi_data[0x1000], "Dr0idZ C0rp");
308 /* inspect nss changes */
309 nss = !!(GPIOA->IDR & (1 << 4));
310 if (nss != prev_nss) {
316 if (SPI_GetFlagStatus(SPI1, SPI_FLAG_RXNE) != RESET)