Still need to correctly handle NSS, since the hardware seems to do strange things. Also, no DMA is being used yet.
229 lines
7.1 KiB
C
229 lines
7.1 KiB
C
/**
|
|
Copyright 2019, Max Regan
|
|
|
|
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.
|
|
**/
|
|
|
|
#include <stdint.h>
|
|
#include "stm32l0xx.h"
|
|
|
|
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
|
|
|
|
void SystemInit()
|
|
{
|
|
|
|
/**
|
|
* Use the MSI for the system clock, and disable all other clocks.
|
|
*/
|
|
|
|
/*!< Set MSION bit. Set by hardware to force the MSI oscillator ON
|
|
* when exiting from Stop or Standby mode, or in case of a failure
|
|
* of the HSE oscillator used directly or indirectly as system
|
|
* clock. This bit cannot be cleared if the MSI is used as system
|
|
* clock. */
|
|
RCC->CR |= RCC_CR_MSION;
|
|
|
|
RCC->ICSCR &= ~RCC_ICSCR_MSIRANGE;
|
|
RCC->ICSCR |= RCC_ICSCR_MSIRANGE_6; // | RCC_ICSCR_MSIRANGE_0;;
|
|
|
|
/*!< Reset
|
|
* SW[1:0] (use MSI oscillator as system clock),
|
|
* HPRE[3:0] (do not divide AHB clock in prescaler) ,
|
|
* PPRE1[2:0] (do not divide APB low-speed clock)
|
|
* PPRE2[2:0] (do not divide APB high-speed clock),
|
|
* MCOSEL[2:0] (disable MCO clock),
|
|
* MCOPRE[2:0] (disable MCO prescaler) */
|
|
RCC->CFGR &= ~RCC_CFGR_SW & ~RCC_CFGR_HPRE & ~RCC_CFGR_PPRE1 &
|
|
~RCC_CFGR_PPRE2 & ~RCC_CFGR_MCOSEL & ~RCC_CFGR_MCOPRE;
|
|
|
|
/*!< Reset
|
|
* HSION (disable HSI),
|
|
* HSIDIVEN (disable 18MHz HSI division)
|
|
* HSEON (disable HSE clock)
|
|
* CSSHSEON (disable HSE clock monitoring)
|
|
* PLLON (disable PLL)
|
|
*/
|
|
RCC->CR &= ~RCC_CR_HSION & ~RCC_CR_HSIDIVEN & ~RCC_CR_HSEON &
|
|
~RCC_CR_CSSHSEON & ~RCC_CR_PLLON;
|
|
|
|
/*!< Reset HSEBYP bit (disable HSE bypass) */
|
|
RCC->CR &= ~RCC_CR_HSEBYP;
|
|
|
|
/*!< Reset
|
|
* PLLSRC (HSI16 is the PLL source),
|
|
* PLLMUL[3:0] (3x PLL multiplication)
|
|
* Don't touch PLLDIV[1:0], since 0 is undefined
|
|
*/
|
|
RCC->CFGR &= ~RCC_CFGR_PLLSRC & ~RCC_CFGR_PLLMUL & ~RCC_CFGR_PLLDIV;
|
|
|
|
|
|
/*!< Disable all interrupts */
|
|
RCC->CIER = 0x00000000;
|
|
|
|
/* Configure the Vector Table location add offset address ------------------*/
|
|
SCB->VTOR = FLASH_BASE; /* Vector Table Relocation in Internal FLASH */
|
|
}
|
|
|
|
void init_lptim()
|
|
{
|
|
/* Enable APB1 for LPTIM */
|
|
RCC->APB1ENR |= RCC_APB1ENR_LPTIM1EN;
|
|
|
|
// Enable low-speed internal
|
|
RCC->CSR |= RCC_CSR_LSION;
|
|
while (!(RCC->CSR & RCC_CSR_LSIRDY)) {};
|
|
|
|
/*!< Set the LSI clock to be the source of the LPTIM */
|
|
RCC->CCIPR &= ~RCC_CCIPR_LPTIM1SEL;
|
|
RCC->CCIPR |= RCC_CCIPR_LPTIM1SEL_0;
|
|
|
|
/** Write CR CFGR and IER while LPTIM is disabled (LPTIM_CR_ENABLE not yet set) */
|
|
|
|
/*!< Disable Interrupts (not needed, this is the default */
|
|
LPTIM1->IER = 0;
|
|
|
|
/*!< Reset
|
|
* ENC (Disable encoder mode)
|
|
* TIMOUT (disable timeout mode)
|
|
* TRIGEN (Trigger count start with software only)
|
|
* PRELOAD (Update ARR and CMP registers immediately after write)
|
|
* CKSEL (LPTIM is using internal clock source)
|
|
* COUNTMODE (LPTIM counter updated on every clock pulse)
|
|
* TRGFLT (Do not debounce triggers)
|
|
*/
|
|
LPTIM1->CFGR &= ~LPTIM_CFGR_ENC & ~LPTIM_CFGR_TIMOUT & ~LPTIM_CFGR_TRIGEN &
|
|
~LPTIM_CFGR_TRIGSEL & ~LPTIM_CFGR_PRELOAD & ~LPTIM_CFGR_CKSEL &
|
|
~LPTIM_CFGR_COUNTMODE;
|
|
|
|
/*!< Set
|
|
* PRESC (Set prescaler to 128. Using 32kHz LSI as input, this should
|
|
* correspond to 250Hz counting.
|
|
*/
|
|
LPTIM1->CFGR &= LPTIM_CFGR_PRESC;
|
|
LPTIM1->CFGR |= 7u << LPTIM_CFGR_PRESC_Pos;
|
|
|
|
LPTIM1->CR |= LPTIM_CR_ENABLE;
|
|
|
|
/*!< Do not modify ARR and CMP until after ENABLE bit is set */
|
|
/*!< Produce a 60Hz, 50% duty cycle square wave. These values were
|
|
* determined experimentally. */
|
|
LPTIM1->ARR = 9;
|
|
LPTIM1->CMP = 4;
|
|
while(!(LPTIM1->ISR & LPTIM_ISR_ARROK)) {}
|
|
while(!(LPTIM1->ISR & LPTIM_ISR_CMPOK)) {}
|
|
|
|
/*!< Enable and start the timer */
|
|
LPTIM1->CR |= LPTIM_CR_CNTSTRT;
|
|
}
|
|
|
|
/*!< The buffer that will be used in the future to display on the watch display. */
|
|
//static uint8_t display_buffer[144 * 168 / 8];
|
|
|
|
void init_lptim_toggler() {
|
|
init_lptim();
|
|
|
|
/* Assign LPTIM1_OUT to PA7 */
|
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL7;
|
|
GPIOA->AFR[0] |= 1 << GPIO_AFRL_AFRL7_Pos;
|
|
|
|
GPIOA->MODER &= ~GPIO_MODER_MODE7;
|
|
GPIOA->MODER |= 2u << GPIO_MODER_MODE7_Pos;
|
|
|
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_7;
|
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD7;
|
|
}
|
|
|
|
void init_spi_display()
|
|
{
|
|
RCC->APB2ENR = RCC_APB2ENR_SPI1EN;
|
|
|
|
GPIOB->OSPEEDR = ~0;
|
|
|
|
/* Assign SPI_MOSI to PA12 (AFRH5), since PA7 is taken by LPTIM_OUT */
|
|
GPIOA->AFR[1] &= ~GPIO_AFRH_AFRH4;
|
|
|
|
GPIOA->MODER &= ~GPIO_MODER_MODE12;
|
|
GPIOA->MODER |= 2u << GPIO_MODER_MODE12_Pos;
|
|
|
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_12;
|
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD12;
|
|
|
|
// SPI1 NSS
|
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL4;
|
|
|
|
GPIOA->MODER &= ~GPIO_MODER_MODE4;
|
|
GPIOA->MODER |= 2u << GPIO_MODER_MODE4_Pos;
|
|
|
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_4;
|
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD4;
|
|
// enable pullup, since the pin doesn't seem to stay up
|
|
GPIOA->PUPDR |= 2u << GPIO_PUPDR_PUPD4_Pos;
|
|
|
|
// SPI1 SCK
|
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL5;
|
|
|
|
GPIOA->MODER &= ~GPIO_MODER_MODE5;
|
|
GPIOA->MODER |= 2u << GPIO_MODER_MODE5_Pos;
|
|
|
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_5;
|
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5;
|
|
|
|
// SPI1 MISO
|
|
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL6;
|
|
|
|
GPIOA->MODER &= ~GPIO_MODER_MODE6;
|
|
GPIOA->MODER |= 2u << GPIO_MODER_MODE6_Pos;
|
|
|
|
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_6;
|
|
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD6;
|
|
|
|
SPI1->CR1 |= SPI_CR1_SPE | SPI_CR1_MSTR | SPI_CR1_SSOE;// | SPI_CR1_SSM | SPI_CR1_SSI;
|
|
// SPI1->CR1 |= SPI_CR1_BR;
|
|
}
|
|
|
|
int main() {
|
|
/** Enable Port A,B clock */
|
|
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
|
|
RCC->IOPENR |= RCC_IOPENR_IOPBEN;
|
|
|
|
/** Enable pin P3 for output */
|
|
GPIOB->MODER &= ~GPIO_MODER_MODE3;
|
|
GPIOB->MODER |= GPIO_MODER_MODE3_0;
|
|
|
|
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_3;
|
|
GPIOB->PUPDR &= GPIO_PUPDR_PUPD3;
|
|
|
|
init_lptim_toggler();
|
|
init_spi_display();
|
|
|
|
// GPIOB->ODR |= GPIO_ODR_OD3;
|
|
// for (volatile int i = 0; i < 100000; i++) {}
|
|
// GPIOB->ODR &= ~GPIO_ODR_OD3;
|
|
|
|
while (1) {
|
|
if (SPI1->SR & SPI_SR_TXE) {
|
|
SPI1->DR = 0xA5;
|
|
GPIOB->ODR |= GPIO_ODR_OD3;
|
|
} else {
|
|
GPIOB->ODR &= ~GPIO_ODR_OD3;
|
|
}
|
|
}
|
|
}
|