mqtt2LoRaWAN/stm32/i2c.c

276 lines
9.7 KiB
C
Raw Normal View History

/*
* Copyright (c) 2014-2016 IBM Corporation.
* 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 <organization> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 <COPYRIGHT HOLDER> BE 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 "hw.h"
#define I2C_TIMEOUT_ticks 1000
#define I2C_BUS 1
#define I2C_SCL_PIN 8
#define I2C_SDA_PIN 9
#define I2Cx I2C1
// I2Cs are clocked by the APB1 clock, can run from 2MHz to 32MHz
// The I2C_APB1_FREQ sets up this frequency as a value in MHz
// (The I2C interface needs to know the speed of the bus in order to produce the
// correct clock )
// Note:
// Unfortunately, neither 8 MHz nor 32 MHz seemeed to work with the Semtech
// boards ... just define this constant based on the real freq of the APB1
#define I2C_APB1_FREQ 32
// APB1 frequency must be
// - at least 2 MHz to achieve standard mode I<>C frequencies
// - at least 4 MHz to achieve fast mode I<>C frequencies
// - multiple of 10MHz to reach the 400 kHz maximum I<>C fast mode clock
// I2C_DEFAULT_CCR is the default divider for the APB1_Freq
// In standard mode, I2C frequency can be computed as:
// I2C_Freq = APB1_Freq / CCR
// Thus:
// 400 kHz = 32 MHz / 80 -> CCR = 80
// 100 kHz = 32 MHz / 320 -> CCR = 320
// In fast mode, the computation is based on the duty cycle
// Assuming duty cycle is Tlow/Thigh = 2:1
// I2C_Freq = APB1_Freq / ( 2 * CCR + 1 * CCR )
// Thus:
// 400 kHz = 32 MHz / (3 * 26.666) -> rounding
// CCR = 27 -> 395.062 kHz
// CCR = 26 -> 410.256 kHz
// #define I2C_DEFAULT_CCR 320 // standard mode @ 100kHz
// #define I2C_DEFAULT_CCR 27 // fast mode @ 395kHz
#define I2C_DEFAULT_CCR (I2C_APB1_FREQ * 1000 / (3 * 400))
// -- Duty cycle / fast mode configuration
// FAST_MODE: Standard (0) or Fast mode (1) setup
// DUTY_CYCLE: Tlow/Thigh = 2:1 (0) or Tlow/Thigh = 16:9 (1)
// 16/9 mode is not implemented
#define I2C_FAST_MODE 1
#define I2C_DUTY_CYCLE 0
// TRISE - Nicely undocumented by STM32, one needs to refer
// to the I2C specification.
// For standard mode, just use I2C_APB1_FREQ + 1
// For fast mode, just do the same as STM does in their lib
// TRISE = I2C_APB1_FREQ * 300 / 1000 + 1;
#define I2C_DEFAULT_TRISE (((I2C_APB1_FREQ * 3) / 10) + 1 )
// #define I2C_DEFAULT_TRISE (I2C_APB1_FREQ + 1) // standard speed
static u1_t i2c_isBusy () {
// wait for bus to be free for max 20ms
ostime_t timeout = os_getTime() + ms2osticks(20);
while ((I2Cx->SR2 & I2C_SR2_BUSY) && os_getTime() < timeout); // wait...
// if still busy, send STOP, something went terribly wrong!
if (I2Cx->SR2 & I2C_SR2_BUSY) {
I2Cx->CR1 |= I2C_CR1_STOP;
return 1;
}
return 0; // bus free
}
// the addr has to be left aligned, with the read (aka not-write) bit set
static s1_t i2c_send_start (u1_t addr) {
I2Cx->CR1 |= I2C_CR1_START; // send start condition
u1_t timeout = 0xff;
while(!(I2Cx->SR1 & I2C_SR1_SB) && timeout-- > 0); // wait for SB (start bit / start condition generated)
if (timeout == 0) return -1;
I2Cx->DR = addr & I2C_DR_DR; // send slave address
timeout = 0xff;
// address is being TXed
while(!(I2Cx->SR1 & I2C_SR1_ADDR) && !(I2Cx->SR1 & I2C_SR1_AF) && timeout-- > 0);
if ( timeout == 0 || (I2Cx->SR1 & I2C_SR1_AF) ) {
// the address is not on the bus
I2Cx->SR1 &= ~I2C_SR1_AF; // make sure the flash is cleared
return -1;
}
return 0;
}
static s1_t i2c_restart_write (u1_t addr){
if (i2c_send_start(addr) != 0)
return -1;
// check that the start was ACKed
if (!(I2Cx->SR2 & I2C_SR2_TRA)){
// wait a bit
hal_waitUntil(os_getTime()+us2osticks(50));
if (!(I2Cx->SR2 & I2C_SR2_TRA))
return -2; // utterly broken I2C (a STOP will be sent by i2c main loop)
}
// OK now
return 0;
}
static s1_t i2c_restart_read (u1_t addr) {
if (i2c_send_start(addr | 1) != 0)
return -1;
// check that the start was ACKed
if (I2Cx->SR2 & I2C_SR2_TRA){
// wait a bit
hal_waitUntil(os_getTime()+us2osticks(50));
if (I2Cx->SR2 & I2C_SR2_TRA)
return -2; // utterly broken I2C (a STOP will be sent by i2c main loop)
}
// OK now
return 0;
}
u1_t i2c_init (void) {
// setup the PB10 and PB11 pins as OC outputs
// enable power to GPIOs
RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
#if I2C_BUS == 2 //TODO:xan: change to use "hw_cfg_pin" for the pin configuration!
// select AF4 for the pins
GPIOB->AFR[1] |= 4 << 2*4 | 4 << 3*4 ;
// there are external pull-ups, no internal pulling
GPIOB->PUPDR &= ~( 3 << 10*2 | 3 << 11*2 );
// write 2 (= alternate function) in the MODER
GPIOB->MODER |= 2 << 10*2 | 2 << 11*2;
// set open drain on the pins
GPIOB->OTYPER |= 1 << 10 | 1 << 11;
GPIOB->OSPEEDR &= ~(3 << 10*2 | 3 << 11*2); // 0 is default, no need to set
// enable clock to the I2C
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
// reseting the bus (optional)
// I2Cx->CR1 |= I2C_CR1_SWRST;
// I2Cx->CR1 &= ~I2C_CR1_SWRST;
// while (I2Cx->CR1 & I2C_CR1_SWRST);
// disable I2C
I2Cx->CR1 &= ~I2C_CR1_PE;
// setup frequency
I2Cx->CR2 |= I2C_APB1_FREQ & I2C_CR2_FREQ;
// the CCR must be configured when the I2C is disabled (PE=0)
I2Cx->CCR |=
((I2C_FAST_MODE << 15) & I2C_CCR_FS) |
((I2C_DUTY_CYCLE << 14) & I2C_CCR_DUTY) |
( I2C_DEFAULT_CCR & I2C_CCR_CCR);
I2Cx->TRISE |= (I2C_DEFAULT_TRISE) & I2C_TRISE_TRISE; // max. rise time
I2Cx->CR1 |= I2C_CR1_PE; // enable peripheral
#elif I2C_BUS == 1
// set up the I2C1 pins
if (I2Cx->SR2 & I2C_SR2_BUSY) {
// brute force 'wobble clock'
hw_cfg_pin(GPIOB,I2C_SCL_PIN,GPIOCFG_MODE_OUT|GPIOCFG_OSPEED_40MHz|GPIO_AF_I2C1|GPIOCFG_OTYPE_OPEN|GPIOCFG_PUPD_NONE);
for (int i=0;i<64;i++) {
GPIOB->ODR |= (1<<I2C_SCL_PIN);
GPIOB->ODR &= ~(1<<I2C_SCL_PIN);
}
}
// correct pin configuration
hw_cfg_pin(GPIOB,I2C_SCL_PIN,GPIOCFG_MODE_ALT|GPIOCFG_OSPEED_40MHz|GPIO_AF_I2C1|GPIOCFG_OTYPE_OPEN|GPIOCFG_PUPD_NONE);
hw_cfg_pin(GPIOB,I2C_SDA_PIN,GPIOCFG_MODE_ALT|GPIOCFG_OSPEED_40MHz|GPIO_AF_I2C1|GPIOCFG_OTYPE_OPEN|GPIOCFG_PUPD_NONE);
// enable the clock for the I2C1 module
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
I2Cx->CR1 &= ~I2C_CR1_PE; // disable I2C peripheral
// setup I2C frequency for SCL (must be done only when peripheral is disabled!)
I2Cx->CR2 |= 32; // we run at 32 MHz
//I2Cx->CCR = 160; // SCL 100 kHz
I2Cx->CCR = 40; // SCL 400 kHz
I2Cx->TRISE = 33; // max 1000 ns
I2Cx->CR1 |= I2C_CR1_PE; // enable peripheral
#endif
return 0;
}
s1_t i2c_xfer (u1_t addr, u1_t* data, u1_t wlen, u1_t rlen) {
s1_t errc = 0;
hal_disableIRQs();
// check that the bus is not stuck a previous transaction
if (i2c_isBusy()) { // will also try to 'mend' the bus
errc = 0xDD;
goto finish;
}
if (wlen > 0) {
if ( (errc = i2c_restart_write(addr)) != 0)
goto finish;
for (u1_t i = 0; i < wlen; i++) {
I2Cx->DR = data[i] & I2C_DR_DR;
while(!(I2Cx->SR1 & I2C_SR1_BTF) && !(I2Cx->SR1 & I2C_SR1_TXE));
if (I2Cx->SR1 & I2C_SR1_AF) { // no ACK
I2Cx->SR1 &= ~I2C_SR1_AF; // clear flag
errc = -1;
goto finish;
}
}
while( !(I2Cx->SR1 & I2C_SR1_BTF) ); // wait for the last byte to be transferred
}
hal_waitUntil(os_getTime() + us2osticks(50));
if (rlen > 0){
if ( (errc=i2c_restart_read(addr)) != 0)
goto finish;
for (u1_t i = 0; i < rlen; i++){
// we always ACK the received data, except last byte
if ( i == (rlen-1))
I2Cx->CR1 &= ~I2C_CR1_ACK;
else
I2Cx->CR1 |= I2C_CR1_ACK;
u2_t counter = 0;
while(!(I2Cx->SR1 & I2C_SR1_RXNE)) {
if (counter++ > 50000) { // safety timeout, in case nothing is received
errc = -2;
goto finish;
}
}
data[i] = I2Cx->DR;
}
}
finish:
I2Cx->CR1 |= I2C_CR1_STOP;
hal_enableIRQs();
return errc;
}