Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: azonenberg/stm32-cpp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 1c093a0d8ec2
Choose a base ref
...
head repository: azonenberg/stm32-cpp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: b6553819c007
Choose a head ref
  • 1 commit
  • 9 files changed
  • 1 contributor

Commits on Jul 4, 2020

  1. Initial I2C support

    azonenberg committed Jul 4, 2020
    Copy the full SHA
    b655381 View commit details
Showing with 278 additions and 50 deletions.
  1. +39 −5 devices/inc/stm32f031.h
  2. +6 −0 devices/link/stm32f031.ld
  3. +2 −0 devices/src/stm32f031.cpp
  4. +107 −0 src/peripheral/I2C.cpp
  5. +59 −0 src/peripheral/I2C.h
  6. +9 −0 src/peripheral/RCC.cpp
  7. +2 −1 src/peripheral/RCC.h
  8. +39 −43 src/peripheral/Timer.cpp
  9. +15 −1 src/peripheral/Timer.h
44 changes: 39 additions & 5 deletions devices/inc/stm32f031.h
Original file line number Diff line number Diff line change
@@ -71,11 +71,12 @@ enum rcc_apb2

enum rcc_apb1
{
RCC_APB1_TIM14 = 0x0100,
RCC_APB1_TIM7 = 0x0020,
RCC_APB1_TIM6 = 0x0010,
RCC_APB1_TIM3 = 0x0002,
RCC_APB1_TIM2 = 0x0001
RCC_APB1_I2C1 = 0x200000,
RCC_APB1_TIM14 = 0x000100,
RCC_APB1_TIM7 = 0x000020,
RCC_APB1_TIM6 = 0x000010,
RCC_APB1_TIM3 = 0x000002,
RCC_APB1_TIM2 = 0x000001
};

typedef struct
@@ -207,4 +208,37 @@ extern volatile tim_t TIM14;
extern volatile tim_t TIM16;
extern volatile tim_t TIM17;

enum i2c_cr2_bits
{
I2C_AUTO_END = 0x02000000,
I2C_STOP = 0x00004000,
I2C_START = 0x00002000,
I2C_READ = 0x00000400
};

enum i2c_isr_bits
{
I2C_BUSY = 0x8000,
I2C_TRANSFER_COMPLETE = 0x0040,
I2C_RX_READY = 0x0004,
I2C_TX_EMPTY = 0x0001
};

typedef struct
{
uint32_t CR1;
uint32_t CR2;
uint32_t OAR1;
uint32_t OAR2;
uint32_t TIMINGR;
uint32_t TIMEOUTR;
uint32_t ISR;
uint32_t ICR;
uint32_t PECR;
uint32_t RXDR;
uint32_t TXDR;
} i2c_t;

extern volatile i2c_t I2C1;

#endif
6 changes: 6 additions & 0 deletions devices/link/stm32f031.ld
Original file line number Diff line number Diff line change
@@ -80,6 +80,12 @@ SECTIONS
*(.tim14)
. = ALIGN(1024);
. += 1024; /* Reserved */
. += 1024; /* RTC */
. += 1024; /* WWDG */
. += 1024; /* IWDG */
. += 8192; /* Reserved */
. = ALIGN(1024);
*(.i2c1)
} > APB1

.sfr_apb2 :
2 changes: 2 additions & 0 deletions devices/src/stm32f031.cpp
Original file line number Diff line number Diff line change
@@ -37,6 +37,8 @@ volatile rcc_t RCC __attribute__((section(".rcc")));

//volatile flash_t FLASH __attribute__((section(".flash")));

volatile i2c_t I2C1 __attribute__((section(".i2c1")));

volatile spi_t SPI1 __attribute__((section(".spi1")));

volatile syscfg_t SYSCFG __attribute__((section(".syscfg")));
107 changes: 107 additions & 0 deletions src/peripheral/I2C.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/***********************************************************************************************************************
* *
* STM32-CPP v0.1 *
* *
* Copyright (c) 2020 Andrew D. Zonenberg *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
* following conditions are met: *
* *
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
* following disclaimer. *
* *
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
* following disclaimer in the documentation and/or other materials provided with the distribution. *
* *
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
* *
***********************************************************************************************************************/

#include <stm32fxxx.h>
#include <ctype.h>
#include <string.h>
#include <peripheral/RCC.h>
#include <peripheral/I2C.h>

/**
@brief Initialize an I2C lane
@param lane The peripheral to use
@param prescale Prescale divisor from APB clock
@param clkdiv Clock high/low divider from prescaled divider
*/
I2C::I2C(volatile i2c_t* lane, uint8_t prescale, uint8_t clkdiv)
: m_lane(lane)
{
RCCHelper::Enable(lane);

lane->CR2 = 0;

lane->TIMINGR =
( (prescale - 1) << 28 ) | //prescaler
1 << 20 | //Data setup time: 2 prescaled ticks
1 << 16 | //Data hold time: 2 prescaled ticks
(clkdiv-1) << 8 | //SCL high time
(clkdiv-1); //SCL low time

//Enable the peripheral
lane->CR1 = 1;
}

/**
@brief Sends a write command
@param addr 8-bit device address (LSB ignored)
@param data Data to send
@param len Number of bytes to send
*/
void I2C::BlockingWrite(uint8_t addr, const uint8_t* data, uint8_t len)
{
//Auto end, specified length
m_lane->CR2 = I2C_AUTO_END | (len << 16) | I2C_START | addr;

//Send the data
for(uint8_t i=0; i<len; i++)
{
//Wait for last byte to finish
while(!(m_lane->ISR & I2C_TX_EMPTY))
{}

//Send it
m_lane->TXDR = data[i];
}

//Wait for send to finish
while((m_lane->ISR & I2C_TX_EMPTY) == 0)
{}
}

/**
@brief Sends a read command
*/
void I2C::BlockingRead(uint8_t addr, uint8_t* data, uint8_t len)
{
//Auto end, specified length
m_lane->CR2 = I2C_AUTO_END | (len << 16) | I2C_START | addr | I2C_READ;

//Read the data
for(uint8_t i=0; i<len; i++)
{
//Wait for data to be ready
while(!(m_lane->ISR & I2C_RX_READY))
{}

//Read it
data[i] = m_lane->RXDR;
}
}
59 changes: 59 additions & 0 deletions src/peripheral/I2C.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/***********************************************************************************************************************
* *
* STM32-CPP v0.1 *
* *
* Copyright (c) 2020 Andrew D. Zonenberg *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the *
* following conditions are met: *
* *
* * Redistributions of source code must retain the above copyright notice, this list of conditions, and the *
* following disclaimer. *
* *
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the *
* following disclaimer in the documentation and/or other materials provided with the distribution. *
* *
* * Neither the name of the author nor the names of any contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
* POSSIBILITY OF SUCH DAMAGE. *
* *
***********************************************************************************************************************/

#ifndef i2c_h
#define i2c_h

class I2C
{
public:
I2C(volatile i2c_t* lane, uint8_t prescale, uint8_t clkdiv);

void BlockingRead(uint8_t addr, uint8_t* data, uint8_t len);

void BlockingWrite(uint8_t addr, const uint8_t* data, uint8_t len);

void BlockingWrite8(uint8_t addr, uint8_t data)
{ BlockingWrite(addr, &data, 1); }

void BlockingWrite16(uint8_t addr, uint16_t data)
{
uint8_t buf[2] =
{
static_cast<uint8_t>(data >> 8),
static_cast<uint8_t>(data & 0xff)
};
BlockingWrite(addr, buf, 2);
}

protected:
volatile i2c_t* m_lane;
};

#endif
9 changes: 9 additions & 0 deletions src/peripheral/RCC.cpp
Original file line number Diff line number Diff line change
@@ -59,6 +59,15 @@ void RCCHelper::Enable(volatile spi_t* spi)
RCC.APB2ENR |= RCC_APB2_SPI1;
}

/**
@brief Enable an I2C bus
*/
void RCCHelper::Enable(volatile i2c_t* i2c)
{
if(i2c == &I2C1)
RCC.APB1ENR |= RCC_APB1_I2C1;
}

/**
@brief Enable a timer
*/
3 changes: 2 additions & 1 deletion src/peripheral/RCC.h
Original file line number Diff line number Diff line change
@@ -43,9 +43,10 @@ class RCCHelper
{
public:
static void Enable(volatile gpio_t* gpio);
static void Enable(volatile usart_t* uart);
static void Enable(volatile i2c_t* i2c);
static void Enable(volatile spi_t* spi);
static void Enable(volatile tim_t* tim);
static void Enable(volatile usart_t* uart);

#ifdef STM32F0
static void InitializePLLFromInternalOscillator(
82 changes: 39 additions & 43 deletions src/peripheral/Timer.cpp
Original file line number Diff line number Diff line change
@@ -39,55 +39,51 @@
@param chan The peripheral to use
@param features Capabilities of the requested timer
*/
Timer::Timer(volatile tim_t* chan, Features features)
Timer::Timer(volatile tim_t* chan, Features features, uint16_t prescale)
: m_chan(chan)
, m_features(features)
{
RCCHelper::Enable(chan);
/*
//8-bit word size
//TODO: make this configurable
lane->CR2 = 7 << 8;

//Turn on the peripheral in master mode.
//To prevent problems, we need to have the internal CS# pulled high.
//TODO: support slave mode
lane->CR1 = SPI_MASTER | SPI_SOFT_CS | SPI_INTERNAL_CS;
lane->CR1 |= SPI_ENABLE;
//Configure the counter
chan->CNT = 0x0;
chan->PSC = (prescale - 1);

//Calculate correct init value for baud rate divisor
switch(baudDiv)
{
case 2:
break;
case 4:
lane->CR1 |= 0x8;
break;
case 8:
lane->CR1 |= 0x10;
break;
case 16:
lane->CR1 |= 0x18;
break;
case 32:
lane->CR1 |= 0x20;
break;
case 64:
lane->CR1 |= 0x28;
break;
case 128:
lane->CR1 |= 0x30;
break;
//Turn off most features
chan->CR2 = 0x0;
chan->SMCR = 0x0;
chan->DIER = 0x0;
chan->CCMR1 = 0x0;
chan->CCMR2 = 0x0;
chan->CCR1 = 0x0;
chan->CCR2 = 0x0;
chan->CCR3 = 0x0;
chan->CCR4 = 0x0;
chan->CCER = 0x0;
chan->BDTR = 0x0;
chan->DCR = 0x0;
chan->DMAR = 0x0;

//use max value for invalid divisor
case 256:
default:
lane->CR1 |= 0x38;
break;
}
//Reset the counter
chan->CR1 = 0x0;
chan->EGR = 0x1;

//Enable bidirectional mode if requested
if(!fullDuplex)
lane->CR1 |= SPI_BIDI_MODE;
*/
//Enable the counter
chan->CR1 = 0x1;
}

/**
@brief Blocking wait until the timer has advanced by the specified number of ticks
@param ticks Number of ticks to wait
@param reset If true, restart the counter from zero before starting the delay interval
*/
void Timer::Sleep(uint16_t ticks, bool reset)
{
if(reset)
Restart();

unsigned int target = m_chan->CNT + ticks;
while(m_chan->CNT != target)
{}
}
16 changes: 15 additions & 1 deletion src/peripheral/Timer.h
Original file line number Diff line number Diff line change
@@ -39,7 +39,21 @@ class Timer
FEATURE_ADVANCED
};

Timer(volatile tim_t* chan, Features features);
Timer(volatile tim_t* chan, Features features, uint16_t prescale);

/**
@brief Gets the current counter value
*/
unsigned int GetCount()
{ return m_chan->CNT; }

/**
@brief Restarts the counter from the default value (0 if up, auto reload value if down)
*/
void Restart()
{ m_chan->EGR = 0x1; }

void Sleep(uint16_t ticks, bool reset = false);

protected:
volatile tim_t* m_chan;