From e608ac87afd39ed837ad3518bec652e3fd6eeffa Mon Sep 17 00:00:00 2001 From: Perry Hung Date: Sat, 24 Apr 2010 04:16:47 -0400 Subject: Initial SPI implementation. Squashed commit of the following: commit b41eb846ca60559cff242d0c550699eb8f309909 Author: Perry Hung Date: Sat Apr 24 04:09:16 2010 -0400 Turn the other peripherals back on. Not extensively tested for interactions between peripherals. commit bf4fc3bf6bc02342ae508b52fb4515d361d626f6 Author: Perry Hung Date: Sat Apr 24 03:16:35 2010 -0400 Upper level libmaple interface Added a C++ HardwareSPI class to access the SPI interface. See HardwareSPI.cpp and HardwareSPI.h for documentation. commit 17e0e5edde60e9bf2aa4d52173ad7d47d6d6da75 Author: Perry Hung Date: Thu Apr 22 02:36:01 2010 -0400 Initial SPI Polling implementation. Initial commit of a polling-based SPI driver. The driver is limited to synchronous, blocking sends and a 8-bit data frame format. Tested on SPI1 and SPI2. Other peripherals are temporarily disabled, and the rx function is untested until I find a good peripheral to test everything on. --- Makefile | 2 + core/comm/HardwareSPI.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++++++ core/comm/HardwareSPI.h | 57 +++++++++++++++++++ libmaple/libmaple_types.h | 2 + libmaple/rcc.h | 4 ++ libmaple/spi.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ libmaple/spi.h | 115 ++++++++++++++++++++++++++++++++++++++ notes/pin-mapping.txt | 4 ++ 8 files changed, 454 insertions(+) create mode 100644 core/comm/HardwareSPI.cpp create mode 100644 core/comm/HardwareSPI.h create mode 100644 libmaple/spi.c create mode 100644 libmaple/spi.h diff --git a/Makefile b/Makefile index 2076083..e7df3a5 100644 --- a/Makefile +++ b/Makefile @@ -70,6 +70,7 @@ CSRC = libmaple/systick.c \ libmaple/usb.c \ libmaple/rcc.c \ libmaple/flash.c \ + libmaple/spi.c \ core/wiring.c \ core/wiring_shift.c \ core/wiring_analog.c \ @@ -82,6 +83,7 @@ CPPSRC = core/wiring_math.cpp \ core/Print.cpp \ core/comm/HardwareSerial.cpp \ core/comm/HardwareUsb.cpp \ + core/comm/HardwareSPI.cpp \ main.cpp # i really have no idea what i'm doing diff --git a/core/comm/HardwareSPI.cpp b/core/comm/HardwareSPI.cpp new file mode 100644 index 0000000..51093cb --- /dev/null +++ b/core/comm/HardwareSPI.cpp @@ -0,0 +1,138 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @brief HardwareSPI "wiring-like" api for SPI + */ + +/* NOTES: + * + * Speeds: + * ----------------------------------- + * Interface num: SPI1 SPI2 + * Bus APB2 APB1 + * ----------------------------------- + * Prescaler Frequencies + * ----------------------------------- + * 2: N/A 18 000 000 + * 4: 18 000 000 9 000 000 + * 8: 9 000 000 4 500 000 + * 16: 4 500 000 2 250 000 + * 32: 2 250 000 1 125 000 + * 64: 1 125 000 562 500 + * 128: 562 500 281 250 + * 256: 281 250 140 625 + * + * TODO: Do the complementary PWM outputs mess up SPI2? + * */ + +#include "wiring.h" +#include "spi.h" +#include "HardwareSPI.h" + +static const uint32 prescaleFactors[MAX_SPI_FREQS] = { + SPI_PRESCALE_2, // SPI_18MHZ + SPI_PRESCALE_4, // SPI_9MHZ + SPI_PRESCALE_8, // SPI_4_5MHZ + SPI_PRESCALE_16, // SPI_2_25MHZ + SPI_PRESCALE_32, // SPI_1_125MHZ + SPI_PRESCALE_64, // SPI_562_500KHZ + SPI_PRESCALE_128, // SPI_281_250KHZ + SPI_PRESCALE_256, // SPI_140_625KHZ +}; + +/** + * @brief Initialize a SPI peripheral + * @param freq frequency to run at, must one of the following values: + * - SPI_18MHZ + * - SPI_9MHZ + * - SPI_4_5MHZ + * - SPI_2_25MHZ + * - SPI_1_125MHZ + * - SPI_562_500KHZ + * - SPI_281_250KHZ + * - SPI_140_625KHZ + * @param endianness endianness of the data frame, must be either LSBFIRST + * or MSBFIRST + * @param mode SPI standard CPOL and CPHA levels + */ +void HardwareSPI::begin(SPIFrequency freq, uint32 endianness, uint32 mode) { + uint32 spi_num = this->spi_num; + uint32 high_speed = 0; + uint32 index; + uint32 prescale; + + if ((freq >= MAX_SPI_FREQS) || + !((endianness == LSBFIRST) || + (endianness == MSBFIRST)) || + (mode >= 4)) { + return; + } + + if (spi_num == 1) { + /* SPI1 is too fast for 140625 */ + if (freq == SPI_140_625KHZ) { + return; + } + + /* Turn off PWM on shared pins */ + timers_disable_channel(3, 2); + timers_disable_channel(3, 1); + } + + endianness = (endianness == LSBFIRST) ? SPI_LSBFIRST : SPI_MSBFIRST; + prescale = (spi_num == 1) ? prescaleFactors[freq + 1] : prescaleFactors[freq]; + + spi_init(spi_num, prescale, endianness, 0); +} + +/** + * @brief Initialize a SPI peripheral with a default speed of 1.125 MHZ, MSBFIRST, + * mode 0 + * @param mode SPI standard CPOL and CPHA levels + */ +void HardwareSPI::begin(void) { + begin(SPI_1_125MHZ, MSBFIRST, 0); +} + +/** + * @brief send a byte out the spi peripheral + * @param data byte to send + */ +void HardwareSPI::send(uint8 data) { + spi_tx(this->spi_num, data); +} + + +/** + * @brief read a byte from the spi peripheral + * @return byte in the buffer + */ +uint8 HardwareSPI::recv(void) { + return spi_rx(this->spi_num); +} + +HardwareSPI::HardwareSPI(uint32 spi_num) { + this->spi_num = spi_num; +} diff --git a/core/comm/HardwareSPI.h b/core/comm/HardwareSPI.h new file mode 100644 index 0000000..b974334 --- /dev/null +++ b/core/comm/HardwareSPI.h @@ -0,0 +1,57 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @brief HardwareSPI definitions + */ + +#ifndef _HARDWARESPI_H_ +#define _HARDWARESPI_H_ + +typedef enum SPIFrequency { + SPI_18MHZ = 0, + SPI_9MHZ = 1, + SPI_4_5MHZ = 2, + SPI_2_25MHZ = 3, + SPI_1_125MHZ = 4, + SPI_562_500KHZ = 5, + SPI_281_250KHZ = 6, + SPI_140_625KHZ = 7, + MAX_SPI_FREQS = 8, +} SPIFrequency; + +class HardwareSPI { + private: + uint32 spi_num; + + public: + HardwareSPI(uint32 spi_num); + void begin(void); + void begin(SPIFrequency freq, uint32 endianness, uint32 mode); + void send(uint8 data); + uint8 recv(void); +}; + +#endif + diff --git a/libmaple/libmaple_types.h b/libmaple/libmaple_types.h index ca98f6f..d49f95a 100644 --- a/libmaple/libmaple_types.h +++ b/libmaple/libmaple_types.h @@ -43,5 +43,7 @@ typedef long long int64; typedef void (*voidFuncPtr)(void); +#define __io volatile + #endif diff --git a/libmaple/rcc.h b/libmaple/rcc.h index 8f786ee..9973bca 100644 --- a/libmaple/rcc.h +++ b/libmaple/rcc.h @@ -100,6 +100,10 @@ #define RCC_APB1ENR_TIM4EN BIT(2) #define RCC_APB1ENR_USART2EN BIT(17) #define RCC_APB1ENR_USART3EN BIT(18) +#define RCC_APB1ENR_SPI2EN BIT(14) + +#define rcc_enable_clk_spi1() __set_bits(RCC_APB2ENR, RCC_APB2ENR_SPI1EN) +#define rcc_enable_clk_spi2() __set_bits(RCC_APB1ENR, RCC_APB1ENR_SPI2EN) #define rcc_enable_clk_timer1() __set_bits(RCC_APB2ENR, RCC_APB2ENR_TIM1EN) #define rcc_enable_clk_timer2() __set_bits(RCC_APB1ENR, RCC_APB1ENR_TIM2EN) diff --git a/libmaple/spi.c b/libmaple/spi.c new file mode 100644 index 0000000..5a77b03 --- /dev/null +++ b/libmaple/spi.c @@ -0,0 +1,132 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @brief libmaple serial peripheral interface (SPI) definitions + * + * Initial implementation for the SPI interface. + * + * This driver implements only the bare essentials of a polling driver at the + * moment. Master mode only, 8-bit data frames, and polling. + * + * The caller is responsible for controlling the chip select line. + * + * TODO: interrupt based driver, DMA. + * + */ + +#include "libmaple.h" +#include "gpio.h" +#include "rcc.h" +#include "spi.h" + +typedef struct spi_dev { + SPI *base; + GPIO_Port *port; + uint8 sck_pin; + uint8 miso_pin; + uint8 mosi_pin; +} spi_dev; + +static const spi_dev spi_dev1 = { + .base = (SPI*)SPI1_BASE, + .port = GPIOA_BASE, + .sck_pin = 5, + .miso_pin = 6, + .mosi_pin = 7 +}; + +static const spi_dev spi_dev2 = { + .base = (SPI*)SPI2_BASE, + .port = GPIOB_BASE, + .sck_pin = 13, + .miso_pin = 14, + .mosi_pin = 15 +}; + +static void spi_gpio_cfg(spi_dev *dev); + +/** + * @brief Initialize a spi peripheral + * @param spi_num which spi to turn on, SPI1 or SPI2? + * @param prescale prescale factor on the input clock. + * @param endian data frame format (LSBFIRST?) + * @param mode SPI mode number + */ +void spi_init(uint32 spi_num, + uint32 prescale, + uint32 endian, + uint32 mode) { + ASSERT(spi_num == 1 || spi_num == 2); + ASSERT(mode < 4); + + SPI *spi; + uint32 cr1 = 0; + + switch (spi_num) { + case 1: + /* limit to 18 mhz max speed */ + ASSERT(prescale != CR1_BR_PRESCALE_2); + spi = (SPI*)SPI1_BASE; + rcc_enable_clk_spi1(); + spi_gpio_cfg(&spi_dev1); + break; + case 2: + spi = (SPI*)SPI2_BASE; + rcc_enable_clk_spi2(); + spi_gpio_cfg(&spi_dev2); + break; + } + + cr1 = prescale | endian | mode | CR1_MSTR | CR1_SSI | CR1_SSM; + spi->CR1 = cr1; + + /* Peripheral enable */ + spi->CR1 |= CR1_SPE; +} + + +/** + * @brief SPI synchronous 8-bit write, blocking. + * @param spi_num which spi to send on + * @return data shifted back from the slave + */ +void spi_tx(uint32 spi_num, uint8 data) { + SPI *spi; + + ASSERT(spi_num == 1 || spi_num == 2); + + spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE; + + spi->DR = data; + + while (!(spi->SR & SR_TXE) || (spi->SR & SR_BSY)) + ; +} + +static void spi_gpio_cfg(spi_dev *dev) { + gpio_set_mode(dev->port, dev->sck_pin, GPIO_MODE_AF_OUTPUT_PP); + gpio_set_mode(dev->port, dev->miso_pin, GPIO_MODE_AF_OUTPUT_PP); + gpio_set_mode(dev->port, dev->mosi_pin, GPIO_MODE_AF_OUTPUT_PP); +} diff --git a/libmaple/spi.h b/libmaple/spi.h new file mode 100644 index 0000000..cde3b42 --- /dev/null +++ b/libmaple/spi.h @@ -0,0 +1,115 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @brief libmaple serial peripheral interface (SPI) prototypes and declarations + */ + +#ifndef _SPI_H_ +#define _SPI_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* peripheral addresses */ +#define SPI1_BASE 0x40013000 +#define SPI2_BASE 0x40003800 + +/* baud rate prescaler bits */ +#define CR1_BR 0x00000038 +#define CR1_BR_PRESCALE_2 0x00000000 +#define CR1_BR_PRESCALE_4 0x00000008 +#define CR1_BR_PRESCALE_8 0x00000010 +#define CR1_BR_PRESCALE_16 0x00000018 +#define CR1_BR_PRESCALE_32 0x00000020 +#define CR1_BR_PRESCALE_64 0x00000028 +#define CR1_BR_PRESCALE_128 0x00000030 +#define CR1_BR_PRESCALE_256 0x00000038 + +#define CR1_LSBFIRST BIT(7) // data frame format +#define CR1_MSTR BIT(2) // master selection +#define CR1_SSM BIT(9) // software slave management +#define CR1_SSI BIT(8) // internal slave select +#define CR1_SPE BIT(6) // peripheral enable + +/* Status register bits */ +#define SR_RXNE BIT(0) // rx buffer not empty +#define SR_TXE BIT(1) // transmit buffer empty +#define SR_BSY BIT(7) // busy flag + +typedef struct SPI { + __io uint16 CR1; + uint16 pad0; + __io uint8 CR2; + uint8 pad1[3]; + __io uint8 SR; + uint8 pad2[3]; + __io uint16 DR; + uint16 pad3; + __io uint16 CRCPR; + uint16 pad4; + __io uint16 RXCRCR; + uint16 pad5; + __io uint16 TXCRCR; + uint16 pad6; +} SPI; + +enum { + SPI_MSBFIRST = 0, + SPI_LSBFIRST = BIT(7), +}; + +enum { + SPI_PRESCALE_2 = (0x0 << 3), + SPI_PRESCALE_4 = (0x1 << 3), + SPI_PRESCALE_8 = (0x2 << 3), + SPI_PRESCALE_16 = (0x3 << 3), + SPI_PRESCALE_32 = (0x4 << 3), + SPI_PRESCALE_64 = (0x5 << 3), + SPI_PRESCALE_128 = (0x6 << 3), + SPI_PRESCALE_256 = (0x7 << 3) +}; + +void spi_init(uint32 spi_num, + uint32 prescale, + uint32 endian, + uint32 mode); +void spi_tx(uint32 spi_num, uint8 data); + +static inline uint8 spi_rx(uint32 spi_num) { + SPI *spi; + + ASSERT(spi_num == 1 || spi_num == 2); + spi = (spi_num == 1) ? (SPI*)SPI1_BASE : (SPI*)SPI2_BASE; + + return spi->DR; +} + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/notes/pin-mapping.txt b/notes/pin-mapping.txt index c1709e8..e6debf7 100644 --- a/notes/pin-mapping.txt +++ b/notes/pin-mapping.txt @@ -63,3 +63,7 @@ D38 PC9 - - - - - Y ------------------------------------------------------------------------------- +todo: +adc pin check +jtag pins for gpio + -- cgit v1.2.3