stm32f2: add initial support for stm32f2
parent
8518843a40
commit
4064858e8d
@ -0,0 +1,7 @@
|
||||
# define the module that is build
|
||||
MODULE = cpu
|
||||
|
||||
# add a list of subdirectories, that should also be build
|
||||
DIRS += periph $(RIOTCPU)/cortexm_common
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
@ -0,0 +1,6 @@
|
||||
export CPU_ARCH = cortex-m3
|
||||
|
||||
# use common periph functions
|
||||
USEMODULE += periph_common
|
||||
|
||||
include $(RIOTCPU)/Makefile.include.cortexm_common
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the kernel cpu functions
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#ifdef HSI_VALUE
|
||||
# define RCC_CR_SOURCE RCC_CR_HSION
|
||||
# define RCC_CR_SOURCE_RDY RCC_CR_HSIRDY
|
||||
# define RCC_PLL_SOURCE RCC_PLLCFGR_PLLSRC_HSI
|
||||
#else
|
||||
# define RCC_CR_SOURCE RCC_CR_HSEON
|
||||
# define RCC_CR_SOURCE_RDY RCC_CR_HSERDY
|
||||
# define RCC_PLL_SOURCE RCC_PLLCFGR_PLLSRC_HSE
|
||||
#endif
|
||||
|
||||
static void clk_init(void);
|
||||
|
||||
void cpu_init(void)
|
||||
{
|
||||
/* initialize the Cortex-M core */
|
||||
cortexm_init();
|
||||
/* initialize system clocks */
|
||||
clk_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure the clock system of the stm32f2
|
||||
*
|
||||
*/
|
||||
static void clk_init(void)
|
||||
{
|
||||
/* Reset the RCC clock configuration to the default reset state(for debug purpose) */
|
||||
/* Set HSION bit */
|
||||
RCC->CR |= 0x00000001U;
|
||||
/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
|
||||
RCC->CFGR = 0x00000000U;
|
||||
/* Reset HSEON, CSSON and PLLON bits */
|
||||
RCC->CR &= 0xFEF6FFFFU;
|
||||
/* Reset PLLCFGR register */
|
||||
RCC->PLLCFGR = 0x24003010U;
|
||||
/* Reset HSEBYP bit */
|
||||
RCC->CR &= 0xFFFBFFFFU;
|
||||
/* Disable all interrupts and clear pending bits */
|
||||
RCC->CIR = 0x00000000U;
|
||||
|
||||
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration */
|
||||
/* Enable the high speed clock source */
|
||||
RCC->CR |= RCC_CR_SOURCE;
|
||||
/* Wait till hish speed clock source is ready,
|
||||
* NOTE: the MCU will stay here forever if no HSE clock is connected */
|
||||
while ((RCC->CR & RCC_CR_SOURCE_RDY) == 0);
|
||||
/* Configure Flash prefetch, Instruction cache, Data cache and wait state */
|
||||
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
|
||||
/* Flash 2 wait state */
|
||||
FLASH->ACR &= ~((uint32_t)FLASH_ACR_LATENCY);
|
||||
FLASH->ACR |= (uint32_t)CLOCK_FLASH_LATENCY;
|
||||
/* HCLK = SYSCLK */
|
||||
RCC->CFGR |= (uint32_t)CLOCK_AHB_DIV;
|
||||
/* PCLK2 = HCLK */
|
||||
RCC->CFGR |= (uint32_t)CLOCK_APB2_DIV;
|
||||
/* PCLK1 = HCLK */
|
||||
RCC->CFGR |= (uint32_t)CLOCK_APB1_DIV;
|
||||
/* PLL configuration: PLLCLK = SOURCE_CLOCK / SOURCE_CLOCK_DIV * SOURCE_CLOCK_MUL */
|
||||
RCC->PLLCFGR &= ~((uint32_t)(RCC_PLLCFGR_PLLSRC | RCC_PLLCFGR_PLLN | RCC_PLLCFGR_PLLM | RCC_PLLCFGR_PLLP | RCC_PLLCFGR_PLLQ));
|
||||
RCC->PLLCFGR |= (uint32_t)(RCC_PLL_SOURCE | CLOCK_PLL_DIV | (CLOCK_PLL_MUL << 6));
|
||||
/* Enable PLL */
|
||||
RCC->CR |= RCC_CR_PLLON;
|
||||
/* Wait till PLL is ready */
|
||||
while ((RCC->CR & RCC_CR_PLLRDY) == 0);
|
||||
/* Select PLL as system clock source */
|
||||
RCC->CFGR &= ~((uint32_t)(RCC_CFGR_SW));
|
||||
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
|
||||
/* Wait till PLL is used as system clock source */
|
||||
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup cpu_stm32f2 STM32F2
|
||||
* @ingroup cpu
|
||||
* @brief CPU specific implementations for the STM32F2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation specific CPU configuration options
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl
|
||||
*/
|
||||
|
||||
#ifndef __CPU_CONF_H
|
||||
#define __CPU_CONF_H
|
||||
|
||||
#include "stm32f2xx.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ARM Cortex-M specific CPU configuration
|
||||
* @{
|
||||
*/
|
||||
#define CPU_DEFAULT_IRQ_PRIO (1U)
|
||||
#define CPU_IRQ_NUMOF (81U)
|
||||
#define CPU_FLASH_BASE FLASH_BASE
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Length for reading CPU_ID
|
||||
*/
|
||||
#define CPUID_ID_LEN (12)
|
||||
|
||||
/**
|
||||
* @brief Configure the CPU's clock system
|
||||
*
|
||||
* @param[in] source source clock frequency
|
||||
* @param[in] target target clock frequency
|
||||
* @param[in] prescale prescaler to use
|
||||
*/
|
||||
void cpu_clock_scale(uint32_t source, uint32_t target, uint32_t *prescale);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CPU_CONF_H */
|
||||
/** @} */
|
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief CPU specific definitions for internal peripheral handling
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_H
|
||||
#define PERIPH_CPU_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Overwrite the default gpio_t type definition
|
||||
* @{
|
||||
*/
|
||||
#define HAVE_GPIO_T
|
||||
typedef uint32_t gpio_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Definition of a fitting UNDEF value
|
||||
*/
|
||||
#define GPIO_UNDEF (0xffffffff)
|
||||
|
||||
/**
|
||||
* @brief Define a CPU specific GPIO pin generator macro
|
||||
*/
|
||||
#define GPIO_PIN(x, y) ((GPIOA_BASE + (x << 10)) | y)
|
||||
|
||||
/**
|
||||
* @brief declare needed generic SPI functions
|
||||
* @{
|
||||
*/
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_BYTES
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_REG
|
||||
#define PERIPH_SPI_NEEDS_TRANSFER_REGS
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Available ports on the STM32F2 family
|
||||
*/
|
||||
enum {
|
||||
PORT_A = 0, /**< port A */
|
||||
PORT_B = 1, /**< port B */
|
||||
PORT_C = 2, /**< port C */
|
||||
PORT_D = 3, /**< port D */
|
||||
PORT_E = 4, /**< port E */
|
||||
PORT_F = 5, /**< port F */
|
||||
PORT_G = 6, /**< port G */
|
||||
PORT_H = 7, /**< port H */
|
||||
PORT_I = 8 /**< port I */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Available MUX values for configuring a pin's alternate function
|
||||
*/
|
||||
typedef enum {
|
||||
GPIO_AF0 = 0, /**< use alternate function 0 */
|
||||
GPIO_AF1, /**< use alternate function 1 */
|
||||
GPIO_AF2, /**< use alternate function 2 */
|
||||
GPIO_AF3, /**< use alternate function 3 */
|
||||
GPIO_AF4, /**< use alternate function 4 */
|
||||
GPIO_AF5, /**< use alternate function 5 */
|
||||
GPIO_AF6, /**< use alternate function 6 */
|
||||
GPIO_AF7, /**< use alternate function 7 */
|
||||
GPIO_AF8, /**< use alternate function 8 */
|
||||
GPIO_AF9, /**< use alternate function 9 */
|
||||
GPIO_AF10, /**< use alternate function 10 */
|
||||
GPIO_AF11, /**< use alternate function 11 */
|
||||
GPIO_AF12, /**< use alternate function 12 */
|
||||
GPIO_AF13, /**< use alternate function 13 */
|
||||
GPIO_AF14 /**< use alternate function 14 */
|
||||
} gpio_af_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for UART configuration data
|
||||
* @{
|
||||
*/
|
||||
typedef struct {
|
||||
USART_TypeDef *dev; /**< UART device base register address */
|
||||
uint32_t rcc_mask; /**< bit in clock enable register */
|
||||
gpio_t rx_pin; /**< RX pin */
|
||||
gpio_t tx_pin; /**< TX pin */
|
||||
gpio_af_t af; /**< alternate pin function to use */
|
||||
uint8_t irqn; /**< IRQ channel */
|
||||
uint8_t dma_stream; /**< DMA stream used for TX */
|
||||
uint8_t dma_chan; /**< DMA channel used for TX */
|
||||
} uart_conf_t;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Configure the alternate function for the given pin
|
||||
*
|
||||
* @note This is meant for internal use in STM32F4 peripheral drivers only
|
||||
*
|
||||
* @param[in] pin pin to configure
|
||||
* @param[in] af alternate function to use
|
||||
*/
|
||||
void gpio_init_af(gpio_t pin, gpio_af_t af);
|
||||
|
||||
/**
|
||||
* @brief Power on the DMA device the given stream belongs to
|
||||
*
|
||||
* @param[in] stream logical DMA stream
|
||||
*/
|
||||
static inline void dma_poweron(int stream)
|
||||
{
|
||||
if (stream < 8) {
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
|
||||
} else {
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get DMA base register
|
||||
*
|
||||
* For simplifying DMA stream handling, we map the DMA channels transparently to
|
||||
* one integer number, such that DMA1 stream0 equals 0, DMA2 stream0 equals 8,
|
||||
* DMA2 stream 7 equals 15 and so on.
|
||||
*
|
||||
* @param[in] stream logical DMA stream
|
||||
*/
|
||||
static inline DMA_TypeDef *dma_base(int stream)
|
||||
{
|
||||
return (stream < 8) ? DMA1 : DMA2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the DMA stream base address
|
||||
*
|
||||
* @param[in] stream logical DMA stream
|
||||
*
|
||||
* @return base address for the selected DMA stream
|
||||
*/
|
||||
static inline DMA_Stream_TypeDef *dma_stream(int stream)
|
||||
{
|
||||
uint32_t base = (uint32_t)dma_base(stream);
|
||||
return (DMA_Stream_TypeDef *)(base + (0x10 + (0x18 * (stream & 0x7))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Select high or low DMA interrupt register based on stream number
|
||||
*
|
||||
* @param[in] stream logical DMA stream
|
||||
*
|
||||
* @return 0 for streams 0-3, 1 for streams 3-7
|
||||
*/
|
||||
static inline int dma_hl(int stream)
|
||||
{
|
||||
return ((stream & 0x4) >> 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the interrupt flag clear bit position in the DMA LIFCR register
|
||||
*
|
||||
* @param[in] stream logical DMA stream
|
||||
*/
|
||||
static inline uint32_t dma_ifc(int stream)
|
||||
{
|
||||
switch (stream & 0x3) {
|
||||
case 0:
|
||||
return (1 << 5);
|
||||
case 1:
|
||||
return (1 << 11);
|
||||
case 2:
|
||||
return (1 << 21);
|
||||
case 3:
|
||||
return (1 << 27);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dma_isr_enable(int stream)
|
||||
{
|
||||
if (stream < 7) {
|
||||
NVIC_EnableIRQ((IRQn_Type)((int)DMA1_Stream0_IRQn + stream));
|
||||
}
|
||||
else if (stream == 7) {
|
||||
NVIC_EnableIRQ(DMA1_Stream7_IRQn);
|
||||
}
|
||||
else if (stream < 13) {
|
||||
NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream0_IRQn + (stream - 8)));
|
||||
}
|
||||
else if (stream < 16) {
|
||||
NVIC_EnableIRQ((IRQn_Type)((int)DMA2_Stream5_IRQn + (stream - 13)));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH_CPU_H */
|
||||
/** @} */
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Memory definitions for the STM32F215RG
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
}
|
||||
|
||||
INCLUDE cortexm_base.ld
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Memory definitions for the STM32F215RG
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||
}
|
||||
|
||||
INCLUDE cortexm_base.ld
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Implementation of the kernels power management interface
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzndoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "arch/lpm_arch.h"
|
||||
|
||||
static enum lpm_mode current_mode = LPM_UNKNOWN;
|
||||
|
||||
void lpm_arch_init(void)
|
||||
{
|
||||
current_mode = LPM_ON;
|
||||
}
|
||||
|
||||
enum lpm_mode lpm_arch_set(enum lpm_mode target)
|
||||
{
|
||||
enum lpm_mode last_mode = current_mode;
|
||||
|
||||
switch (target) {
|
||||
case LPM_ON: /* STM Run mode */
|
||||
break;
|
||||
case LPM_IDLE: /* STM Sleep mode */
|
||||
break;
|
||||
case LPM_SLEEP: /* STM Stop mode */
|
||||
break;
|
||||
case LPM_POWERDOWN: /* STM Standby mode */
|
||||
/* Fall-through */
|
||||
case LPM_OFF: /* STM Standby mode */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return last_mode;
|
||||
}
|
||||
|
||||
enum lpm_mode lpm_arch_get(void)
|
||||
{
|
||||
return current_mode;
|
||||
}
|
||||
|
||||
void lpm_arch_awake(void)
|
||||
{
|
||||
if (current_mode == LPM_SLEEP) {
|
||||
/* After stop mode, the clock system needs to be reconfigured */
|
||||
cpu_init();
|
||||
}
|
||||
current_mode = LPM_ON;
|
||||
}
|
||||
|
||||
/** Not provided */
|
||||
inline void lpm_arch_begin_awake(void) { }
|
||||
|
||||
/** Not provided */
|
||||
inline void lpm_arch_end_awake(void) { }
|
@ -0,0 +1,5 @@
|
||||
# define the module name
|
||||
MODULE = periph
|
||||
|
||||
# include RIOTs generic Makefile
|
||||
include $(RIOTBASE)/Makefile.base
|
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level ADC driver implementation
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/adc.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no ADC device is defined */
|
||||
#if ADC_NUMOF
|
||||
|
||||
typedef struct {
|
||||
int max_value;
|
||||
} adc_config_t;
|
||||
|
||||
adc_config_t adc_config[ADC_NUMOF];
|
||||
|
||||
int adc_init(adc_t dev, adc_precision_t precision)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
|
||||
adc_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
ADC_0_PORT_CLKEN();
|
||||
ADC_0_PORT->MODER |= (3 << (ADC_0_CH0_PIN * 2) | 3 << (ADC_0_CH1_PIN * 2));
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
ADC_1_PORT_CLKEN();
|
||||
ADC_1_PORT->MODER |= (3 << (ADC_1_CH0_PIN * 2) | 3 << (ADC_1_CH1_PIN * 2));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* reset control registers */
|
||||
adc->CR1 = 0;
|
||||
adc->CR2 = 0;
|
||||
adc->SQR1 = 0;
|
||||
|
||||
/* set precision */
|
||||
|
||||
switch (precision) {
|
||||
case ADC_RES_6BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_0 | ADC_CR1_RES_1;
|
||||
adc_config[dev].max_value = 0x3f;
|
||||
break;
|
||||
case ADC_RES_8BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_1;
|
||||
adc_config[dev].max_value = 0xff;
|
||||
break;
|
||||
case ADC_RES_10BIT:
|
||||
adc->CR1 |= ADC_CR1_RES_0;
|
||||
adc_config[dev].max_value = 0x3ff;
|
||||
break;
|
||||
case ADC_RES_12BIT:
|
||||
adc_config[dev].max_value = 0xfff;
|
||||
break;
|
||||
case ADC_RES_14BIT:
|
||||
case ADC_RES_16BIT:
|
||||
adc_poweroff(dev);
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* set clock prescaler */
|
||||
ADC->CCR = (3 << 16); /* ADC clock = 10,5MHz */
|
||||
|
||||
/* enable the ADC module */
|
||||
adc->CR2 |= ADC_CR2_ADON;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_sample(adc_t dev, int channel)
|
||||
{
|
||||
ADC_TypeDef *adc = 0;
|
||||
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
adc = ADC_0_DEV;
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SQR3 = ADC_0_CH0 & 0x1f;
|
||||
break;
|
||||
case 1:
|
||||
adc->SQR3 = ADC_0_CH1 & 0x1f;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
adc = ADC_1_DEV;
|
||||
switch (channel) {
|
||||
case 0:
|
||||
adc->SQR3 = ADC_1_CH0 & 0x1f;
|
||||
break;
|
||||
case 1:
|
||||
adc->SQR3 = ADC_1_CH1 & 0x1f;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* start single conversion */
|
||||
adc->CR2 |= ADC_CR2_SWSTART;
|
||||
/* wait until conversion is complete */
|
||||
while (!(adc->SR & ADC_SR_EOC));
|
||||
/* read and return result */
|
||||
return (int)adc->DR;
|
||||
}
|
||||
|
||||
void adc_poweron(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void adc_poweroff(adc_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if ADC_0_EN
|
||||
case ADC_0:
|
||||
ADC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
#if ADC_1_EN
|
||||
case ADC_1:
|
||||
ADC_1_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int adc_map(adc_t dev, int value, int min, int max)
|
||||
{
|
||||
return (int)adc_mapf(dev, value, (float)min, (float)max);
|
||||
}
|
||||
|
||||
float adc_mapf(adc_t dev, int value, float min, float max)
|
||||
{
|
||||
return ((max - min) / ((float)adc_config[dev].max_value)) * value;
|
||||
}
|
||||
|
||||
#endif /* ADC_NUMOF */
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup driver_periph
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level CPUID driver implementation
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "periph/cpuid.h"
|
||||
|
||||
void cpuid_get(void *id)
|
||||
{
|
||||
memcpy(id, (void *)(0x1fff7a10), CPUID_ID_LEN);
|
||||
}
|
||||
|
||||
/** @} */
|
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level DAC driver implementation
|
||||
*
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "periph/dac.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/* guard in case that no DAC device is defined */
|
||||
#if DAC_NUMOF
|
||||
|
||||
#define DAC_MAX_12BIT 0x0fff
|
||||
|
||||
typedef struct {
|
||||
uint8_t shift_mod;
|
||||
} dac_config_t;
|
||||
|
||||
dac_config_t dac_config[DAC_NUMOF];
|
||||
|
||||
int8_t dac_init(dac_t dev, dac_precision_t precision)
|
||||
{
|
||||
DAC_TypeDef *dac = 0;
|
||||
dac_poweron(dev);
|
||||
|
||||
switch (dev) {
|
||||
#if DAC_0_EN
|
||||
case DAC_0:
|
||||
dac = DAC_0_DEV;
|
||||
DAC_0_PORT_CLKEN();
|
||||
/* Set Mode to analoge out, disable Pullup Pulldown Resistors for both channels */
|
||||
DAC_0_PORT->MODER |= (3 << (DAC_0_CH0_PIN * 2) | 3 << (DAC_0_CH1_PIN * 2));
|
||||
DAC_0_PORT->PUPDR &= ~(3 << (DAC_0_CH0_PIN * 2) | 3 << (DAC_0_CH1_PIN * 2));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Unknown Device */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Select Shift value to normalize given Value */
|
||||
switch(precision) {
|
||||
case DAC_RES_6BIT:
|
||||
dac_config[dev].shift_mod = 0x06; /* 2^6 << 6 = 2^12 */
|
||||
break;
|
||||
case DAC_RES_8BIT:
|
||||
dac_config[dev].shift_mod = 0x04; /* 2^8 << 4 = 2^12 */
|
||||
break;
|
||||
case DAC_RES_10BIT:
|
||||
dac_config[dev].shift_mod = 0x02; /* 2^10 << 2 = 2^12 */
|
||||
break;
|
||||
case DAC_RES_12BIT:
|
||||
dac_config[dev].shift_mod = 0x00; /* 2^12 << 0 = 2^12 */
|
||||
break;
|
||||
/* Not Supported Resolutions */
|
||||
case DAC_RES_14BIT:
|
||||
case DAC_RES_16BIT:
|
||||
default:
|
||||
dac_poweroff(dev);
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable Channels, Clear Output */
|
||||
dac->CR = 0;
|
||||
dac->CR |= (DAC_CR_EN1 | DAC_CR_EN2);
|
||||
dac->DHR12R1 = 0;
|
||||
dac->DHR12R2 = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t dac_write(dac_t dev, uint8_t channel, uint16_t value)
|
||||
{
|
||||
DAC_TypeDef* __attribute__((unused)) dac = 0;
|
||||
uint16_t __attribute__((unused)) val = value << dac_config[dev].shift_mod;
|
||||
|
||||
switch(dev){
|
||||
#if DAC_0_EN
|
||||
case DAC_0:
|
||||
dac = DAC_0_DEV;
|
||||
|
||||
if( DAC_MAX_12BIT < val ){
|
||||
/* Value out of Range */
|
||||
return -3;
|
||||
}
|
||||
|
||||
switch(channel){
|
||||
case 0:
|
||||
dac->DHR12R1 = val;
|
||||
break;
|
||||
case 1:
|
||||
dac->DHR12R2 = val;
|
||||
break;
|
||||
/* Invalid Channel */
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
/* Unknown Device */
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t dac_poweron(dac_t dev)
|
||||
{
|
||||
switch (dev){
|
||||
#if DAC_0_EN
|
||||
case DAC_0:
|
||||
DAC_0_CLKEN();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Unknown Device */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int8_t dac_poweroff(dac_t dev)
|
||||
{
|
||||
switch (dev) {
|
||||
#if DAC_0_EN
|
||||
case DAC_0:
|
||||
DAC_0_CLKDIS();
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Unknown Device */
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t dac_map(dac_t dev, int value, int min, int max)
|
||||
{
|
||||
return dac_mapf(dev, (int) value, (int) min, (int) max);
|
||||
}
|
||||
|
||||
uint16_t dac_mapf(dac_t dev, float value, float min, float max)
|
||||
{
|
||||
uint16_t val_12_bit = ((value - min) * DAC_MAX_12BIT)/(max-min);
|
||||
return val_12_bit >> dac_config[dev].shift_mod;
|
||||
}
|
||||
|
||||
#undef DAC_MAX_12BIT
|
||||
|
||||
#endif /* DAC_NUMOF */
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Engineering-Spirit
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_stm32f2
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level GPIO driver implementation
|
||||
*
|
||||
* @author Hauke Petersen <mail@haukepetersen.de>
|
||||
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
||||
* @author Nick v. IJzendoorn <nijzendoorn@engineering-spirit.nl>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "sched.h"
|
||||
#include "thread.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
/**
|
||||
* @brief Number of available external interrupt lines
|
||||
*/
|
||||
#define GPIO_ISR_CHAN_NUMOF (16U)
|
||||
|
||||
/**
|
||||
* @brief Datastructure to hold an interrupt context
|
||||
*/
|
||||
typedef struct {
|
||||
void (*cb)(void *arg); /**< interrupt callback routine */
|
||||
void *arg; /**< optional argument */
|
||||
} exti_ctx_t;
|
||||
|
||||
/**
|
||||
* @brief Hold one callback function pointer for each interrupt line
|
||||
*/
|
||||
static exti_ctx_t exti_chan[GPIO_ISR_CHAN_NUMOF];
|
||||
|
||||
/**
|
||||
* @brief Extract the port base address from the given pin identifier
|
||||
*/
|
||||
static inline GPIO_TypeDef *_port(gpio_t pin)
|
||||
{
|
||||
return (GPIO_TypeDef *)(pin & ~(0x0f));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract the port number form the given identifier
|
||||
*
|
||||
* The port number is extracted by looking at bits 10, 11, 12, 13 of the base
|
||||
* register addresses.
|
||||
*/
|
||||
static inline int _port_num(gpio_t pin)
|
||||
{
|
||||
return ((pin >> 10) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Extract the pin number from the last 4 bit of the pin identifier
|
||||
*/
|
||||
static inline int _pin_num(gpio_t pin)
|
||||
{
|
||||
return (pin & 0x0f);
|
||||
}
|
||||
|
||||
int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pullup)
|
||||
{
|
||||
GPIO_TypeDef *port = _port(pin);
|
||||
int pin_num = _pin_num(pin);
|
||||
|
||||
/* enable clock */
|
||||
RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOAEN << _port_num(pin));
|
||||
/* configure pull register */
|
||||
port->PUPDR &= ~(3 << (2 * pin_num));
|
||||
port->PUPDR |= (pullup << (2 * pin_num));
|
||||
/* set direction */
|
||||
if (dir == GPIO_DIR_OUT) {
|
||||
port->MODER &= ~(3 << (2 * pin_num)); /* set pin to output mode */
|
||||
port->MODER |= (1 << (2 * pin_num));
|
||||
port->OTYPER &= ~(1 << pin_num); /* set to push-pull */
|
||||
port->OSPEEDR |= (3 << (2 * pin_num)); /* set to high speed */
|
||||
port->ODR &= ~(1 << pin_num); /* set pin to low signal */
|
||||
}
|
||||
else {
|
||||
port->MODER &= ~(3 << (2 * pin_num)); /* configure pin as input */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpio_init_int(gpio_t pin,
|
||||
gpio_pp_t pullup, gpio_flank_t flank,
|
||||
gpio_cb_t cb, void *arg)
|
||||
{
|
||||
int pin_num = _pin_num(pin);
|
||||
int port_num = _port_num(pin);
|
||||
|
||||
/* configure and save exti configuration struct */
|
||||
exti_chan[pin_num].cb = cb;
|
||||
exti_chan[pin_num].arg = arg;
|
||||
/* enable the SYSCFG clock */
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
|
||||
/* initialize pin as input */
|
||||
gpio_init(pin, GPIO_DIR_IN, pullup);
|
||||
/* enable global pin interrupt */
|
||||
if (pin_num < 5) {
|
||||
NVIC_EnableIRQ(EXTI0_IRQn + pin_num);
|
||||
}
|
||||
else if (pin_num < 10) {
|
||||
NVIC_EnableIRQ(EXTI9_5_IRQn);
|
||||
}
|
||||
else {
|
||||
NVIC_EnableIRQ(EXTI15_10_IRQn);
|
||||
}
|
||||
/* configure the active edge(s) */
|
||||
switch (flank) {
|
||||
case GPIO_RISING:
|
||||
EXTI->RTSR |= (1 << pin_num);
|
||||
EXTI->FTSR &= ~(1 << pin_num);
|
||||
break;
|
||||
case GPIO_FALLING:
|
||||
EXTI->RTSR &= ~(1 << pin_num);
|
||||
EXTI->FTSR |= (1 << pin_num);
|
||||
break;
|
||||
case GPIO_BOTH:
|
||||
EXTI->RTSR |= (1 << pin_num);
|
||||
EXTI->FTSR |= (1 << pin_num);
|
||||
break;
|
||||
}
|
||||
/* enable specific pin as exti sources */
|
||||
SYSCFG->EXTICR[pin_num >> 2] &= ~(0xf << ((pin_num & 0x03) * 4));
|
||||
SYSCFG->EXTICR[pin_num >> 2] |= (port_num << ((pin_num & 0x03) * 4));
|
||||
/* clear any pending requests */
|
||||
EXTI->PR = (1 << pin_num);
|
||||
/* enable interrupt for EXTI line */
|
||||
EXTI->IMR |= (1 << pin_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void gpio_init_af(gpio_t pin, gpio_af_t af)
|
||||
{
|
||||
GPIO_TypeDef *port = _port(pin);
|
||||
uint32_t pin_num = _pin_num(pin);
|
||||
|
||||
/* set pin to AF mode */
|
||||
port->MODER &= ~(3 << (2 * pin_num));
|
||||
port->MODER |= (2 << (2 * pin_num));
|
||||
/* set selected function */
|
||||
port->AFR[(pin_num > 7) ? 1 : 0] &= ~(0xf << ((pin_num & 0x07) * 4));
|
||||
port->AFR[(pin_num > 7) ? 1 : 0] |= (af << ((pin_num & 0x07) * 4));
|
||||
}
|
||||
|
||||
void gpio_irq_enable(gpio_t pin)
|
||||
{
|
||||
EXTI->IMR |= (1 << _pin_num(pin));
|
||||
}
|
||||
|
||||
void gpio_irq_disable(gpio_t pin)
|
||||
{
|
||||
EXTI->IMR &= ~(1 << _pin_num(pin));
|
||||
}
|
||||
|
||||
int gpio_read(gpio_t pin)
|
||||
{
|
||||
GPIO_TypeDef *port = _port(pin);
|
||||
uint32_t pin_num = _pin_num(pin);
|
||||
|
||||
if (port->MODER & (3 << (pin_num * 2))) { /* if configured as output */
|
||||
return port->ODR & (1 << pin_num); /* read output data reg */
|
||||
} else {
|
||||
return port->IDR & (1 << pin_num); /* else read input data reg */
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_set(gpio_t pin)
|
||||
{
|
||||
_port(pin)->BSRRL = (1 << _pin_num(pin));
|
||||
}
|
||||
|
||||
void gpio_clear(gpio_t pin)
|
||||
{
|
||||
_port(pin)->BSRRH = (1 << _pin_num(pin));
|
||||
}
|
||||
|
||||
void gpio_toggle(gpio_t pin)
|
||||
{
|
||||
if (gpio_read(pin)) {
|
||||
gpio_clear(pin);
|
||||
} else {
|
||||
gpio_set(pin);
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_write(gpio_t pin, int value)
|
||||
{
|
||||
if (value) {
|
||||
gpio_set(pin);
|
||||
} else {
|
||||
gpio_clear(pin);
|
||||
}
|
||||
}
|
||||
|
||||
void isr_exti(void)
|
||||
{
|
||||
for (unsigned i = 0; i < GPIO_ISR_CHAN_NUMOF; i++) {
|
||||
if (EXTI->PR & (1 << i)) {
|
||||
EXTI->PR = (1 << i); /* clear by writing a 1 */
|
||||
exti_chan[i].cb(exti_chan[i].arg);
|
||||
}
|
||||
}
|
||||
if (sched_context_switch_request) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
@ -0,0 +1,576 @@
|
||||
/*
|
||||
* Copyright (C) 2014 FU Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup driver_periph
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level I2C driver implementation
|
||||
*
|
||||
* @note This implementation only implements the 7-bit addressing mode.
|
||||
*
|
||||
* @author Peter Kietzmann <peter.kietzmann@haw-hamburg.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
* @auhtor Thomas Eichinge <thomas.eichinger@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "irq.h"
|
||||
#include "mutex.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/i2c.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
/* guard file in case no I2C device is defined */
|
||||
#if I2C_NUMOF
|
||||
|
||||
/* static function definitions */
|
||||
static void _i2c_init(I2C_TypeDef *i2c, int ccr);
|
||||
static void _toggle_pins(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda);
|
||||
static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda);
|
||||
static void _start(I2C_TypeDef *dev, uint8_t address, uint8_t rw_flag);
|
||||
static inline void _clear_addr(I2C_TypeDef *dev);
|
||||
static inline void _write(I2C_TypeDef *dev, char *data, int length);
|
||||
static inline void _stop(I2C_TypeDef *dev);
|
||||
|
||||
/**
|
||||
* @brief Array holding one pre-initialized mutex for each I2C device
|
||||
*/
|
||||
static mutex_t locks[] = {
|
||||
#if I2C_0_EN
|
||||
[I2C_0] = MUTEX_INIT,
|
||||
#endif
|
||||
#if I2C_1_EN
|
||||
[I2C_1] = MUTEX_INIT,
|
||||
#endif
|
||||
#if I2C_2_EN
|
||||
[I2C_2] = MUTEX_INIT
|
||||
#endif
|
||||
#if I2C_3_EN
|
||||
[I2C_3] = MUTEX_INIT
|
||||
#endif
|
||||
};
|
||||
|
||||
int i2c_init_master(i2c_t dev, i2c_speed_t speed)
|
||||
{
|
||||
I2C_TypeDef *i2c;
|
||||
GPIO_TypeDef *port_scl;
|
||||
GPIO_TypeDef *port_sda;
|
||||
int pin_scl = 0, pin_sda = 0;
|
||||
int ccr;
|
||||
|
||||
/* read speed configuration */
|
||||
switch (speed) {
|
||||
case I2C_SPEED_NORMAL:
|
||||
ccr = I2C_APBCLK / 200000;
|
||||
break;
|
||||
|
||||
case I2C_SPEED_FAST:
|
||||
ccr = I2C_APBCLK / 800000;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* read static device configuration */
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
port_scl = I2C_0_SCL_PORT;
|
||||
pin_scl = I2C_0_SCL_PIN;
|
||||
port_sda = I2C_0_SDA_PORT;
|
||||
pin_sda = I2C_0_SDA_PIN;
|
||||
I2C_0_CLKEN();
|
||||
I2C_0_SCL_CLKEN();
|
||||
I2C_0_SDA_CLKEN();
|
||||
NVIC_SetPriority(I2C_0_ERR_IRQ, I2C_IRQ_PRIO);
|
||||
NVIC_EnableIRQ(I2C_0_ERR_IRQ);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* configure pins */
|
||||
_pin_config(port_scl, port_sda, pin_scl, pin_sda);
|
||||
|
||||
/* configure device */
|
||||
_i2c_init(i2c, ccr);
|
||||
|
||||
/* make sure the analog filters don't hang -> see errata sheet 2.14.7 */
|
||||
if (i2c->SR2 & I2C_SR2_BUSY) {
|
||||
DEBUG("LINE BUSY AFTER RESET -> toggle pins now\n");
|
||||
/* disable peripheral */
|
||||
i2c->CR1 &= ~I2C_CR1_PE;
|
||||
/* toggle both pins to reset analog filter */
|
||||
_toggle_pins(port_scl, port_sda, pin_scl, pin_sda);
|
||||
/* reset pins for alternate function */
|
||||
_pin_config(port_scl, port_sda, pin_scl, pin_sda);
|
||||
/* make peripheral soft reset */
|
||||
i2c->CR1 |= I2C_CR1_SWRST;
|
||||
i2c->CR1 &= ~I2C_CR1_SWRST;
|
||||
/* enable device */
|
||||
_i2c_init(i2c, ccr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _i2c_init(I2C_TypeDef *i2c, int ccr)
|
||||
{
|
||||
/* disable device and set ACK bit */
|
||||
i2c->CR1 = I2C_CR1_ACK;
|
||||
/* configure I2C clock */
|
||||
i2c->CR2 = (I2C_APBCLK / 1000000) | I2C_CR2_ITERREN;
|
||||
i2c->CCR = ccr;
|
||||
i2c->TRISE = (I2C_APBCLK / 1000000) + 1;
|
||||
/* configure device */
|
||||
i2c->OAR1 = 0; /* makes sure we are in 7-bit address mode */
|
||||
/* enable device */
|
||||
i2c->CR1 |= I2C_CR1_PE;
|
||||
}
|
||||
|
||||
static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda)
|
||||
{
|
||||
/* Set GPIOs to AF mode */
|
||||
port_scl->MODER &= ~(3 << (2 * pin_scl));
|
||||
port_scl->MODER |= (2 << (2 * pin_scl));
|
||||
port_sda->MODER &= ~(3 << (2 * pin_sda));
|
||||
port_sda->MODER |= (2 << (2 * pin_sda));
|
||||
|
||||
/* Set speed high*/
|
||||
port_scl->OSPEEDR |= (3 << (2 * pin_scl));
|
||||
port_sda->OSPEEDR |= (3 << (2 * pin_sda));
|
||||
|
||||
/* Set to push-pull configuration open drain*/
|
||||
port_scl->OTYPER |= (1 << pin_scl);
|
||||
port_sda->OTYPER |= (1 << pin_sda);
|
||||
|
||||
/* Enable pull-up resistors */
|
||||
port_scl->PUPDR &= ~(3 << (2 * pin_scl));
|
||||
port_scl->PUPDR |= (1 << (2 * pin_scl));
|
||||
port_sda->PUPDR &= ~(3 << (2 * pin_sda));
|
||||
port_sda->PUPDR |= (1 << (2 * pin_sda));
|
||||
|
||||
/* Configure GPIOs to for the I2C alternate function */
|
||||
if (pin_scl < 8) {
|
||||
port_scl->AFR[0] &= ~(0xf << (4 * pin_scl));
|
||||
port_scl->AFR[0] |= (I2C_0_SCL_AF << (4 * pin_scl));
|
||||
}
|
||||
else {
|
||||
port_scl->AFR[1] &= ~(0xf << (4 * (pin_scl - 8)));
|
||||
port_scl->AFR[1] |= (I2C_0_SCL_AF << (4 * (pin_scl - 8)));
|
||||
}
|
||||
|
||||
if (pin_sda < 8) {
|
||||
port_sda->AFR[0] &= ~(0xf << (4 * pin_sda));
|
||||
port_sda->AFR[0] |= (I2C_0_SDA_AF << (4 * pin_sda));
|
||||
}
|
||||
else {
|
||||
port_sda->AFR[1] &= ~(0xf << (4 * (pin_sda - 8)));
|
||||
port_sda->AFR[1] |= (I2C_0_SDA_AF << (4 * (pin_sda - 8)));
|
||||
}
|
||||
}
|
||||
|
||||
static void _toggle_pins(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, int pin_scl, int pin_sda)
|
||||
{
|
||||
/* Set GPIOs to General purpose output mode mode */
|
||||
port_scl->MODER &= ~(3 << (2 * pin_scl));
|
||||
port_scl->MODER |= (1 << (2 * pin_scl));
|
||||
port_sda->MODER &= ~(3 << (2 * pin_sda));
|
||||
port_sda->MODER |= (1 << (2 * pin_sda));
|
||||
|
||||
/* Set speed high*/
|
||||
port_scl->OSPEEDR |= (3 << (2 * pin_scl));
|
||||
port_sda->OSPEEDR |= (3 << (2 * pin_sda));
|
||||
|
||||
/* Set to push-pull configuration open drain*/
|
||||
port_scl->OTYPER |= (1 << pin_scl);
|
||||
port_sda->OTYPER |= (1 << pin_sda);
|
||||
|
||||
/* set both to high */
|
||||
port_scl->ODR |= (1 << pin_scl);
|
||||
port_sda->ODR |= (1 << pin_sda);
|
||||
/* set SDA to low */
|
||||
port_sda->ODR &= ~(1 << pin_sda);
|
||||
/* set SCL to low */
|
||||
port_scl->ODR &= ~(1 << pin_scl);
|
||||
/* set SCL to high */
|
||||
port_scl->ODR |= (1 << pin_scl);
|
||||
/* set SDA to high */
|
||||
port_sda->ODR |= (1 << pin_sda);
|
||||
}
|
||||
|
||||
int i2c_acquire(i2c_t dev)
|
||||
{
|
||||
if (dev >= I2C_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
mutex_lock(&locks[dev]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_release(i2c_t dev)
|
||||
{
|
||||
if (dev >= I2C_NUMOF) {
|
||||
return -1;
|
||||
}
|
||||
mutex_unlock(&locks[dev]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int i2c_read_byte(i2c_t dev, uint8_t address, char *data)
|
||||
{
|
||||
return i2c_read_bytes(dev, address, data, 1);
|
||||
}
|
||||
|
||||
int i2c_read_bytes(i2c_t dev, uint8_t address, char *data, int length)
|
||||
{
|
||||
unsigned int state;
|
||||
int i = 0;
|
||||
I2C_TypeDef *i2c;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (length) {
|
||||
case 1:
|
||||
DEBUG("Send Slave address and wait for ADDR == 1\n");
|
||||
_start(i2c, address, I2C_FLAG_READ);
|
||||
|
||||
DEBUG("Set ACK = 0\n");
|
||||
i2c->CR1 &= ~(I2C_CR1_ACK);
|
||||
|
||||
DEBUG("Clear ADDR and set STOP = 1\n");
|
||||
state = disableIRQ();
|
||||
_clear_addr(i2c);
|
||||
i2c->CR1 |= (I2C_CR1_STOP);
|
||||
restoreIRQ(state);
|
||||
|
||||
DEBUG("Wait for RXNE == 1\n");
|
||||
|
||||
while (!(i2c->SR1 & I2C_SR1_RXNE));
|
||||
|
||||
DEBUG("Read received data\n");
|
||||
*data = (char)i2c->DR;
|
||||
|
||||
/* wait until STOP is cleared by hardware */
|
||||
while (i2c->CR1 & I2C_CR1_STOP);
|
||||
|
||||
/* reset ACK to be able to receive new data */
|
||||
i2c->CR1 |= (I2C_CR1_ACK);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
DEBUG("Send Slave address and wait for ADDR == 1\n");
|
||||
_start(i2c, address, I2C_FLAG_READ);
|
||||
DEBUG("Set POS bit\n");
|
||||
i2c->CR1 |= (I2C_CR1_POS | I2C_CR1_ACK);
|
||||
DEBUG("Crit block: Clear ADDR bit and clear ACK flag\n");
|
||||
state = disableIRQ();
|
||||
_clear_addr(i2c);
|
||||
i2c->CR1 &= ~(I2C_CR1_ACK);
|
||||
restoreIRQ(state);
|
||||
|
||||
DEBUG("Wait for transfer to be completed\n");
|
||||
|
||||
while (!(i2c->SR1 & I2C_SR1_BTF));
|
||||
|
||||
DEBUG("Crit block: set STOP and read first byte\n");
|
||||
state = disableIRQ();
|
||||
i2c->CR1 |= (I2C_CR1_STOP);
|
||||
data[0] = (char)i2c->DR;
|
||||
restoreIRQ(state);
|
||||
|
||||
DEBUG("read second byte\n");
|
||||
data[1] = (char)i2c->DR;
|
||||
|
||||
DEBUG("wait for STOP bit to be cleared again\n");
|
||||
|
||||
while (i2c->CR1 & I2C_CR1_STOP);
|
||||
|
||||
DEBUG("reset POS = 0 and ACK = 1\n");
|
||||
i2c->CR1 &= ~(I2C_CR1_POS);
|
||||
i2c->CR1 |= (I2C_CR1_ACK);
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG("Send Slave address and wait for ADDR == 1\n");
|
||||
_start(i2c, address, I2C_FLAG_READ);
|
||||
_clear_addr(i2c);
|
||||
|
||||
while (i < (length - 3)) {
|
||||
DEBUG("Wait until byte was received\n");
|
||||
|
||||
while (!(i2c->SR1 & I2C_SR1_RXNE));
|
||||
|
||||
DEBUG("Copy byte from DR\n");
|
||||
data[i++] = (char)i2c->DR;
|
||||
}
|
||||
|
||||
DEBUG("Reading the last 3 bytes, waiting for BTF flag\n");
|
||||
|
||||
while (!(i2c->SR1 & I2C_SR1_BTF));
|
||||
|
||||
DEBUG("Disable ACK\n");
|
||||
i2c->CR1 &= ~(I2C_CR1_ACK);
|
||||
|
||||
DEBUG("Crit block: set STOP and read N-2 byte\n");
|
||||
state = disableIRQ();
|
||||
data[i++] = (char)i2c->DR;
|
||||
i2c->CR1 |= (I2C_CR1_STOP);
|
||||
restoreIRQ(state);
|
||||
|
||||
DEBUG("Read N-1 byte\n");
|
||||
data[i++] = (char)i2c->DR;
|
||||
|
||||
while (!(i2c->SR1 & I2C_SR1_RXNE));
|
||||
|
||||
DEBUG("Read last byte\n");
|
||||
|
||||
data[i++] = (char)i2c->DR;
|
||||
|
||||
DEBUG("wait for STOP bit to be cleared again\n");
|
||||
|
||||
while (i2c->CR1 & I2C_CR1_STOP);
|
||||
|
||||
DEBUG("reset POS = 0 and ACK = 1\n");
|
||||
i2c->CR1 &= ~(I2C_CR1_POS);
|
||||
i2c->CR1 |= (I2C_CR1_ACK);
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, char *data)
|
||||
{
|
||||
return i2c_read_regs(dev, address, reg, data, 1);
|
||||
}
|
||||
|
||||
int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, char *data, int length)
|
||||
{
|
||||
I2C_TypeDef *i2c;
|
||||
|
||||
switch (dev) {
|
||||
#if I2C_0_EN
|
||||
case I2C_0:
|
||||
i2c = I2C_0_DEV;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* send start condition and slave address */
|
||||
DEBUG("Send slave address and clear ADDR flag\n");
|
||||
_start(i2c, address, I2C_FLAG_WRITE);
|
||||
_clear_addr(i2c);
|
||||
DEBUG("Write reg into DR\n");
|
||||
i2c->DR = reg;
|
||||
_stop(i2c);
|
||||
DEBUG("Now start a read transaction\n");
|
||||
return i2c_read_bytes(dev, address, data, length);
|
||||
}
|
||||
|
||||
int i2c_write_byte(i2c_t dev, uint8_t address, char data)
|
||||
{
|
||||
return i2c_write_bytes(dev, address, &data, 1);
|
||||