1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-01 09:24:10 +01:00

OP-1275 copy relevant F1 PiOS drivers (yet unmodified)

This commit is contained in:
Alessio Morale 2014-03-30 12:19:11 +02:00
parent 7eca9f80e3
commit 5a60c254ed
18 changed files with 5454 additions and 0 deletions

View File

@ -0,0 +1,391 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_ADC ADC Functions
* @brief STM32 ADC PIOS interface
* @{
*
* @file pios_adc.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief Analog to Digital converstion routines
* @see The GNU Public License (GPL) Version 3
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_ADC
#include <pios_adc_priv.h>
// Private types
enum pios_adc_dev_magic {
PIOS_ADC_DEV_MAGIC = 0x58375124,
};
struct pios_adc_dev {
const struct pios_adc_cfg *cfg;
ADCCallback callback_function;
#if defined(PIOS_INCLUDE_FREERTOS)
xQueueHandle data_queue;
#endif
volatile int16_t *valid_data_buffer;
volatile uint8_t adc_oversample;
uint8_t dma_block_size;
uint16_t dma_half_buffer_size;
#if defined(PIOS_INCLUDE_ADC)
int16_t fir_coeffs[PIOS_ADC_MAX_SAMPLES + 1] __attribute__((aligned(4)));
volatile int16_t raw_data_buffer[PIOS_ADC_MAX_SAMPLES] __attribute__((aligned(4))); // Double buffer that DMA just used
float downsampled_buffer[PIOS_ADC_NUM_CHANNELS] __attribute__((aligned(4)));
#endif
enum pios_adc_dev_magic magic;
};
float PIOS_ADC_PinGetVolt(uint32_t pin)
{
return ((float)PIOS_ADC_PinGet(pin)) * PIOS_ADC_VOLTAGE_SCALE;
}
#if defined(PIOS_INCLUDE_FREERTOS)
struct pios_adc_dev *pios_adc_dev;
#endif
// Private functions
void PIOS_ADC_downsample_data();
static struct pios_adc_dev *PIOS_ADC_Allocate();
static bool PIOS_ADC_validate(struct pios_adc_dev *);
/* Local Variables */
static GPIO_TypeDef *ADC_GPIO_PORT[PIOS_ADC_NUM_PINS] = PIOS_ADC_PORTS;
static const uint32_t ADC_GPIO_PIN[PIOS_ADC_NUM_PINS] = PIOS_ADC_PINS;
static const uint32_t ADC_CHANNEL[PIOS_ADC_NUM_PINS] = PIOS_ADC_CHANNELS;
static ADC_TypeDef *ADC_MAPPING[PIOS_ADC_NUM_PINS] = PIOS_ADC_MAPPING;
static const uint32_t ADC_CHANNEL_MAPPING[PIOS_ADC_NUM_PINS] = PIOS_ADC_CHANNEL_MAPPING;
static bool PIOS_ADC_validate(struct pios_adc_dev *dev)
{
if (dev == NULL) {
return false;
}
return dev->magic == PIOS_ADC_DEV_MAGIC;
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_adc_dev *PIOS_ADC_Allocate()
{
struct pios_adc_dev *adc_dev;
adc_dev = (struct pios_adc_dev *)pvPortMalloc(sizeof(*adc_dev));
if (!adc_dev) {
return NULL;
}
adc_dev->magic = PIOS_ADC_DEV_MAGIC;
return adc_dev;
}
#else
#error Not implemented
#endif
/**
* @brief Initialise the ADC Peripheral, configure to run at the max oversampling
*/
int32_t PIOS_ADC_Init(const struct pios_adc_cfg *cfg)
{
pios_adc_dev = PIOS_ADC_Allocate();
if (pios_adc_dev == NULL) {
return -1;
}
pios_adc_dev->cfg = cfg;
pios_adc_dev->callback_function = NULL;
#if defined(PIOS_INCLUDE_FREERTOS)
pios_adc_dev->data_queue = NULL;
#endif
/* Setup analog pins */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
/* Enable each ADC pin in the array */
for (int32_t i = 0; i < PIOS_ADC_NUM_PINS; i++) {
GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN[i];
GPIO_Init(ADC_GPIO_PORT[i], &GPIO_InitStructure);
}
PIOS_ADC_Config(PIOS_ADC_MAX_OVERSAMPLING);
return 0;
}
/**
* @brief Configure the ADC to run at a fixed oversampling
* @param[in] oversampling the amount of oversampling to run at
*/
void PIOS_ADC_Config(uint32_t oversampling)
{
pios_adc_dev->adc_oversample = (oversampling > PIOS_ADC_MAX_OVERSAMPLING) ? PIOS_ADC_MAX_OVERSAMPLING : oversampling;
ADC_DeInit(ADC1);
ADC_DeInit(ADC2);
/* Disable interrupts */
DMA_ITConfig(pios_adc_dev->cfg->dma.rx.channel, pios_adc_dev->cfg->dma.irq.flags, DISABLE);
/* Enable ADC clocks */
PIOS_ADC_CLOCK_FUNCTION;
/* Map channels to conversion slots depending on the channel selection mask */
for (int32_t i = 0; i < PIOS_ADC_NUM_PINS; i++) {
ADC_RegularChannelConfig(ADC_MAPPING[i], ADC_CHANNEL[i],
ADC_CHANNEL_MAPPING[i],
PIOS_ADC_SAMPLE_TIME);
}
#if (PIOS_ADC_USE_TEMP_SENSOR)
ADC_TempSensorVrefintCmd(ENABLE);
ADC_RegularChannelConfig(PIOS_ADC_TEMP_SENSOR_ADC, ADC_Channel_16,
PIOS_ADC_TEMP_SENSOR_ADC_CHANNEL,
PIOS_ADC_SAMPLE_TIME);
#endif
// return
/* Configure ADCs */
ADC_InitTypeDef ADC_InitStructure;
ADC_StructInit(&ADC_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_RegSimult;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = ((PIOS_ADC_NUM_CHANNELS + 1) >> 1);
ADC_Init(ADC1, &ADC_InitStructure);
#if (PIOS_ADC_USE_ADC2)
ADC_Init(ADC2, &ADC_InitStructure);
/* Enable ADC2 external trigger conversion (to synch with ADC1) */
ADC_ExternalTrigConvCmd(ADC2, ENABLE);
#endif
RCC_ADCCLKConfig(PIOS_ADC_ADCCLK);
/* Enable ADC1->DMA request */
ADC_DMACmd(ADC1, ENABLE);
/* ADC1 calibration */
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1)) {
;
}
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1)) {
;
}
#if (PIOS_ADC_USE_ADC2)
/* ADC2 calibration */
ADC_Cmd(ADC2, ENABLE);
ADC_ResetCalibration(ADC2);
while (ADC_GetResetCalibrationStatus(ADC2)) {
;
}
ADC_StartCalibration(ADC2);
while (ADC_GetCalibrationStatus(ADC2)) {
;
}
#endif
/* This makes sure we have an even number of transfers if using ADC2 */
pios_adc_dev->dma_block_size = ((PIOS_ADC_NUM_CHANNELS + PIOS_ADC_USE_ADC2) >> PIOS_ADC_USE_ADC2) << PIOS_ADC_USE_ADC2;
pios_adc_dev->dma_half_buffer_size = pios_adc_dev->dma_block_size * pios_adc_dev->adc_oversample;
/* Configure DMA channel */
DMA_InitTypeDef dma_init = pios_adc_dev->cfg->dma.rx.init;
dma_init.DMA_MemoryBaseAddr = (uint32_t)&pios_adc_dev->raw_data_buffer[0];
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_BufferSize = pios_adc_dev->dma_half_buffer_size; /* x2 for double buffer /2 for 32-bit xfr */
DMA_Init(pios_adc_dev->cfg->dma.rx.channel, &dma_init);
DMA_Cmd(pios_adc_dev->cfg->dma.rx.channel, ENABLE);
/* Trigger interrupt when for half conversions too to indicate double buffer */
DMA_ITConfig(pios_adc_dev->cfg->dma.rx.channel, DMA_IT_TC, ENABLE);
DMA_ITConfig(pios_adc_dev->cfg->dma.rx.channel, DMA_IT_HT, ENABLE);
/* Configure DMA interrupt */
NVIC_Init(&pios_adc_dev->cfg->dma.irq.init);
/* Finally start initial conversion */
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
/* Use simple averaging filter for now */
for (int32_t i = 0; i < pios_adc_dev->adc_oversample; i++) {
pios_adc_dev->fir_coeffs[i] = 1;
}
pios_adc_dev->fir_coeffs[pios_adc_dev->adc_oversample] = pios_adc_dev->adc_oversample;
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(pios_adc_dev->cfg->dma.ahb_clk, ENABLE);
}
/**
* Returns value of an ADC Pin
* \param[in] pin number
* \return ADC pin value - resolution depends on the selected oversampling rate
* \return -1 if pin doesn't exist
*/
int32_t PIOS_ADC_PinGet(uint32_t pin)
{
/* Check if pin exists */
if (pin >= PIOS_ADC_NUM_CHANNELS) {
return -1;
}
/* Return last conversion result */
return pios_adc_dev->downsampled_buffer[pin];
}
/**
* @brief Set a callback function that is executed whenever
* the ADC double buffer swaps
*/
void PIOS_ADC_SetCallback(ADCCallback new_function)
{
pios_adc_dev->callback_function = new_function;
}
#if defined(PIOS_INCLUDE_FREERTOS)
/**
* @brief Register a queue to add data to when downsampled
*/
void PIOS_ADC_SetQueue(xQueueHandle data_queue)
{
pios_adc_dev->data_queue = data_queue;
}
#endif
/**
* @brief Return the address of the downsampled data buffer
*/
float *PIOS_ADC_GetBuffer(void)
{
return pios_adc_dev->downsampled_buffer;
}
/**
* @brief Return the address of the raw data data buffer
*/
int16_t *PIOS_ADC_GetRawBuffer(void)
{
return (int16_t *)pios_adc_dev->valid_data_buffer;
}
/**
* @brief Return the amount of over sampling
*/
uint8_t PIOS_ADC_GetOverSampling(void)
{
return pios_adc_dev->adc_oversample;
}
/**
* @brief Set the fir coefficients. Takes as many samples as the
* current filter order plus one (normalization)
*
* @param new_filter Array of adc_oversampling floats plus one for the
* filter coefficients
*/
void PIOS_ADC_SetFIRCoefficients(float *new_filter)
{
// Less than or equal to get normalization constant
for (int i = 0; i <= pios_adc_dev->adc_oversample; i++) {
pios_adc_dev->fir_coeffs[i] = new_filter[i];
}
}
/**
* @brief Downsample the data for each of the channels then call
* callback function if installed
*/
void PIOS_ADC_downsample_data()
{
uint16_t chan;
uint16_t sample;
float *downsampled_buffer = &pios_adc_dev->downsampled_buffer[0];
for (chan = 0; chan < PIOS_ADC_NUM_CHANNELS; chan++) {
int32_t sum = 0;
for (sample = 0; sample < pios_adc_dev->adc_oversample; sample++) {
sum += pios_adc_dev->valid_data_buffer[chan + sample * pios_adc_dev->dma_block_size] * pios_adc_dev->fir_coeffs[sample];
}
downsampled_buffer[chan] = (float)sum / pios_adc_dev->fir_coeffs[pios_adc_dev->adc_oversample];
}
#if defined(PIOS_INCLUDE_FREERTOS)
if (pios_adc_dev->data_queue) {
static portBASE_TYPE xHigherPriorityTaskWoken;
xQueueSendFromISR(pios_adc_dev->data_queue, pios_adc_dev->downsampled_buffer, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
#endif
if (pios_adc_dev->callback_function) {
pios_adc_dev->callback_function(pios_adc_dev->downsampled_buffer);
}
}
/**
* @brief Interrupt for half and full buffer transfer
*
* This interrupt handler swaps between the two halfs of the double buffer to make
* sure the ahrs uses the most recent data. Only swaps data when AHRS is idle, but
* really this is a pretense of a sanity check since the DMA engine is consantly
* running in the background. Keep an eye on the ekf_too_slow variable to make sure
* it's keeping up.
*/
void PIOS_ADC_DMA_Handler(void)
{
if (!PIOS_ADC_validate(pios_adc_dev)) {
return;
}
if (DMA_GetFlagStatus(pios_adc_dev->cfg->full_flag /*DMA1_IT_TC1*/)) { // whole double buffer filled
pios_adc_dev->valid_data_buffer = &pios_adc_dev->raw_data_buffer[pios_adc_dev->dma_half_buffer_size];
DMA_ClearFlag(pios_adc_dev->cfg->full_flag);
PIOS_ADC_downsample_data();
} else if (DMA_GetFlagStatus(pios_adc_dev->cfg->half_flag /*DMA1_IT_HT1*/)) {
pios_adc_dev->valid_data_buffer = &pios_adc_dev->raw_data_buffer[0];
DMA_ClearFlag(pios_adc_dev->cfg->half_flag);
PIOS_ADC_downsample_data();
} else {
// This should not happen, probably due to transfer errors
DMA_ClearFlag(pios_adc_dev->cfg->dma.irq.flags /*DMA1_FLAG_GL1*/);
}
}
#endif /* PIOS_INCLUDE_ADC */
/**
* @}
* @}
*/

View File

@ -0,0 +1,102 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_BKP Backup SRAM functions
* @brief Hardware abstraction layer for backup sram
* @{
*
* @file pios_bkp.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
* @brief IAP functions
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pios.h>
#include <pios_bkp.h>
#include <stm32f0xx.h>
#include <stm32f0xx_rtc.h>
#include <stm32f0xx_pwr.h>
/****************************************************************************************
* Header files
****************************************************************************************/
/*****************************************************************************************
* Public Definitions/Macros
****************************************************************************************/
/****************************************************************************************
* Public Functions
****************************************************************************************/
const uint32_t pios_bkp_registers_map[] = {
RTC_BKP_DR0,
RTC_BKP_DR1,
RTC_BKP_DR2,
RTC_BKP_DR3,
RTC_BKP_DR4
};
#define PIOS_BKP_REGISTERS_COUNT NELEMENTS(pios_bkp_registers_map)
void PIOS_BKP_Init(void)
{
/* Enable CRC clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
/* Enable PWR and BKP clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
/* Clear Tamper pin Event(TE) pending flag */
BKP_ClearFlag();
}
uint16_t PIOS_BKP_ReadRegister(uint32_t regnumber)
{
if (PIOS_BKP_REGISTERS_COUNT < regnumber) {
PIOS_Assert(0);
} else {
return (uint16_t)BKP_ReadBackupRegister(pios_bkp_registers_map[regnumber]);
}
}
void PIOS_BKP_WriteRegister(uint32_t regnumber, uint16_t data)
{
if (PIOS_BKP_REGISTERS_COUNT < regnumber) {
PIOS_Assert(0);
} else {
BKP_WriteBackupRegister(pios_bkp_registers_map[regnumber], (uint32_t)data);
}
}
void PIOS_BKP_EnableWrite(void)
{
/* Enable write access to Backup domain */
PWR_BackupAccessCmd(ENABLE);
}
void PIOS_BKP_DisableWrite(void)
{
/* Enable write access to Backup domain */
PWR_BackupAccessCmd(DISABLE);
}
/****************************************************************************************
* Public Data
****************************************************************************************/

View File

@ -0,0 +1,131 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_BOOTLOADER Functions
* @brief HAL code to interface to the OpenPilot AHRS module
* @{
*
* @file pios_bl_helper.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief Bootloader Helper Functions
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pios.h>
#ifdef PIOS_INCLUDE_BL_HELPER
#include <pios_board_info.h>
#include <stm32f10x_flash.h>
#include <stdbool.h>
uint8_t *PIOS_BL_HELPER_FLASH_If_Read(uint32_t SectorAddress)
{
return (uint8_t *)(SectorAddress);
}
#if defined(PIOS_INCLUDE_BL_HELPER_WRITE_SUPPORT)
static bool erase_flash(uint32_t startAddress, uint32_t endAddress);
uint8_t PIOS_BL_HELPER_FLASH_Ini()
{
FLASH_Unlock();
return 1;
}
uint8_t PIOS_BL_HELPER_FLASH_Start()
{
const struct pios_board_info *bdinfo = &pios_board_info_blob;
uint32_t startAddress = bdinfo->fw_base;
uint32_t endAddress = bdinfo->fw_base + bdinfo->fw_size + bdinfo->desc_size;
bool success = erase_flash(startAddress, endAddress);
return (success) ? 1 : 0;
}
uint8_t PIOS_BL_HELPER_FLASH_Erase_Bootloader()
{
/// Bootloader memory space erase
uint32_t startAddress = BL_BANK_BASE;
uint32_t endAddress = BL_BANK_BASE + BL_BANK_SIZE;
bool success = erase_flash(startAddress, endAddress);
return (success) ? 1 : 0;
}
static bool erase_flash(uint32_t startAddress, uint32_t endAddress)
{
uint32_t pageAddress = startAddress;
uint8_t fail = false;
while ((pageAddress < endAddress) && (fail == false)) {
for (int retry = 0; retry < MAX_DEL_RETRYS; ++retry) {
if (FLASH_ErasePage(pageAddress) == FLASH_COMPLETE) {
fail = false;
break;
} else {
fail = true;
}
}
#ifdef STM32F10X_HD
pageAddress += 2048;
#elif defined(STM32F10X_MD)
pageAddress += 1024;
#endif
}
return !fail;
}
#endif /* if defined(PIOS_INCLUDE_BL_HELPER_WRITE_SUPPORT) */
uint32_t PIOS_BL_HELPER_CRC_Memory_Calc()
{
const struct pios_board_info *bdinfo = &pios_board_info_blob;
PIOS_BL_HELPER_CRC_Ini();
CRC_ResetDR();
CRC_CalcBlockCRC((uint32_t *)bdinfo->fw_base, (bdinfo->fw_size) >> 2);
return CRC_GetCRC();
}
void PIOS_BL_HELPER_FLASH_Read_Description(uint8_t *array, uint8_t size)
{
const struct pios_board_info *bdinfo = &pios_board_info_blob;
uint8_t x = 0;
if (size > bdinfo->desc_size) {
size = bdinfo->desc_size;
}
for (uint32_t i = bdinfo->fw_base + bdinfo->fw_size; i < bdinfo->fw_base + bdinfo->fw_size + size; ++i) {
array[x] = *PIOS_BL_HELPER_FLASH_If_Read(i);
++x;
}
}
void PIOS_BL_HELPER_CRC_Ini()
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);
}
#endif /* PIOS_INCLUDE_BL_HELPER */

View File

@ -0,0 +1,171 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @defgroup PIOS_DEBUG Debugging Functions
* @brief Debugging functionality
* @{
*
* @file pios_debug.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
* @brief Debugging Functions
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
// Global variables
const char *PIOS_DEBUG_AssertMsg = "ASSERT FAILED";
#ifdef PIOS_ENABLE_DEBUG_PINS
static const struct pios_tim_channel *debug_channels;
static uint8_t debug_num_channels;
#endif /* PIOS_ENABLE_DEBUG_PINS */
/**
* Initialise Debug-features
*/
void PIOS_DEBUG_Init(__attribute__((unused)) const struct pios_tim_channel *channels,
__attribute__((unused)) uint8_t num_channels)
{
#ifdef PIOS_ENABLE_DEBUG_PINS
PIOS_Assert(channels);
PIOS_Assert(num_channels);
/* Store away the GPIOs we've been given */
debug_channels = channels;
debug_num_channels = num_channels;
/* Configure the GPIOs we've been given */
for (uint8_t i = 0; i < num_channels; i++) {
const struct pios_tim_channel *chan = &channels[i];
// Initialise pins as standard output pins
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = chan->pin.init.GPIO_Pin;
/* Initialize the GPIO */
GPIO_Init(chan->pin.gpio, &GPIO_InitStructure);
/* Set the pin low */
GPIO_WriteBit(chan->pin.gpio, chan->pin.init.GPIO_Pin, Bit_RESET);
}
#endif // PIOS_ENABLE_DEBUG_PINS
}
/**
* Set debug-pin high
* \param pin 0 for S1 output
*/
void PIOS_DEBUG_PinHigh(__attribute__((unused)) uint8_t pin)
{
#ifdef PIOS_ENABLE_DEBUG_PINS
if (!debug_channels || pin >= debug_num_channels) {
return;
}
const struct pios_tim_channel *chan = &debug_channels[pin];
GPIO_WriteBit(chan->pin.gpio, chan->pin.init.GPIO_Pin, Bit_SET);
#endif // PIOS_ENABLE_DEBUG_PINS
}
/**
* Set debug-pin low
* \param pin 0 for S1 output
*/
void PIOS_DEBUG_PinLow(__attribute__((unused)) uint8_t pin)
{
#ifdef PIOS_ENABLE_DEBUG_PINS
if (!debug_channels || pin >= debug_num_channels) {
return;
}
const struct pios_tim_channel *chan = &debug_channels[pin];
GPIO_WriteBit(chan->pin.gpio, chan->pin.init.GPIO_Pin, Bit_RESET);
#endif // PIOS_ENABLE_DEBUG_PINS
}
void PIOS_DEBUG_PinValue8Bit(__attribute__((unused)) uint8_t value)
{
#ifdef PIOS_ENABLE_DEBUG_PINS
if (!debug_channels) {
return;
}
uint32_t bsrr_l = (((~value) & 0x0F) << (16 + 6)) | ((value & 0x0F) << 6);
uint32_t bsrr_h = (((~value) & 0xF0) << (16 + 6 - 4)) | ((value & 0xF0) << (6 - 4));
PIOS_IRQ_Disable();
/*
* This is sketchy since it assumes a particular ordering
* and bitwise layout of the channels provided to the debug code.
*/
debug_channels[0].pin.gpio->BSRR = bsrr_l;
debug_channels[4].pin.gpio->BSRR = bsrr_h;
PIOS_IRQ_Enable();
#endif // PIOS_ENABLE_DEBUG_PINS
}
void PIOS_DEBUG_PinValue4BitL(__attribute__((unused)) uint8_t value)
{
#ifdef PIOS_ENABLE_DEBUG_PINS
if (!debug_channels) {
return;
}
/*
* This is sketchy since it assumes a particular ordering
* and bitwise layout of the channels provided to the debug code.
*/
uint32_t bsrr_l = ((~(value & 0x0F) << (16 + 6))) | ((value & 0x0F) << 6);
debug_channels[0].pin.gpio->BSRR = bsrr_l;
#endif // PIOS_ENABLE_DEBUG_PINS
}
/**
* Report a serious error and halt
*/
void PIOS_DEBUG_Panic(__attribute__((unused)) const char *msg)
{
#ifdef PIOS_INCLUDE_DEBUG_CONSOLE
register int *lr asm ("lr"); // Link-register holds the PC of the caller
DEBUG_PRINTF(0, "\r%s @0x%x\r", msg, lr);
#endif
// Stay put
while (1) {
;
}
}
/**
* @}
* @}
*/

View File

@ -0,0 +1,176 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_DELAY Delay Functions
* @brief PiOS Delay functionality
* @{
*
* @file pios_delay.c
* @author Michael Smith Copyright (C) 2011
* @brief Delay Functions
* - Provides a micro-second granular delay using the CPU
* cycle counter.
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pios.h>
#ifdef PIOS_INCLUDE_DELAY
/* these should be defined by CMSIS, but they aren't */
#define DWT_CTRL (*(volatile uint32_t *)0xe0001000)
#define CYCCNTENA (1 << 0)
#define DWT_CYCCNT (*(volatile uint32_t *)0xe0001004)
/* cycles per microsecond */
static uint32_t us_ticks;
/**
* Initialises the Timer used by PIOS_DELAY functions.
*
* \return always zero (success)
*/
int32_t PIOS_DELAY_Init(void)
{
RCC_ClocksTypeDef clocks;
/* compute the number of system clocks per microsecond */
RCC_GetClocksFreq(&clocks);
us_ticks = clocks.SYSCLK_Frequency / 1000000;
PIOS_DEBUG_Assert(us_ticks > 1);
/* turn on access to the DWT registers */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
/* enable the CPU cycle counter */
DWT_CTRL |= CYCCNTENA;
return 0;
}
/**
* Waits for a specific number of uS
*
* Example:<BR>
* \code
* // Wait for 500 uS
* PIOS_DELAY_Wait_uS(500);
* \endcode
* \param[in] uS delay
* \return < 0 on errors
*/
int32_t PIOS_DELAY_WaituS(uint32_t uS)
{
uint32_t elapsed = 0;
uint32_t last_count = DWT_CYCCNT;
for (;;) {
uint32_t current_count = DWT_CYCCNT;
uint32_t elapsed_uS;
/* measure the time elapsed since the last time we checked */
elapsed += current_count - last_count;
last_count = current_count;
/* convert to microseconds */
elapsed_uS = elapsed / us_ticks;
if (elapsed_uS >= uS) {
break;
}
/* reduce the delay by the elapsed time */
uS -= elapsed_uS;
/* keep fractional microseconds for the next iteration */
elapsed %= us_ticks;
}
/* No error */
return 0;
}
/**
* Waits for a specific number of mS
*
* Example:<BR>
* \code
* // Wait for 500 mS
* PIOS_DELAY_Wait_mS(500);
* \endcode
* \param[in] mS delay
* \return < 0 on errors
*/
int32_t PIOS_DELAY_WaitmS(uint32_t mS)
{
while (mS--) {
PIOS_DELAY_WaituS(1000);
}
/* No error */
return 0;
}
/**
* @brief Query the Delay timer for the current uS
* @return A microsecond value
*/
uint32_t PIOS_DELAY_GetuS(void)
{
return DWT_CYCCNT / us_ticks;
}
/**
* @brief Calculate time in microseconds since a previous time
* @param[in] t previous time
* @return time in us since previous time t.
*/
uint32_t PIOS_DELAY_GetuSSince(uint32_t t)
{
return PIOS_DELAY_GetuS() - t;
}
/**
* @brief Get the raw delay timer, useful for timing
* @return Unitless value (uint32 wrap around)
*/
uint32_t PIOS_DELAY_GetRaw()
{
return DWT_CYCCNT;
}
/**
* @brief Compare to raw times to and convert to us
* @return A microsecond value
*/
uint32_t PIOS_DELAY_DiffuS(uint32_t raw)
{
uint32_t diff = DWT_CYCCNT - raw;
return diff / us_ticks;
}
#endif /* PIOS_INCLUDE_DELAY */
/**
* @}
* @}
*/

View File

@ -0,0 +1,164 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_EEPROM EEPROM reading/writing functions
* @brief PIOS EEPROM reading/writing functions
* @{
*
* @file pios_eeprom.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief COM layer functions
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pios.h>
#ifdef PIOS_INCLUDE_FLASH_EEPROM
#include <stm32f10x_flash.h>
#include <pios_board_info.h>
static struct pios_eeprom_cfg config;
/**
* Initialize the flash eeprom device
* \param cfg The configuration structure.
*/
void PIOS_EEPROM_Init(const struct pios_eeprom_cfg *cfg)
{
config = *cfg;
}
/**
* Save a block of data to the flash eeprom device.
* \param data A pointer to the data to write.
* \param len The length of data to write.
* \return 0 on sucess
*/
int32_t PIOS_EEPROM_Save(uint8_t *data, uint32_t len)
{
// We need to write 32 bit words, so extend the length to be an even multiple of 4 bytes,
// and include 4 bytes for the 32 bit CRC.
uint32_t nwords = (len / 4) + 1 + (len % 4 ? 1 : 0);
uint32_t size = nwords * 4;
// Ensure that the length is not longer than the max size.
if (size > config.max_size) {
return -1;
}
// Calculate a 32 bit CRC of the data.
uint32_t crc = PIOS_CRC32_updateCRC(0xffffffff, data, len);
// Unlock the Flash Program Erase controller
FLASH_Unlock();
// See if we have to write the data.
if ((memcmp(data, (uint8_t *)config.base_address, len) == 0) &&
(memcmp((uint8_t *)&crc, (uint8_t *)config.base_address + size - 4, 4) == 0)) {
return 0;
}
// TODO: Check that the area isn't already erased
// Erase page
FLASH_Status fs = FLASH_ErasePage(config.base_address);
if (fs != FLASH_COMPLETE) { // error
FLASH_Lock();
return -2;
}
// write 4 bytes at a time into program flash area (emulated EEPROM area)
uint8_t *p1 = data;
uint32_t *p3 = (uint32_t *)config.base_address;
for (uint32_t i = 0; i < size; p3++) {
uint32_t value = 0;
if (i == (size - 4)) {
// write the CRC.
value = crc;
i += 4;
} else {
if (i < len) {
value |= (uint32_t)*p1++ << 0;
} else { value |= 0x000000ff; }
i++;
if (i < len) {
value |= (uint32_t)*p1++ << 8;
} else { value |= 0x0000ff00; }
i++;
if (i < len) {
value |= (uint32_t)*p1++ << 16;
} else { value |= 0x00ff0000; }
i++;
if (i < len) {
value |= (uint32_t)*p1++ << 24;
} else { value |= 0xff000000; }
i++;
}
// write a 32-bit value
fs = FLASH_ProgramWord((uint32_t)p3, value);
if (fs != FLASH_COMPLETE) {
FLASH_Lock();
return -3;
}
}
// Lock the Flash Program Erase controller
FLASH_Lock();
return 0;
}
/**
* Reads a block of data from the flash eeprom device.
* \param data A pointer to the output data buffer.
* \param len The length of data to read.
* \return 0 on sucess
*/
int32_t PIOS_EEPROM_Load(uint8_t *data, uint32_t len)
{
// We need to write 32 bit words, so the length should have been extended
// to an even multiple of 4 bytes, and should include 4 bytes for the 32 bit CRC.
uint32_t nwords = (len / 4) + 1 + (len % 4 ? 1 : 0);
uint32_t size = nwords * 4;
// Ensure that the length is not longer than the max size.
if (size > config.max_size) {
return -1;
}
// Read the data from flash.
memcpy(data, (uint8_t *)config.base_address, len);
// Read the CRC.
uint32_t crc_flash = *((uint32_t *)(config.base_address + size - 4));
// Calculate a 32 bit CRC of the data.
uint32_t crc = PIOS_CRC32_updateCRC(0xffffffff, data, len);
if (crc != crc_flash) {
return -2;
}
return 0;
}
#endif /* PIOS_INCLUDE_FLASH_EEPROM */

View File

@ -0,0 +1,331 @@
/**
******************************************************************************
*
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_EXTI External Interrupt Handlers
* @brief External interrupt handler functions
* @{
*
* @file pios_exti.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief External Interrupt Handlers
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_EXTI
/* Map EXTI line to full config */
#define EXTI_MAX_LINES 16
#define PIOS_EXTI_INVALID 0xFF
static uint8_t pios_exti_line_to_cfg_map[EXTI_MAX_LINES] = {
[0 ... EXTI_MAX_LINES - 1] = PIOS_EXTI_INVALID,
};
/* Table of exti configs registered at compile time */
extern struct pios_exti_cfg __start__exti __attribute__((weak));
extern struct pios_exti_cfg __stop__exti __attribute__((weak));
static uint8_t PIOS_EXTI_line_to_index(uint32_t line)
{
switch (line) {
case EXTI_Line0: return 0;
case EXTI_Line1: return 1;
case EXTI_Line2: return 2;
case EXTI_Line3: return 3;
case EXTI_Line4: return 4;
case EXTI_Line5: return 5;
case EXTI_Line6: return 6;
case EXTI_Line7: return 7;
case EXTI_Line8: return 8;
case EXTI_Line9: return 9;
case EXTI_Line10: return 10;
case EXTI_Line11: return 11;
case EXTI_Line12: return 12;
case EXTI_Line13: return 13;
case EXTI_Line14: return 14;
case EXTI_Line15: return 15;
}
PIOS_Assert(0);
return 0xFF;
}
uint8_t PIOS_EXTI_gpio_port_to_exti_source_port(GPIO_TypeDef *gpio_port)
{
switch ((uint32_t)gpio_port) {
case (uint32_t)GPIOA: return GPIO_PortSourceGPIOA;
case (uint32_t)GPIOB: return GPIO_PortSourceGPIOB;
case (uint32_t)GPIOC: return GPIO_PortSourceGPIOC;
case (uint32_t)GPIOD: return GPIO_PortSourceGPIOD;
case (uint32_t)GPIOE: return GPIO_PortSourceGPIOE;
case (uint32_t)GPIOF: return GPIO_PortSourceGPIOF;
case (uint32_t)GPIOG: return GPIO_PortSourceGPIOG;
}
PIOS_Assert(0);
return 0xFF;
}
uint8_t PIOS_EXTI_gpio_pin_to_exti_source_pin(uint32_t gpio_pin)
{
switch ((uint32_t)gpio_pin) {
case GPIO_Pin_0: return GPIO_PinSource0;
case GPIO_Pin_1: return GPIO_PinSource1;
case GPIO_Pin_2: return GPIO_PinSource2;
case GPIO_Pin_3: return GPIO_PinSource3;
case GPIO_Pin_4: return GPIO_PinSource4;
case GPIO_Pin_5: return GPIO_PinSource5;
case GPIO_Pin_6: return GPIO_PinSource6;
case GPIO_Pin_7: return GPIO_PinSource7;
case GPIO_Pin_8: return GPIO_PinSource8;
case GPIO_Pin_9: return GPIO_PinSource9;
case GPIO_Pin_10: return GPIO_PinSource10;
case GPIO_Pin_11: return GPIO_PinSource11;
case GPIO_Pin_12: return GPIO_PinSource12;
case GPIO_Pin_13: return GPIO_PinSource13;
case GPIO_Pin_14: return GPIO_PinSource14;
case GPIO_Pin_15: return GPIO_PinSource15;
}
PIOS_Assert(0);
return 0xFF;
}
int32_t PIOS_EXTI_Init(const struct pios_exti_cfg *cfg)
{
PIOS_Assert(cfg);
PIOS_Assert(&__start__exti);
PIOS_Assert(cfg >= &__start__exti);
PIOS_Assert(cfg < &__stop__exti);
uint8_t cfg_index = cfg - &__start__exti;
/* Connect this config to the requested vector */
uint8_t line_index = PIOS_EXTI_line_to_index(cfg->line);
if (pios_exti_line_to_cfg_map[line_index] != PIOS_EXTI_INVALID) {
/* Someone else already has this mapped */
goto out_fail;
}
/* Bind the config to the exti line */
pios_exti_line_to_cfg_map[line_index] = cfg_index;
/* Initialize the GPIO pin */
GPIO_Init(cfg->pin.gpio, &cfg->pin.init);
/* Set up the EXTI interrupt source */
uint8_t exti_source_port = PIOS_EXTI_gpio_port_to_exti_source_port(cfg->pin.gpio);
uint8_t exti_source_pin = PIOS_EXTI_gpio_pin_to_exti_source_pin(cfg->pin.init.GPIO_Pin);
GPIO_EXTILineConfig(exti_source_port, exti_source_pin);
EXTI_Init(&cfg->exti.init);
/* Enable the interrupt channel */
NVIC_Init(&cfg->irq.init);
return 0;
out_fail:
return -1;
}
static bool PIOS_EXTI_generic_irq_handler(uint8_t line_index)
{
uint8_t cfg_index = pios_exti_line_to_cfg_map[line_index];
PIOS_Assert(&__start__exti);
if (cfg_index > NELEMENTS(pios_exti_line_to_cfg_map) ||
cfg_index == PIOS_EXTI_INVALID) {
/* Unconfigured interrupt just fired! */
return false;
}
struct pios_exti_cfg *cfg = &__start__exti + cfg_index;
return cfg->vector();
}
#ifdef PIOS_INCLUDE_FREERTOS
#define PIOS_EXTI_HANDLE_LINE(line, woken) \
if (EXTI_GetITStatus(EXTI_Line##line) != RESET) { \
EXTI_ClearITPendingBit(EXTI_Line##line); \
woken = PIOS_EXTI_generic_irq_handler(line) ? pdTRUE : woken; \
}
#else
#define PIOS_EXTI_HANDLE_LINE(line, woken) \
if (EXTI_GetITStatus(EXTI_Line##line) != RESET) { \
EXTI_ClearITPendingBit(EXTI_Line##line); \
PIOS_EXTI_generic_irq_handler(line); \
}
#endif
/* Bind Interrupt Handlers */
static void PIOS_EXTI_0_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(0, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI0_IRQHandler(void) __attribute__((alias("PIOS_EXTI_0_irq_handler")));
static void PIOS_EXTI_1_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(1, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI1_IRQHandler(void) __attribute__((alias("PIOS_EXTI_1_irq_handler")));
static void PIOS_EXTI_2_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(2, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI2_IRQHandler(void) __attribute__((alias("PIOS_EXTI_2_irq_handler")));
static void PIOS_EXTI_3_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(3, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI3_IRQHandler(void) __attribute__((alias("PIOS_EXTI_3_irq_handler")));
static void PIOS_EXTI_4_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(4, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI4_IRQHandler(void) __attribute__((alias("PIOS_EXTI_4_irq_handler")));
static void PIOS_EXTI_9_5_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(5, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(6, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(7, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(8, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(9, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI9_5_IRQHandler(void) __attribute__((alias("PIOS_EXTI_9_5_irq_handler")));
static void PIOS_EXTI_15_10_irq_handler(void)
{
#ifdef PIOS_INCLUDE_FREERTOS
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
#else
bool xHigherPriorityTaskWoken; // dummy variable
#endif
PIOS_EXTI_HANDLE_LINE(10, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(11, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(12, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(13, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(14, xHigherPriorityTaskWoken);
PIOS_EXTI_HANDLE_LINE(15, xHigherPriorityTaskWoken);
#ifdef PIOS_INCLUDE_FREERTOS
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
#endif
}
void EXTI15_10_IRQHandler(void) __attribute__((alias("PIOS_EXTI_15_10_irq_handler")));
#endif /* PIOS_INCLUDE_EXTI */
/**
* @}
* @}
*/

View File

@ -0,0 +1,304 @@
/**
******************************************************************************
*
* @file pios_flash_internal.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
* @brief brief goes here.
* --
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_FLASH_INTERNAL
#include "stm32f10x_flash.h"
#include "pios_flash_internal_priv.h"
#include "pios_flash.h"
#include <stdbool.h>
struct device_flash_sector {
uint32_t start;
uint32_t size;
uint16_t st_sector;
};
static bool PIOS_Flash_Internal_GetSectorInfo(uint32_t address, uint8_t *sector_number, uint32_t *sector_start, uint32_t *sector_size)
{
uint16_t sector = (address - 0x08000000) / 1024;
if (sector <= 127) {
/* address lies within this sector */
*sector_number = sector;
*sector_start = sector * 1024 + 0x08000000;
*sector_size = 1024;
return true;
}
return false;
}
enum pios_internal_flash_dev_magic {
PIOS_INTERNAL_FLASH_DEV_MAGIC = 0x33445902,
};
struct pios_internal_flash_dev {
enum pios_internal_flash_dev_magic magic;
#if defined(PIOS_INCLUDE_FREERTOS)
xSemaphoreHandle transaction_lock;
#endif /* defined(PIOS_INCLUDE_FREERTOS) */
};
static bool PIOS_Flash_Internal_Validate(struct pios_internal_flash_dev *flash_dev)
{
return flash_dev && (flash_dev->magic == PIOS_INTERNAL_FLASH_DEV_MAGIC);
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_internal_flash_dev *PIOS_Flash_Internal_alloc(void)
{
struct pios_internal_flash_dev *flash_dev;
flash_dev = (struct pios_internal_flash_dev *)pvPortMalloc(sizeof(*flash_dev));
if (!flash_dev) {
return NULL;
}
flash_dev->magic = PIOS_INTERNAL_FLASH_DEV_MAGIC;
return flash_dev;
}
#else
static struct pios_internal_flash_dev pios_internal_flash_devs[PIOS_INTERNAL_FLASH_MAX_DEVS];
static uint8_t pios_internal_flash_num_devs;
static struct pios_internal_flash_dev *PIOS_Flash_Internal_alloc(void)
{
struct pios_internal_flash_dev *flash_dev;
if (pios_internal_flash_num_devs >= PIOS_INTERNAL_FLASH_MAX_DEVS) {
return NULL;
}
flash_dev = &pios_internal_flash_devs[pios_internal_flash_num_devs++];
flash_dev->magic = PIOS_INTERNAL_FLASH_DEV_MAGIC;
return flash_dev;
}
#endif /* defined(PIOS_INCLUDE_FREERTOS) */
int32_t PIOS_Flash_Internal_Init(uintptr_t *flash_id, __attribute__((unused)) const struct pios_flash_internal_cfg *cfg)
{
struct pios_internal_flash_dev *flash_dev;
flash_dev = PIOS_Flash_Internal_alloc();
if (flash_dev == NULL) {
return -1;
}
#if defined(PIOS_INCLUDE_FREERTOS)
flash_dev->transaction_lock = xSemaphoreCreateMutex();
#endif /* defined(PIOS_INCLUDE_FREERTOS) */
*flash_id = (uintptr_t)flash_dev;
return 0;
}
/**********************************
*
* Provide a PIOS flash driver API
*
*********************************/
#include "pios_flash.h"
static int32_t PIOS_Flash_Internal_StartTransaction(uintptr_t flash_id)
{
struct pios_internal_flash_dev *flash_dev = (struct pios_internal_flash_dev *)flash_id;
if (!PIOS_Flash_Internal_Validate(flash_dev)) {
return -1;
}
#if defined(PIOS_INCLUDE_FREERTOS)
if (xSemaphoreTake(flash_dev->transaction_lock, portMAX_DELAY) != pdTRUE) {
return -2;
}
#endif /* defined(PIOS_INCLUDE_FREERTOS) */
/* Unlock the internal flash so we can write to it */
FLASH_Unlock();
return 0;
}
static int32_t PIOS_Flash_Internal_EndTransaction(uintptr_t flash_id)
{
struct pios_internal_flash_dev *flash_dev = (struct pios_internal_flash_dev *)flash_id;
if (!PIOS_Flash_Internal_Validate(flash_dev)) {
return -1;
}
#if defined(PIOS_INCLUDE_FREERTOS)
if (xSemaphoreGive(flash_dev->transaction_lock) != pdTRUE) {
return -2;
}
#endif /* defined(PIOS_INCLUDE_FREERTOS) */
/* Lock the internal flash again so we can no longer write to it */
FLASH_Lock();
return 0;
}
static int32_t PIOS_Flash_Internal_EraseSector(uintptr_t flash_id, uint32_t addr)
{
struct pios_internal_flash_dev *flash_dev = (struct pios_internal_flash_dev *)flash_id;
if (!PIOS_Flash_Internal_Validate(flash_dev)) {
return -1;
}
uint8_t sector_number;
uint32_t sector_start;
uint32_t sector_size;
if (!PIOS_Flash_Internal_GetSectorInfo(addr,
&sector_number,
&sector_start,
&sector_size)) {
/* We're asking for an invalid flash address */
return -2;
}
if (FLASH_ErasePage(sector_start) != FLASH_COMPLETE) {
return -3;
}
return 0;
}
static int32_t PIOS_Flash_Internal_WriteData(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len)
{
PIOS_Assert(data);
struct pios_internal_flash_dev *flash_dev = (struct pios_internal_flash_dev *)flash_id;
if (!PIOS_Flash_Internal_Validate(flash_dev)) {
return -1;
}
uint8_t sector_number;
uint32_t sector_start;
uint32_t sector_size;
uint32_t hword_data;
uint32_t offset;
/* Ensure that the base address is in a valid sector */
if (!PIOS_Flash_Internal_GetSectorInfo(addr,
&sector_number,
&sector_start,
&sector_size)) {
/* We're asking for an invalid flash address */
return -2;
}
/* Ensure that the entire write occurs within the same sector */
if ((uintptr_t)addr + len > sector_start + sector_size) {
/* Write crosses the end of the sector */
return -3;
}
/* Write the data */
uint32_t temp_addr = addr;
uint16_t numberOfhWords = len / 2;
uint16_t x = 0;
FLASH_Status status;
for (x = 0; x < numberOfhWords; ++x) {
offset = 2 * x;
hword_data = (data[offset + 1] << 8) | data[offset];
if (hword_data != *(uint16_t *)(temp_addr + offset)) {
status = FLASH_ProgramHalfWord(temp_addr + offset, hword_data);
} else {
status = FLASH_COMPLETE;
}
PIOS_Assert(status == FLASH_COMPLETE);
}
uint16_t mod = len % 2;
if (mod == 1) {
offset = 2 * x;
hword_data = 0xFF00 | data[offset];
if (hword_data != *(uint16_t *)(temp_addr + offset)) {
status = FLASH_ProgramHalfWord(temp_addr + offset, hword_data);
} else {
status = FLASH_COMPLETE;
}
PIOS_Assert(status == FLASH_COMPLETE);
}
return 0;
}
static int32_t PIOS_Flash_Internal_ReadData(uintptr_t flash_id, uint32_t addr, uint8_t *data, uint16_t len)
{
PIOS_Assert(data);
struct pios_internal_flash_dev *flash_dev = (struct pios_internal_flash_dev *)flash_id;
if (!PIOS_Flash_Internal_Validate(flash_dev)) {
return -1;
}
uint8_t sector_number;
uint32_t sector_start;
uint32_t sector_size;
/* Ensure that the base address is in a valid sector */
if (!PIOS_Flash_Internal_GetSectorInfo(addr,
&sector_number,
&sector_start,
&sector_size)) {
/* We're asking for an invalid flash address */
return -2;
}
/* Ensure that the entire read occurs within the same sector */
if ((uintptr_t)addr + len > sector_start + sector_size) {
/* Read crosses the end of the sector */
return -3;
}
/* Read the data into the buffer directly */
memcpy(data, (void *)addr, len);
return 0;
}
/* Provide a flash driver to external drivers */
const struct pios_flash_driver pios_internal_flash_driver = {
.start_transaction = PIOS_Flash_Internal_StartTransaction,
.end_transaction = PIOS_Flash_Internal_EndTransaction,
.erase_sector = PIOS_Flash_Internal_EraseSector,
.write_data = PIOS_Flash_Internal_WriteData,
.read_data = PIOS_Flash_Internal_ReadData,
};
#endif /* PIOS_INCLUDE_FLASH_INTERNAL */

View File

@ -0,0 +1,161 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_GPIO GPIO Functions
* @brief STM32 Hardware GPIO handling code
* @{
*
* @file pios_gpio.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2013.
* @brief GPIO functions, init, toggle, on & off.
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_GPIO
#include <pios_gpio_priv.h>
/**
* Initialises all the GPIO's
*/
int32_t PIOS_GPIO_Init(uint32_t *gpios_dev_id, const struct pios_gpio_cfg *cfg)
{
PIOS_Assert(cfg);
*gpios_dev_id = (uint32_t)cfg;
for (uint8_t i = 0; i < cfg->num_gpios; i++) {
const struct pios_gpio *gpio = &(cfg->gpios[i]);
/* Enable the peripheral clock for the GPIO */
switch ((uint32_t)gpio->pin.gpio) {
case (uint32_t)GPIOA:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
break;
case (uint32_t)GPIOB:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
break;
case (uint32_t)GPIOC:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
break;
default:
PIOS_Assert(0);
break;
}
if (gpio->remap) {
GPIO_PinRemapConfig(gpio->remap, ENABLE);
}
GPIO_Init(gpio->pin.gpio, &gpio->pin.init);
PIOS_GPIO_Off(*gpios_dev_id, i);
}
return 0;
}
/**
* Turn on GPIO
* \param[in] GPIO GPIO id
*/
void PIOS_GPIO_On(uint32_t gpios_dev_id, uint8_t gpio_id)
{
const struct pios_gpio_cfg *gpio_cfg = (const struct pios_gpio_cfg *)gpios_dev_id;
PIOS_Assert(gpio_cfg);
if (gpio_id >= gpio_cfg->num_gpios) {
/* GPIO index out of range */
return;
}
const struct pios_gpio *gpio = &(gpio_cfg->gpios[gpio_id]);
if (gpio->active_low) {
GPIO_ResetBits(gpio->pin.gpio, gpio->pin.init.GPIO_Pin);
} else {
GPIO_SetBits(gpio->pin.gpio, gpio->pin.init.GPIO_Pin);
}
}
/**
* Turn off GPIO
* \param[in] GPIO GPIO id
*/
void PIOS_GPIO_Off(uint32_t gpios_dev_id, uint8_t gpio_id)
{
const struct pios_gpio_cfg *gpio_cfg = (const struct pios_gpio_cfg *)gpios_dev_id;
PIOS_Assert(gpio_cfg);
if (gpio_id >= gpio_cfg->num_gpios) {
/* GPIO index out of range */
return;
}
const struct pios_gpio *gpio = &(gpio_cfg->gpios[gpio_id]);
if (gpio->active_low) {
GPIO_SetBits(gpio->pin.gpio, gpio->pin.init.GPIO_Pin);
} else {
GPIO_ResetBits(gpio->pin.gpio, gpio->pin.init.GPIO_Pin);
}
}
/**
* Toggle GPIO on/off
* \param[in] GPIO GPIO id
*/
void PIOS_GPIO_Toggle(uint32_t gpios_dev_id, uint8_t gpio_id)
{
const struct pios_gpio_cfg *gpio_cfg = (const struct pios_gpio_cfg *)gpios_dev_id;
PIOS_Assert(gpio_cfg);
if (gpio_id >= gpio_cfg->num_gpios) {
/* GPIO index out of range */
return;
}
const struct pios_gpio *gpio = &(gpio_cfg->gpios[gpio_id]);
if (GPIO_ReadOutputDataBit(gpio->pin.gpio, gpio->pin.init.GPIO_Pin) == Bit_SET) {
if (gpio->active_low) {
PIOS_GPIO_On(gpios_dev_id, gpio_id);
} else {
PIOS_GPIO_Off(gpios_dev_id, gpio_id);
}
} else {
if (gpio->active_low) {
PIOS_GPIO_Off(gpios_dev_id, gpio_id);
} else {
PIOS_GPIO_On(gpios_dev_id, gpio_id);
}
}
}
#endif /* PIOS_INCLUDE_GPIO */
/**
* @}
* @}
*/

View File

@ -0,0 +1,1170 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_I2C I2C Functions
* @brief STM32 Hardware dependent I2C functionality
* @{
*
* @file pios_i2c.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief I2C Enable/Disable routines
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_I2C
#if defined(PIOS_INCLUDE_FREERTOS)
#define USE_FREERTOS
#endif
#include <pios_i2c_priv.h>
// #define I2C_HALT_ON_ERRORS
enum i2c_adapter_event {
I2C_EVENT_BUS_ERROR,
I2C_EVENT_START,
I2C_EVENT_STARTED_MORE_TXN_READ,
I2C_EVENT_STARTED_MORE_TXN_WRITE,
I2C_EVENT_STARTED_LAST_TXN_READ,
I2C_EVENT_STARTED_LAST_TXN_WRITE,
I2C_EVENT_ADDR_SENT_LEN_EQ_0,
I2C_EVENT_ADDR_SENT_LEN_EQ_1,
I2C_EVENT_ADDR_SENT_LEN_EQ_2,
I2C_EVENT_ADDR_SENT_LEN_GT_2,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_0,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_1,
I2C_EVENT_TRANSFER_DONE_LEN_EQ_2,
I2C_EVENT_TRANSFER_DONE_LEN_GT_2,
I2C_EVENT_NACK,
I2C_EVENT_STOPPED,
I2C_EVENT_AUTO, /* FIXME: remove this */
I2C_EVENT_NUM_EVENTS /* Must be last */
};
#if defined(PIOS_I2C_DIAGNOSTICS)
static struct pios_i2c_fault_history i2c_adapter_fault_history;
volatile uint32_t i2c_evirq_history[I2C_LOG_DEPTH];
volatile uint8_t i2c_evirq_history_pointer = 0;
volatile uint32_t i2c_erirq_history[I2C_LOG_DEPTH];
volatile uint8_t i2c_erirq_history_pointer = 0;
volatile enum i2c_adapter_state i2c_state_history[I2C_LOG_DEPTH];
volatile uint8_t i2c_state_history_pointer = 0;
volatile enum i2c_adapter_event i2c_state_event_history[I2C_LOG_DEPTH];
volatile uint8_t i2c_state_event_history_pointer;
static uint8_t i2c_fsm_fault_count = 0;
static uint8_t i2c_bad_event_counter = 0;
static uint8_t i2c_error_interrupt_counter = 0;
static uint8_t i2c_nack_counter = 0;
static uint8_t i2c_timeout_counter = 0;
#endif
static void go_fsm_fault(struct pios_i2c_adapter *i2c_adapter);
static void go_bus_error(struct pios_i2c_adapter *i2c_adapter);
static void go_stopping(struct pios_i2c_adapter *i2c_adapter);
static void go_stopped(struct pios_i2c_adapter *i2c_adapter);
static void go_starting(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_addr(struct pios_i2c_adapter *i2c_adapter);
static void go_r_more_txn_pre_one(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_pre_first(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_pre_middle(struct pios_i2c_adapter *i2c_adapter);
static void go_r_more_txn_pre_last(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_post_last(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_addr(struct pios_i2c_adapter *i2c_adapter);
static void go_r_last_txn_pre_one(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_pre_first(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_pre_middle(struct pios_i2c_adapter *i2c_adapter);
static void go_r_last_txn_pre_last(struct pios_i2c_adapter *i2c_adapter);
static void go_r_any_txn_post_last(struct pios_i2c_adapter *i2c_adapter);
static void go_w_any_txn_addr(struct pios_i2c_adapter *i2c_adapter);
static void go_w_any_txn_middle(struct pios_i2c_adapter *i2c_adapter);
static void go_w_more_txn_last(struct pios_i2c_adapter *i2c_adapter);
static void go_w_any_txn_addr(struct pios_i2c_adapter *i2c_adapter);
static void go_w_any_txn_middle(struct pios_i2c_adapter *i2c_adapter);
static void go_w_last_txn_last(struct pios_i2c_adapter *i2c_adapter);
static void go_nack(struct pios_i2c_adapter *i2c_adapter);
struct i2c_adapter_transition {
void (*entry_fn)(struct pios_i2c_adapter *i2c_adapter);
enum i2c_adapter_state next_state[I2C_EVENT_NUM_EVENTS];
};
static void i2c_adapter_process_auto(struct pios_i2c_adapter *i2c_adapter);
static void i2c_adapter_inject_event(struct pios_i2c_adapter *i2c_adapter, enum i2c_adapter_event event);
static void i2c_adapter_fsm_init(struct pios_i2c_adapter *i2c_adapter);
static bool i2c_adapter_wait_for_stopped(struct pios_i2c_adapter *i2c_adapter);
static void i2c_adapter_reset_bus(struct pios_i2c_adapter *i2c_adapter);
static void i2c_adapter_log_fault(enum pios_i2c_error_type type);
static const struct i2c_adapter_transition i2c_adapter_transitions[I2C_STATE_NUM_STATES] = {
[I2C_STATE_FSM_FAULT] = {
.entry_fn = go_fsm_fault,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPING,
},
},
[I2C_STATE_BUS_ERROR] = {
.entry_fn = go_bus_error,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPING,
},
},
[I2C_STATE_STOPPED] = {
.entry_fn = go_stopped,
.next_state = {
[I2C_EVENT_START] = I2C_STATE_STARTING,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_STOPPING] = {
.entry_fn = go_stopping,
.next_state = {
[I2C_EVENT_STOPPED] = I2C_STATE_STOPPED,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_STARTING] = {
.entry_fn = go_starting,
.next_state = {
[I2C_EVENT_STARTED_MORE_TXN_READ] = I2C_STATE_R_MORE_TXN_ADDR,
[I2C_EVENT_STARTED_MORE_TXN_WRITE] = I2C_STATE_W_MORE_TXN_ADDR,
[I2C_EVENT_STARTED_LAST_TXN_READ] = I2C_STATE_R_LAST_TXN_ADDR,
[I2C_EVENT_STARTED_LAST_TXN_WRITE] = I2C_STATE_W_LAST_TXN_ADDR,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
/*
* Read with restart
*/
[I2C_STATE_R_MORE_TXN_ADDR] = {
.entry_fn = go_r_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_PRE_ONE,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_FIRST,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_FIRST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_MORE_TXN_PRE_ONE] = {
.entry_fn = go_r_more_txn_pre_one,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_MORE_TXN_PRE_FIRST] = {
.entry_fn = go_r_any_txn_pre_first,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_MORE_TXN_PRE_MIDDLE] = {
.entry_fn = go_r_any_txn_pre_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_MORE_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_MORE_TXN_PRE_MIDDLE,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_MORE_TXN_PRE_LAST] = {
.entry_fn = go_r_more_txn_pre_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_MORE_TXN_POST_LAST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_MORE_TXN_POST_LAST] = {
.entry_fn = go_r_any_txn_post_last,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STARTING,
},
},
/*
* Read
*/
[I2C_STATE_R_LAST_TXN_ADDR] = {
.entry_fn = go_r_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_PRE_ONE,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_FIRST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_LAST_TXN_PRE_ONE] = {
.entry_fn = go_r_last_txn_pre_one,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_LAST_TXN_PRE_FIRST] = {
.entry_fn = go_r_any_txn_pre_first,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_LAST_TXN_PRE_MIDDLE] = {
.entry_fn = go_r_any_txn_pre_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_R_LAST_TXN_PRE_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_R_LAST_TXN_PRE_MIDDLE,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_LAST_TXN_PRE_LAST] = {
.entry_fn = go_r_last_txn_pre_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_R_LAST_TXN_POST_LAST,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_R_LAST_TXN_POST_LAST] = {
.entry_fn = go_r_any_txn_post_last,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPING,
},
},
/*
* Write with restart
*/
[I2C_STATE_W_MORE_TXN_ADDR] = {
.entry_fn = go_w_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_W_MORE_TXN_MIDDLE] = {
.entry_fn = go_w_any_txn_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_MORE_TXN_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_MORE_TXN_MIDDLE,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_W_MORE_TXN_LAST] = {
.entry_fn = go_w_more_txn_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STARTING,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
/*
* Write
*/
[I2C_STATE_W_LAST_TXN_ADDR] = {
.entry_fn = go_w_any_txn_addr,
.next_state = {
[I2C_EVENT_ADDR_SENT_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST,
[I2C_EVENT_ADDR_SENT_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_ADDR_SENT_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_W_LAST_TXN_MIDDLE] = {
.entry_fn = go_w_any_txn_middle,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_1] = I2C_STATE_W_LAST_TXN_LAST,
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_TRANSFER_DONE_LEN_GT_2] = I2C_STATE_W_LAST_TXN_MIDDLE,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_W_LAST_TXN_LAST] = {
.entry_fn = go_w_last_txn_last,
.next_state = {
[I2C_EVENT_TRANSFER_DONE_LEN_EQ_0] = I2C_STATE_STOPPING,
[I2C_EVENT_NACK] = I2C_STATE_NACK,
[I2C_EVENT_BUS_ERROR] = I2C_STATE_BUS_ERROR,
},
},
[I2C_STATE_NACK] = {
.entry_fn = go_nack,
.next_state = {
[I2C_EVENT_AUTO] = I2C_STATE_STOPPING,
},
},
};
static void go_fsm_fault(struct pios_i2c_adapter *i2c_adapter)
{
#if defined(I2C_HALT_ON_ERRORS)
PIOS_DEBUG_Assert(0);
#endif
/* Note that this transfer has hit a bus error */
i2c_adapter->bus_error = true;
i2c_adapter_reset_bus(i2c_adapter);
}
static void go_bus_error(struct pios_i2c_adapter *i2c_adapter)
{
/* Note that this transfer has hit a bus error */
i2c_adapter->bus_error = true;
i2c_adapter_reset_bus(i2c_adapter);
}
static void go_stopping(struct pios_i2c_adapter *i2c_adapter)
{
#ifdef USE_FREERTOS
signed portBASE_TYPE pxHigherPriorityTaskWoken = pdFALSE;
#endif
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
#ifdef USE_FREERTOS
if (xSemaphoreGiveFromISR(i2c_adapter->sem_ready, &pxHigherPriorityTaskWoken) != pdTRUE) {
#if defined(I2C_HALT_ON_ERRORS)
PIOS_DEBUG_Assert(0);
#endif
}
portEND_SWITCHING_ISR(pxHigherPriorityTaskWoken); /* FIXME: is this the right place for this? */
#endif /* USE_FREERTOS */
}
static void go_stopped(struct pios_i2c_adapter *i2c_adapter)
{
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE);
}
static void go_starting(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
i2c_adapter->active_byte = &(i2c_adapter->active_txn->buf[0]);
i2c_adapter->last_byte = &(i2c_adapter->active_txn->buf[i2c_adapter->active_txn->len - 1]);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
if (i2c_adapter->active_txn->rw == PIOS_I2C_TXN_READ) {
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
} else {
// For write operations, do not enable the IT_BUF events.
// The current driver does not act when the TX data register is not full, only when the complete byte is sent.
// With the IT_BUF enabled, we constantly get IRQs, See OP-326
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_ERR, ENABLE);
}
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_addr(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_READ);
I2C_Send7bitAddress(i2c_adapter->cfg->regs, (i2c_adapter->active_txn->addr) << 1, I2C_Direction_Receiver);
}
static void go_r_more_txn_pre_one(struct pios_i2c_adapter *i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
}
static void go_r_last_txn_pre_one(struct pios_i2c_adapter *i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_pre_first(struct pios_i2c_adapter *i2c_adapter)
{
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, ENABLE);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_pre_middle(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_r_more_txn_pre_last(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
PIOS_IRQ_Disable();
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_GenerateSTART(i2c_adapter->cfg->regs, ENABLE);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
PIOS_IRQ_Enable();
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_r_last_txn_pre_last(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
PIOS_IRQ_Disable();
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, ENABLE);
PIOS_IRQ_Enable();
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
/* Common to 'more' and 'last' transaction */
static void go_r_any_txn_post_last(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
*(i2c_adapter->active_byte) = I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Move to the next byte */
i2c_adapter->active_byte++;
/* Move to the next transaction */
i2c_adapter->active_txn++;
}
/* Common to 'more' and 'last' transaction */
static void go_w_any_txn_addr(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn->rw == PIOS_I2C_TXN_WRITE);
I2C_Send7bitAddress(i2c_adapter->cfg->regs, (i2c_adapter->active_txn->addr) << 1, I2C_Direction_Transmitter);
}
static void go_w_any_txn_middle(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte < i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
/* Move to the next byte */
i2c_adapter->active_byte++;
PIOS_DEBUG_Assert(i2c_adapter->active_byte <= i2c_adapter->last_byte);
}
static void go_w_more_txn_last(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
/* Move to the next byte */
i2c_adapter->active_byte++;
/* Move to the next transaction */
i2c_adapter->active_txn++;
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
}
static void go_w_last_txn_last(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_DEBUG_Assert(i2c_adapter->active_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_byte == i2c_adapter->last_byte);
PIOS_DEBUG_Assert(i2c_adapter->active_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn >= i2c_adapter->first_txn);
PIOS_DEBUG_Assert(i2c_adapter->active_txn <= i2c_adapter->last_txn);
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_BUF, DISABLE);
I2C_SendData(i2c_adapter->cfg->regs, *(i2c_adapter->active_byte));
// SHOULD MOVE THIS INTO A STOPPING STATE AND SET IT ONLY AFTER THE BYTE WAS SENT
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
/* Move to the next byte */
i2c_adapter->active_byte++;
}
static void go_nack(struct pios_i2c_adapter *i2c_adapter)
{
I2C_ITConfig(i2c_adapter->cfg->regs, I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR, DISABLE);
I2C_AcknowledgeConfig(i2c_adapter->cfg->regs, DISABLE);
I2C_GenerateSTOP(i2c_adapter->cfg->regs, ENABLE);
}
static void i2c_adapter_inject_event(struct pios_i2c_adapter *i2c_adapter, enum i2c_adapter_event event)
{
PIOS_IRQ_Disable();
#if defined(PIOS_I2C_DIAGNOSTICS)
i2c_state_event_history[i2c_state_event_history_pointer] = event;
i2c_state_event_history_pointer = (i2c_state_event_history_pointer + 1) % I2C_LOG_DEPTH;
i2c_state_history[i2c_state_history_pointer] = i2c_adapter->curr_state;
i2c_state_history_pointer = (i2c_state_history_pointer + 1) % I2C_LOG_DEPTH;
if (i2c_adapter_transitions[i2c_adapter->curr_state].next_state[event] == I2C_STATE_FSM_FAULT) {
i2c_adapter_log_fault(PIOS_I2C_ERROR_FSM);
}
#endif
/*
* Move to the next state
*
* This is done prior to calling the new state's entry function to
* guarantee that the entry function never depends on the previous
* state. This way, it cannot ever know what the previous state was.
*/
i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[event];
/* Call the entry function (if any) for the next state. */
if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) {
i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter);
}
/* Process any AUTO transitions in the FSM */
i2c_adapter_process_auto(i2c_adapter);
PIOS_IRQ_Enable();
}
static void i2c_adapter_process_auto(struct pios_i2c_adapter *i2c_adapter)
{
PIOS_IRQ_Disable();
while (i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO]) {
i2c_adapter->curr_state = i2c_adapter_transitions[i2c_adapter->curr_state].next_state[I2C_EVENT_AUTO];
/* Call the entry function (if any) for the next state. */
if (i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn) {
i2c_adapter_transitions[i2c_adapter->curr_state].entry_fn(i2c_adapter);
}
}
PIOS_IRQ_Enable();
}
static void i2c_adapter_fsm_init(struct pios_i2c_adapter *i2c_adapter)
{
i2c_adapter_reset_bus(i2c_adapter);
i2c_adapter->curr_state = I2C_STATE_STOPPED;
}
static bool i2c_adapter_wait_for_stopped(struct pios_i2c_adapter *i2c_adapter)
{
uint32_t guard;
/*
* Wait for the bus to return to the stopped state.
* This was pulled out of the FSM due to occasional
* failures at this transition which previously resulted
* in spinning on this bit in the ISR forever.
*/
#define I2C_CR1_STOP_REQUESTED 0x0200
for (guard = 1000000; /* FIXME: should use the configured bus timeout */
guard && (i2c_adapter->cfg->regs->CR1 & I2C_CR1_STOP_REQUESTED); guard--) {
continue;
}
if (!guard) {
/* We timed out waiting for the stop condition */
return false;
}
return true;
}
static void i2c_adapter_reset_bus(struct pios_i2c_adapter *i2c_adapter)
{
/* Reset the I2C block */
I2C_DeInit(i2c_adapter->cfg->regs);
/* Make sure the bus is free by clocking it until any slaves release the bus. */
GPIO_InitTypeDef scl_gpio_init;
scl_gpio_init = i2c_adapter->cfg->scl.init;
scl_gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
GPIO_Init(i2c_adapter->cfg->scl.gpio, &scl_gpio_init);
GPIO_InitTypeDef sda_gpio_init;
sda_gpio_init = i2c_adapter->cfg->sda.init;
sda_gpio_init.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_SetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
GPIO_Init(i2c_adapter->cfg->sda.gpio, &sda_gpio_init);
/* Check SDA line to determine if slave is asserting bus and clock out if so, this may */
/* have to be repeated (due to futher bus errors) but better than clocking 0xFF into an */
/* ESC */
// bool sda_hung = GPIO_ReadInputDataBit(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin) == Bit_RESET;
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin) == Bit_RESET) {
/* Set clock high and wait for any clock stretching to finish. */
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin) == Bit_RESET) {
;
}
PIOS_DELAY_WaituS(2);
/* Set clock low */
GPIO_ResetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
/* Clock high again */
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
}
/* Generate a start then stop condition */
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
GPIO_ResetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
GPIO_ResetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
PIOS_DELAY_WaituS(2);
/* Set data and clock high and wait for any clock stretching to finish. */
GPIO_SetBits(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin);
GPIO_SetBits(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin);
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->scl.gpio, i2c_adapter->cfg->scl.init.GPIO_Pin) == Bit_RESET) {
;
}
/* Wait for data to be high */
while (GPIO_ReadInputDataBit(i2c_adapter->cfg->sda.gpio, i2c_adapter->cfg->sda.init.GPIO_Pin) != Bit_SET) {
;
}
/* Bus signals are guaranteed to be high (ie. free) after this point */
/* Initialize the GPIO pins to the peripheral function */
GPIO_Init(i2c_adapter->cfg->scl.gpio, &(i2c_adapter->cfg->scl.init));
GPIO_Init(i2c_adapter->cfg->sda.gpio, &(i2c_adapter->cfg->sda.init));
/* Reset the I2C block */
I2C_DeInit(i2c_adapter->cfg->regs);
/* Initialize the I2C block */
I2C_Init(i2c_adapter->cfg->regs, &(i2c_adapter->cfg->init));
#define I2C_BUSY 0x20
if (i2c_adapter->cfg->regs->SR2 & I2C_BUSY) {
/* Reset the I2C block */
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, ENABLE);
I2C_SoftwareResetCmd(i2c_adapter->cfg->regs, DISABLE);
}
}
#include <pios_i2c_priv.h>
/* Return true if the FSM is in a terminal state */
static bool i2c_adapter_fsm_terminated(struct pios_i2c_adapter *i2c_adapter)
{
switch (i2c_adapter->curr_state) {
case I2C_STATE_STOPPING:
case I2C_STATE_STOPPED:
return true;
default:
return false;
}
}
/**
* Logs the last N state transitions and N IRQ events due to
* an error condition
* \param[in] i2c the adapter number to log an event for
*/
void i2c_adapter_log_fault(enum pios_i2c_error_type type)
{
#if defined(PIOS_I2C_DIAGNOSTICS)
i2c_adapter_fault_history.type = type;
for (uint8_t i = 0; i < I2C_LOG_DEPTH; i++) {
i2c_adapter_fault_history.evirq[i] =
i2c_evirq_history[(I2C_LOG_DEPTH + i2c_evirq_history_pointer - 1 - i) % I2C_LOG_DEPTH];
i2c_adapter_fault_history.erirq[i] =
i2c_erirq_history[(I2C_LOG_DEPTH + i2c_erirq_history_pointer - 1 - i) % I2C_LOG_DEPTH];
i2c_adapter_fault_history.event[i] =
i2c_state_event_history[(I2C_LOG_DEPTH + i2c_state_event_history_pointer - 1 - i) % I2C_LOG_DEPTH];
i2c_adapter_fault_history.state[i] =
i2c_state_history[(I2C_LOG_DEPTH + i2c_state_history_pointer - 1 - i) % I2C_LOG_DEPTH];
}
switch (type) {
case PIOS_I2C_ERROR_EVENT:
i2c_bad_event_counter++;
break;
case PIOS_I2C_ERROR_FSM:
i2c_fsm_fault_count++;
break;
case PIOS_I2C_ERROR_INTERRUPT:
i2c_error_interrupt_counter++;
break;
}
#endif /* if defined(PIOS_I2C_DIAGNOSTICS) */
}
/**
* Logs the last N state transitions and N IRQ events due to
* an error condition
* \param[out] data address where to copy the pios_i2c_fault_history structure to
* \param[out] counts three uint16 that receive the bad event, fsm, and error irq
* counts
*/
void PIOS_I2C_GetDiagnostics(struct pios_i2c_fault_history *data, uint8_t *counts)
{
#if defined(PIOS_I2C_DIAGNOSTICS)
memcpy(data, &i2c_adapter_fault_history, sizeof(i2c_adapter_fault_history));
counts[0] = i2c_bad_event_counter;
counts[1] = i2c_fsm_fault_count;
counts[2] = i2c_error_interrupt_counter;
counts[3] = i2c_nack_counter;
counts[4] = i2c_timeout_counter;
#else
struct pios_i2c_fault_history i2c_adapter_fault_history;
i2c_adapter_fault_history.type = PIOS_I2C_ERROR_EVENT;
memcpy(data, &i2c_adapter_fault_history, sizeof(i2c_adapter_fault_history));
counts[0] = counts[1] = counts[2] = 0;
#endif
}
static bool PIOS_I2C_validate(struct pios_i2c_adapter *i2c_adapter)
{
return i2c_adapter->magic == PIOS_I2C_DEV_MAGIC;
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_i2c_adapter *PIOS_I2C_alloc(void)
{
struct pios_i2c_adapter *i2c_adapter;
i2c_adapter = (struct pios_i2c_adapter *)pvPortMalloc(sizeof(*i2c_adapter));
if (!i2c_adapter) {
return NULL;
}
i2c_adapter->magic = PIOS_I2C_DEV_MAGIC;
return i2c_adapter;
}
#else
static struct pios_i2c_adapter pios_i2c_adapters[PIOS_I2C_MAX_DEVS];
static uint8_t pios_i2c_num_adapters;
static struct pios_i2c_adapter *PIOS_I2C_alloc(void)
{
struct pios_i2c_adapter *i2c_adapter;
if (pios_i2c_num_adapters >= PIOS_I2C_MAX_DEVS) {
return NULL;
}
i2c_adapter = &pios_i2c_adapters[pios_i2c_num_adapters++];
i2c_adapter->magic = PIOS_I2C_DEV_MAGIC;
return i2c_adapter;
}
#endif /* if defined(PIOS_INCLUDE_FREERTOS) */
/**
* Initializes IIC driver
* \param[in] mode currently only mode 0 supported
* \return < 0 if initialisation failed
*/
int32_t PIOS_I2C_Init(uint32_t *i2c_id, const struct pios_i2c_adapter_cfg *cfg)
{
PIOS_DEBUG_Assert(i2c_id);
PIOS_DEBUG_Assert(cfg);
struct pios_i2c_adapter *i2c_adapter;
i2c_adapter = (struct pios_i2c_adapter *)PIOS_I2C_alloc();
if (!i2c_adapter) {
goto out_fail;
}
/* Bind the configuration to the device instance */
i2c_adapter->cfg = cfg;
#ifdef USE_FREERTOS
/*
* Must be done prior to calling i2c_adapter_fsm_init()
* since the sem_ready mutex is used in the initial state.
*/
vSemaphoreCreateBinary(i2c_adapter->sem_ready);
i2c_adapter->sem_busy = xSemaphoreCreateMutex();
#endif // USE_FREERTOS
/* Enable the associated peripheral clock */
switch ((uint32_t)i2c_adapter->cfg->regs) {
case (uint32_t)I2C1:
/* Enable I2C peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
break;
case (uint32_t)I2C2:
/* Enable I2C peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
break;
}
if (i2c_adapter->cfg->remap) {
GPIO_PinRemapConfig(i2c_adapter->cfg->remap, ENABLE);
}
/* Initialize the state machine */
i2c_adapter_fsm_init(i2c_adapter);
*i2c_id = (uint32_t)i2c_adapter;
/* Configure and enable I2C interrupts */
NVIC_Init(&(i2c_adapter->cfg->event.init));
NVIC_Init(&(i2c_adapter->cfg->error.init));
/* No error */
return 0;
out_fail:
return -1;
}
/**
* @brief Perform a series of I2C transactions
* @returns 0 if success or error code
* @retval -1 for failed transaction
* @retval -2 for failure to get semaphore
*/
int32_t PIOS_I2C_Transfer(uint32_t i2c_id, const struct pios_i2c_txn txn_list[], uint32_t num_txns)
{
struct pios_i2c_adapter *i2c_adapter = (struct pios_i2c_adapter *)i2c_id;
bool valid = PIOS_I2C_validate(i2c_adapter);
PIOS_Assert(valid)
PIOS_DEBUG_Assert(txn_list);
PIOS_DEBUG_Assert(num_txns);
bool semaphore_success = true;
#ifdef USE_FREERTOS
/* Lock the bus */
portTickType timeout;
timeout = i2c_adapter->cfg->transfer_timeout_ms / portTICK_RATE_MS;
if (xSemaphoreTake(i2c_adapter->sem_busy, timeout) == pdFALSE) {
return -2;
}
#else
uint32_t timeout = 0xfff;
while (i2c_adapter->busy && --timeout) {
;
}
if (timeout == 0) { // timed out
return false;
}
PIOS_IRQ_Disable();
if (i2c_adapter->busy) {
return false;
}
i2c_adapter->busy = 1;
PIOS_IRQ_Enable();
#endif /* USE_FREERTOS */
PIOS_DEBUG_Assert(i2c_adapter->curr_state == I2C_STATE_STOPPED);
i2c_adapter->first_txn = &txn_list[0];
i2c_adapter->last_txn = &txn_list[num_txns - 1];
i2c_adapter->active_txn = i2c_adapter->first_txn;
#ifdef USE_FREERTOS
/* Make sure the done/ready semaphore is consumed before we start */
semaphore_success &= (xSemaphoreTake(i2c_adapter->sem_ready, timeout) == pdTRUE);
#endif
i2c_adapter->bus_error = false;
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_START);
/* Wait for the transfer to complete */
#ifdef USE_FREERTOS
semaphore_success &= (xSemaphoreTake(i2c_adapter->sem_ready, timeout) == pdTRUE);
xSemaphoreGive(i2c_adapter->sem_ready);
#else
PIOS_IRQ_Disable();
i2c_adapter->busy = 0;
PIOS_IRQ_Enable();
#endif /* USE_FREERTOS */
/* Spin waiting for the transfer to finish */
while (!i2c_adapter_fsm_terminated(i2c_adapter)) {
;
}
if (i2c_adapter_wait_for_stopped(i2c_adapter)) {
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STOPPED);
} else {
i2c_adapter_fsm_init(i2c_adapter);
}
#ifdef USE_FREERTOS
/* Unlock the bus */
xSemaphoreGive(i2c_adapter->sem_busy);
if (!semaphore_success) {
i2c_timeout_counter++;
}
#endif /* USE_FREERTOS */
return !semaphore_success ? -2 :
i2c_adapter->bus_error ? -1 :
0;
}
void PIOS_I2C_EV_IRQ_Handler(uint32_t i2c_id)
{
struct pios_i2c_adapter *i2c_adapter = (struct pios_i2c_adapter *)i2c_id;
bool valid = PIOS_I2C_validate(i2c_adapter);
PIOS_Assert(valid)
uint32_t event = I2C_GetLastEvent(i2c_adapter->cfg->regs);
#if defined(PIOS_I2C_DIAGNOSTICS)
/* Store event for diagnostics */
i2c_evirq_history[i2c_evirq_history_pointer] = event;
i2c_evirq_history_pointer = (i2c_evirq_history_pointer + 1) % I2C_LOG_DEPTH;
#endif
#define EVENT_MASK 0x000700FF
event &= EVENT_MASK;
switch (event) { /* Mask out all the bits we don't care about */
case (I2C_EVENT_MASTER_MODE_SELECT | 0x40):
/* Unexplained event: EV5 + RxNE : Extraneous Rx. Probably a late NACK from previous read. */
/* Clean up the extra Rx until the root cause is identified and just keep going */
(void)I2C_ReceiveData(i2c_adapter->cfg->regs);
/* Fall through */
case I2C_EVENT_MASTER_MODE_SELECT: /* EV5 */
switch (i2c_adapter->active_txn->rw) {
case PIOS_I2C_TXN_READ:
if (i2c_adapter->active_txn == i2c_adapter->last_txn) {
/* Final transaction */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_READ);
} else if (i2c_adapter->active_txn < i2c_adapter->last_txn) {
/* More transactions follow */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_READ);
} else {
PIOS_DEBUG_Assert(0);
}
break;
case PIOS_I2C_TXN_WRITE:
if (i2c_adapter->active_txn == i2c_adapter->last_txn) {
/* Final transaction */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_LAST_TXN_WRITE);
} else if (i2c_adapter->active_txn < i2c_adapter->last_txn) {
/* More transactions follow */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_STARTED_MORE_TXN_WRITE);
} else {
PIOS_DEBUG_Assert(0);
}
break;
default:
PIOS_DEBUG_Assert(0);
break;
}
break;
case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: /* EV6 */
case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED: /* EV6 */
switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) {
case 0:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_0);
break;
case 1:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_1);
break;
case 2:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_EQ_2);
break;
default:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_ADDR_SENT_LEN_GT_2);
break;
}
break;
case 0x80: /* TxE only. TRA + MSL + BUSY have been cleared before we got here. */
/* Ignore */
{
static volatile bool halt = FALSE;
while (halt) {
;
}
}
break;
case 0: /* This triggers an FSM fault sometimes, but not having it stops things working */
case 0x40: /* RxNE only. MSL + BUSY have already been cleared by HW. */
case 0x44: /* RxNE + BTF. MSL + BUSY have already been cleared by HW. */
case I2C_EVENT_MASTER_BYTE_RECEIVED: /* EV7 */
case (I2C_EVENT_MASTER_BYTE_RECEIVED | 0x4): /* EV7 + BTF */
case I2C_EVENT_MASTER_BYTE_TRANSMITTED: /* EV8_2 */
case 0x84: /* TxE + BTF. EV8_2 but TRA + MSL + BUSY have already been cleared by HW. */
switch (i2c_adapter->last_byte - i2c_adapter->active_byte + 1) {
case 0:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_0);
break;
case 1:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_1);
break;
case 2:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_EQ_2);
break;
default:
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_TRANSFER_DONE_LEN_GT_2);
break;
}
break;
case I2C_EVENT_MASTER_BYTE_TRANSMITTING: /* EV8 */
/* Ignore this event and wait for TRANSMITTED in case we can't keep up */
goto skip_event;
break;
case 0x30084: /* Occurs between byte tranmistted and master mode selected */
case 0x30000: /* Need to throw away this spurious event */
case 0x30403 & EVENT_MASK: /* Detected this after got a NACK, probably stop bit */
goto skip_event;
break;
default:
i2c_adapter_log_fault(PIOS_I2C_ERROR_EVENT);
#if defined(I2C_HALT_ON_ERRORS)
PIOS_DEBUG_Assert(0);
#endif
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_BUS_ERROR);
break;
}
skip_event:
;
}
void PIOS_I2C_ER_IRQ_Handler(uint32_t i2c_id)
{
struct pios_i2c_adapter *i2c_adapter = (struct pios_i2c_adapter *)i2c_id;
bool valid = PIOS_I2C_validate(i2c_adapter);
PIOS_Assert(valid)
#if defined(PIOS_I2C_DIAGNOSTICS)
uint32_t event = I2C_GetLastEvent(i2c_adapter->cfg->regs);
i2c_erirq_history[i2c_erirq_history_pointer] = event;
i2c_erirq_history_pointer = (i2c_erirq_history_pointer + 1) % 5;
#endif
if (event & I2C_FLAG_AF) {
i2c_nack_counter++;
I2C_ClearFlag(i2c_adapter->cfg->regs, I2C_FLAG_AF);
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_NACK);
} else { /* Mostly bus errors here */
i2c_adapter_log_fault(PIOS_I2C_ERROR_INTERRUPT);
/* Fail hard on any errors for now */
i2c_adapter_inject_event(i2c_adapter, I2C_EVENT_BUS_ERROR);
}
}
#endif /* PIOS_INCLUDE_I2C */
/**
* @}
* @}
*/

View File

@ -0,0 +1,97 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_IRQ IRQ Setup Functions
* @brief STM32 Hardware code to enable and disable interrupts
* @{
*
* @file pios_irq.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
* @brief IRQ Enable/Disable routines
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_IRQ
/* Private Function Prototypes */
/* Local Variables */
/* The nesting counter ensures, that interrupts won't be enabled so long nested functions disable them */
static uint32_t nested_ctr;
/* Stored priority level before IRQ has been disabled (important for co-existence with vPortEnterCritical) */
static uint32_t prev_primask;
/**
* Disables all interrupts (nested)
* \return < 0 On errors
*/
int32_t PIOS_IRQ_Disable(void)
{
/* Get current priority if nested level == 0 */
if (!nested_ctr) {
__asm volatile (" mrs %0, primask\n" : "=r" (prev_primask)
);
}
/* Disable interrupts */
__asm volatile (" mov r0, #1 \n" " msr primask, r0\n" ::: "r0");
++nested_ctr;
/* No error */
return 0;
}
/**
* Enables all interrupts (nested)
* \return < 0 on errors
* \return -1 on nesting errors (PIOS_IRQ_Disable() hasn't been called before)
*/
int32_t PIOS_IRQ_Enable(void)
{
/* Check for nesting error */
if (nested_ctr == 0) {
/* Nesting error */
return -1;
}
/* Decrease nesting level */
--nested_ctr;
/* Set back previous priority once nested level reached 0 again */
if (nested_ctr == 0) {
__asm volatile (" msr primask, %0\n" ::"r" (prev_primask)
);
}
/* No error */
return 0;
}
#endif /* PIOS_INCLUDE_IRQ */
/**
* @}
* @}
*/

View File

@ -0,0 +1,284 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_PWM PWM Input Functions
* @brief Code to measure with PWM input
* @{
*
* @file pios_pwm.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief PWM Input functions (STM32 dependent)
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_PWM
#include "pios_pwm_priv.h"
/* Provide a RCVR driver */
static int32_t PIOS_PWM_Get(uint32_t rcvr_id, uint8_t channel);
const struct pios_rcvr_driver pios_pwm_rcvr_driver = {
.read = PIOS_PWM_Get,
};
/* Local Variables */
/* 100 ms timeout without updates on channels */
static const uint32_t PWM_SUPERVISOR_TIMEOUT = 100000;
enum pios_pwm_dev_magic {
PIOS_PWM_DEV_MAGIC = 0xab30293c,
};
struct pios_pwm_dev {
enum pios_pwm_dev_magic magic;
const struct pios_pwm_cfg *cfg;
uint8_t CaptureState[PIOS_PWM_NUM_INPUTS];
uint16_t RiseValue[PIOS_PWM_NUM_INPUTS];
uint16_t FallValue[PIOS_PWM_NUM_INPUTS];
uint32_t CaptureValue[PIOS_PWM_NUM_INPUTS];
uint32_t CapCounter[PIOS_PWM_NUM_INPUTS];
uint32_t us_since_update[PIOS_PWM_NUM_INPUTS];
};
static bool PIOS_PWM_validate(struct pios_pwm_dev *pwm_dev)
{
return pwm_dev->magic == PIOS_PWM_DEV_MAGIC;
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_pwm_dev *PIOS_PWM_alloc(void)
{
struct pios_pwm_dev *pwm_dev;
pwm_dev = (struct pios_pwm_dev *)pvPortMalloc(sizeof(*pwm_dev));
if (!pwm_dev) {
return NULL;
}
pwm_dev->magic = PIOS_PWM_DEV_MAGIC;
return pwm_dev;
}
#else
static struct pios_pwm_dev pios_pwm_devs[PIOS_PWM_MAX_DEVS];
static uint8_t pios_pwm_num_devs;
static struct pios_pwm_dev *PIOS_PWM_alloc(void)
{
struct pios_pwm_dev *pwm_dev;
if (pios_pwm_num_devs >= PIOS_PWM_MAX_DEVS) {
return NULL;
}
pwm_dev = &pios_pwm_devs[pios_pwm_num_devs++];
pwm_dev->magic = PIOS_PWM_DEV_MAGIC;
return pwm_dev;
}
#endif /* if defined(PIOS_INCLUDE_FREERTOS) */
static void PIOS_PWM_tim_overflow_cb(uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
static void PIOS_PWM_tim_edge_cb(uint32_t id, uint32_t context, uint8_t channel, uint16_t count);
static const struct pios_tim_callbacks tim_callbacks = {
.overflow = PIOS_PWM_tim_overflow_cb,
.edge = PIOS_PWM_tim_edge_cb,
};
/**
* Initialises all the pins
*/
int32_t PIOS_PWM_Init(uint32_t *pwm_id, const struct pios_pwm_cfg *cfg)
{
PIOS_DEBUG_Assert(pwm_id);
PIOS_DEBUG_Assert(cfg);
struct pios_pwm_dev *pwm_dev;
pwm_dev = (struct pios_pwm_dev *)PIOS_PWM_alloc();
if (!pwm_dev) {
goto out_fail;
}
/* Bind the configuration to the device instance */
pwm_dev->cfg = cfg;
for (uint8_t i = 0; i < PIOS_PWM_NUM_INPUTS; i++) {
/* Flush counter variables */
pwm_dev->CaptureState[i] = 0;
pwm_dev->RiseValue[i] = 0;
pwm_dev->FallValue[i] = 0;
pwm_dev->CaptureValue[i] = PIOS_RCVR_TIMEOUT;
}
uint32_t tim_id;
if (PIOS_TIM_InitChannels(&tim_id, cfg->channels, cfg->num_channels, &tim_callbacks, (uint32_t)pwm_dev)) {
return -1;
}
/* Configure the channels to be in capture/compare mode */
for (uint8_t i = 0; i < cfg->num_channels; i++) {
const struct pios_tim_channel *chan = &cfg->channels[i];
/* Configure timer for input capture */
TIM_ICInitTypeDef TIM_ICInitStructure = cfg->tim_ic_init;
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
/* Enable the Capture Compare Interrupt Request */
switch (chan->timer_chan) {
case TIM_Channel_1:
TIM_ITConfig(chan->timer, TIM_IT_CC1, ENABLE);
break;
case TIM_Channel_2:
TIM_ITConfig(chan->timer, TIM_IT_CC2, ENABLE);
break;
case TIM_Channel_3:
TIM_ITConfig(chan->timer, TIM_IT_CC3, ENABLE);
break;
case TIM_Channel_4:
TIM_ITConfig(chan->timer, TIM_IT_CC4, ENABLE);
break;
}
// Need the update event for that timer to detect timeouts
TIM_ITConfig(chan->timer, TIM_IT_Update, ENABLE);
}
*pwm_id = (uint32_t)pwm_dev;
return 0;
out_fail:
return -1;
}
/**
* Get the value of an input channel
* \param[in] channel Number of the channel desired (zero based)
* \output PIOS_RCVR_INVALID channel not available
* \output PIOS_RCVR_TIMEOUT failsafe condition or missing receiver
* \output >=0 channel value
*/
static int32_t PIOS_PWM_Get(uint32_t rcvr_id, uint8_t channel)
{
struct pios_pwm_dev *pwm_dev = (struct pios_pwm_dev *)rcvr_id;
if (!PIOS_PWM_validate(pwm_dev)) {
/* Invalid device specified */
return PIOS_RCVR_INVALID;
}
if (channel >= PIOS_PWM_NUM_INPUTS) {
/* Channel out of range */
return PIOS_RCVR_INVALID;
}
return pwm_dev->CaptureValue[channel];
}
static void PIOS_PWM_tim_overflow_cb(__attribute__((unused)) uint32_t tim_id, uint32_t context, uint8_t channel, uint16_t count)
{
struct pios_pwm_dev *pwm_dev = (struct pios_pwm_dev *)context;
if (!PIOS_PWM_validate(pwm_dev)) {
/* Invalid device specified */
return;
}
if (channel >= pwm_dev->cfg->num_channels) {
/* Channel out of range */
return;
}
pwm_dev->us_since_update[channel] += count;
if (pwm_dev->us_since_update[channel] >= PWM_SUPERVISOR_TIMEOUT) {
pwm_dev->CaptureState[channel] = 0;
pwm_dev->RiseValue[channel] = 0;
pwm_dev->FallValue[channel] = 0;
pwm_dev->CaptureValue[channel] = PIOS_RCVR_TIMEOUT;
pwm_dev->us_since_update[channel] = 0;
}
}
static void PIOS_PWM_tim_edge_cb(__attribute__((unused)) uint32_t tim_id, uint32_t context, uint8_t chan_idx, uint16_t count)
{
/* Recover our device context */
struct pios_pwm_dev *pwm_dev = (struct pios_pwm_dev *)context;
if (!PIOS_PWM_validate(pwm_dev)) {
/* Invalid device specified */
return;
}
if (chan_idx >= pwm_dev->cfg->num_channels) {
/* Channel out of range */
return;
}
const struct pios_tim_channel *chan = &pwm_dev->cfg->channels[chan_idx];
if (pwm_dev->CaptureState[chan_idx] == 0) {
pwm_dev->RiseValue[chan_idx] = count;
pwm_dev->us_since_update[chan_idx] = 0;
} else {
pwm_dev->FallValue[chan_idx] = count;
}
// flip state machine and capture value here
/* Simple rise or fall state machine */
TIM_ICInitTypeDef TIM_ICInitStructure = pwm_dev->cfg->tim_ic_init;
if (pwm_dev->CaptureState[chan_idx] == 0) {
/* Switch states */
pwm_dev->CaptureState[chan_idx] = 1;
/* Switch polarity of input capture */
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
} else {
/* Capture computation */
if (pwm_dev->FallValue[chan_idx] > pwm_dev->RiseValue[chan_idx]) {
pwm_dev->CaptureValue[chan_idx] = (pwm_dev->FallValue[chan_idx] - pwm_dev->RiseValue[chan_idx]);
} else {
pwm_dev->CaptureValue[chan_idx] = ((chan->timer->ARR - pwm_dev->RiseValue[chan_idx]) + pwm_dev->FallValue[chan_idx]);
}
/* Switch states */
pwm_dev->CaptureState[chan_idx] = 0;
/* Increase supervisor counter */
pwm_dev->CapCounter[chan_idx]++;
/* Switch polarity of input capture */
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_Channel = chan->timer_chan;
TIM_ICInit(chan->timer, &TIM_ICInitStructure);
}
}
#endif /* PIOS_INCLUDE_PWM */
/**
* @}
* @}
*/

View File

@ -0,0 +1,130 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_PWM PWM Input Functions
* @brief Code to measure with PWM input
* @{
*
* @file pios_pwm.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief PWM Input functions (STM32 dependent)
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_RTC
#include <pios_rtc_priv.h>
#ifndef PIOS_RTC_PRESCALER
#define PIOS_RTC_PRESCALER 100
#endif
struct rtc_callback_entry {
void (*fn)(uint32_t);
uint32_t data;
};
#define PIOS_RTC_MAX_CALLBACKS 3
struct rtc_callback_entry rtc_callback_list[PIOS_RTC_MAX_CALLBACKS];
static uint8_t rtc_callback_next = 0;
void PIOS_RTC_Init(const struct pios_rtc_cfg *cfg)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP | RCC_APB1Periph_PWR,
ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_RTCCLKConfig(cfg->clksrc);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForLastTask();
RTC_WaitForSynchro();
RTC_WaitForLastTask();
/* Configure and enable the RTC Second interrupt */
NVIC_Init(&cfg->irq.init);
RTC_ITConfig(RTC_IT_SEC, ENABLE);
RTC_WaitForLastTask();
RTC_SetPrescaler(cfg->prescaler);
RTC_WaitForLastTask();
RTC_SetCounter(0);
RTC_WaitForLastTask();
}
uint32_t PIOS_RTC_Counter()
{
return RTC_GetCounter();
}
/* FIXME: This shouldn't use hard-coded clock rates, dividers or prescalers.
* Should get these from the cfg struct passed to init.
*/
float PIOS_RTC_Rate()
{
return (float)(8e6f / 128.0f) / (1 + PIOS_RTC_PRESCALER);
}
float PIOS_RTC_MsPerTick()
{
return 1000.0f / PIOS_RTC_Rate();
}
/* TODO: This needs a mutex around rtc_callbacks[] */
bool PIOS_RTC_RegisterTickCallback(void (*fn)(uint32_t id), uint32_t data)
{
struct rtc_callback_entry *cb;
if (rtc_callback_next >= PIOS_RTC_MAX_CALLBACKS) {
return false;
}
cb = &rtc_callback_list[rtc_callback_next++];
cb->fn = fn;
cb->data = data;
return true;
}
void PIOS_RTC_irq_handler(void)
{
if (RTC_GetITStatus(RTC_IT_SEC)) {
/* Call all registered callbacks */
for (uint8_t i = 0; i < rtc_callback_next; i++) {
struct rtc_callback_entry *cb = &rtc_callback_list[i];
if (cb->fn) {
(cb->fn)(cb->data);
}
}
/* Wait until last write operation on RTC registers has finished */
RTC_WaitForLastTask();
/* Clear the RTC Second interrupt */
RTC_ClearITPendingBit(RTC_IT_SEC);
}
}
#endif /* PIOS_INCLUDE_RTC */
/**
* @}
* @}
*/

View File

@ -0,0 +1,659 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_SPI SPI Functions
* @brief PIOS interface to read and write from SPI ports
* @{
*
* @file pios_spi.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2012.
* @brief Hardware Abstraction Layer for SPI ports of STM32
* @see The GNU Public License (GPL) Version 3
* @notes
*
* Note that additional chip select lines can be easily added by using
* the remaining free GPIOs of the core module. Shared SPI ports should be
* arbitrated with (FreeRTOS based) Mutexes to avoid collisions!
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pios.h>
#ifdef PIOS_INCLUDE_SPI
#include <pios_spi_priv.h>
static bool PIOS_SPI_validate(__attribute__((unused)) struct pios_spi_dev *com_dev)
{
/* Should check device magic here */
return true;
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_spi_dev *PIOS_SPI_alloc(void)
{
return pvPortMalloc(sizeof(struct pios_spi_dev));
}
#else
static struct pios_spi_dev pios_spi_devs[PIOS_SPI_MAX_DEVS];
static uint8_t pios_spi_num_devs;
static struct pios_spi_dev *PIOS_SPI_alloc(void)
{
if (pios_spi_num_devs >= PIOS_SPI_MAX_DEVS) {
return NULL;
}
return &pios_spi_devs[pios_spi_num_devs++];
}
#endif
/**
* Initialises SPI pins
* \param[in] mode currently only mode 0 supported
* \return < 0 if initialisation failed
*/
int32_t PIOS_SPI_Init(uint32_t *spi_id, const struct pios_spi_cfg *cfg)
{
uint32_t init_ssel = 0;
PIOS_Assert(spi_id);
PIOS_Assert(cfg);
struct pios_spi_dev *spi_dev;
spi_dev = (struct pios_spi_dev *)PIOS_SPI_alloc();
if (!spi_dev) {
goto out_fail;
}
/* Bind the configuration to the device instance */
spi_dev->cfg = cfg;
#if defined(PIOS_INCLUDE_FREERTOS)
vSemaphoreCreateBinary(spi_dev->busy);
xSemaphoreGive(spi_dev->busy);
#else
spi_dev->busy = 0;
#endif
/* Disable callback function */
spi_dev->callback = NULL;
/* Set rx/tx dummy bytes to a known value */
spi_dev->rx_dummy_byte = 0xFF;
spi_dev->tx_dummy_byte = 0xFF;
switch (spi_dev->cfg->init.SPI_NSS) {
case SPI_NSS_Soft:
if (spi_dev->cfg->init.SPI_Mode == SPI_Mode_Master) {
/* We're a master in soft NSS mode, make sure we see NSS high at all times. */
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Set);
/* Init as many slave selects as the config advertises. */
init_ssel = spi_dev->cfg->slave_count;
} else {
/* We're a slave in soft NSS mode, make sure we see NSS low at all times. */
SPI_NSSInternalSoftwareConfig(spi_dev->cfg->regs, SPI_NSSInternalSoft_Reset);
}
break;
case SPI_NSS_Hard:
/* only legal for single-slave config */
PIOS_Assert(spi_dev->cfg->slave_count == 1);
init_ssel = 1;
/* FIXME: Should this also call SPI_SSOutputCmd()? */
break;
default:
PIOS_Assert(0);
}
/* Initialize the GPIO pins */
GPIO_Init(spi_dev->cfg->sclk.gpio, &(spi_dev->cfg->sclk.init));
GPIO_Init(spi_dev->cfg->mosi.gpio, &(spi_dev->cfg->mosi.init));
GPIO_Init(spi_dev->cfg->miso.gpio, &(spi_dev->cfg->miso.init));
for (uint32_t i = 0; i < init_ssel; i++) {
/* Since we're driving the SSEL pin in software, ensure that the slave is deselected */
/* XXX multi-slave support - maybe have another SPI_NSS_ mode? */
GPIO_SetBits(spi_dev->cfg->ssel[i].gpio, spi_dev->cfg->ssel[i].init.GPIO_Pin);
GPIO_Init(spi_dev->cfg->ssel[i].gpio, (GPIO_InitTypeDef *)&(spi_dev->cfg->ssel[i].init));
}
/* Enable the associated peripheral clock */
switch ((uint32_t)spi_dev->cfg->regs) {
case (uint32_t)SPI1:
/* Enable SPI peripheral clock (APB2 == high speed) */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
break;
case (uint32_t)SPI2:
/* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
break;
case (uint32_t)SPI3:
/* Enable SPI peripheral clock (APB1 == slow speed) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
break;
}
/* Enable DMA clock */
RCC_AHBPeriphClockCmd(spi_dev->cfg->dma.ahb_clk, ENABLE);
/* Configure DMA for SPI Rx */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
DMA_Init(spi_dev->cfg->dma.rx.channel, &(spi_dev->cfg->dma.rx.init));
/* Configure DMA for SPI Tx */
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
DMA_Init(spi_dev->cfg->dma.tx.channel, &(spi_dev->cfg->dma.tx.init));
/* Initialize the SPI block */
SPI_Init(spi_dev->cfg->regs, &(spi_dev->cfg->init));
/* Configure CRC calculation */
if (spi_dev->cfg->use_crc) {
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
} else {
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
}
/* Enable SPI */
SPI_Cmd(spi_dev->cfg->regs, ENABLE);
/* Enable SPI interrupts to DMA */
SPI_I2S_DMACmd(spi_dev->cfg->regs, SPI_I2S_DMAReq_Tx | SPI_I2S_DMAReq_Rx, ENABLE);
/* Configure DMA interrupt */
NVIC_Init(&(spi_dev->cfg->dma.irq.init));
*spi_id = (uint32_t)spi_dev;
return 0;
out_fail:
return -1;
}
/**
* (Re-)initialises SPI peripheral clock rate
*
* \param[in] spi SPI number (0 or 1)
* \param[in] spi_prescaler configures the SPI speed:
* <UL>
* <LI>PIOS_SPI_PRESCALER_2: sets clock rate 27.7~ nS @ 72 MHz (36 MBit/s) (only supported for spi==0, spi1 uses 4 instead)
* <LI>PIOS_SPI_PRESCALER_4: sets clock rate 55.5~ nS @ 72 MHz (18 MBit/s)
* <LI>PIOS_SPI_PRESCALER_8: sets clock rate 111.1~ nS @ 72 MHz (9 MBit/s)
* <LI>PIOS_SPI_PRESCALER_16: sets clock rate 222.2~ nS @ 72 MHz (4.5 MBit/s)
* <LI>PIOS_SPI_PRESCALER_32: sets clock rate 444.4~ nS @ 72 MHz (2.25 MBit/s)
* <LI>PIOS_SPI_PRESCALER_64: sets clock rate 888.8~ nS @ 72 MHz (1.125 MBit/s)
* <LI>PIOS_SPI_PRESCALER_128: sets clock rate 1.7~ nS @ 72 MHz (0.562 MBit/s)
* <LI>PIOS_SPI_PRESCALER_256: sets clock rate 3.5~ nS @ 72 MHz (0.281 MBit/s)
* </UL>
* \return 0 if no error
* \return -1 if disabled SPI port selected
* \return -3 if invalid spi_prescaler selected
*/
int32_t PIOS_SPI_SetClockSpeed(uint32_t spi_id, SPIPrescalerTypeDef spi_prescaler)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
SPI_InitTypeDef SPI_InitStructure;
if (spi_prescaler >= 8) {
/* Invalid prescaler selected */
return -3;
}
/* Start with a copy of the default configuration for the peripheral */
SPI_InitStructure = spi_dev->cfg->init;
/* Adjust the prescaler for the peripheral's clock */
SPI_InitStructure.SPI_BaudRatePrescaler = ((uint16_t)spi_prescaler & 7) << 3;
/* Write back the new configuration */
SPI_Init(spi_dev->cfg->regs, &SPI_InitStructure);
PIOS_SPI_TransferByte(spi_id, 0xFF);
return 0;
}
/**
* Claim the SPI bus semaphore. Calling the SPI functions does not require this
* \param[in] spi SPI number (0 or 1)
* \return 0 if no error
* \return -1 if timeout before claiming semaphore
*/
int32_t PIOS_SPI_ClaimBus(uint32_t spi_id)
{
#if defined(PIOS_INCLUDE_FREERTOS)
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
if (xSemaphoreTake(spi_dev->busy, 0xffff) != pdTRUE) {
return -1;
}
#else
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
uint32_t timeout = 0xffff;
while ((PIOS_SPI_Busy(spi_id) || spi_dev->busy) && --timeout) {
;
}
if (timeout == 0) { // timed out
return -1;
}
PIOS_IRQ_Disable();
if (spi_dev->busy) {
return -1;
}
spi_dev->busy = 1;
PIOS_IRQ_Enable();
#endif /* if defined(PIOS_INCLUDE_FREERTOS) */
return 0;
}
/**
* Claim the SPI bus semaphore from an ISR. Has no timeout.
* \param[in] spi SPI number (0 or 1)
* \param woken[in,out] If non-NULL, will be set to true if woken was false and a higher priority
* task has is now eligible to run, else unchanged
* \return 0 if no error
* \return -1 if timeout before claiming semaphore
*/
int32_t PIOS_SPI_ClaimBusISR(uint32_t spi_id, bool *woken)
{
#if defined(PIOS_INCLUDE_FREERTOS)
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
signed portBASE_TYPE higherPriorityTaskWoken = pdFALSE;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
if (xSemaphoreTakeFromISR(spi_dev->busy, &higherPriorityTaskWoken) != pdTRUE) {
return -1;
}
if (woken) {
*woken = *woken || (higherPriorityTaskWoken == pdTRUE);
}
return 0;
#else
if (woken) {
*woken = false;
}
return PIOS_SPI_ClaimBus(spi_id);
#endif
}
/**
* Release the SPI bus semaphore. Calling the SPI functions does not require this
* \param[in] spi SPI number (0 or 1)
* \return 0 if no error
*/
int32_t PIOS_SPI_ReleaseBus(uint32_t spi_id)
{
#if defined(PIOS_INCLUDE_FREERTOS)
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
xSemaphoreGive(spi_dev->busy);
#else
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
PIOS_IRQ_Disable();
spi_dev->busy = 0;
PIOS_IRQ_Enable();
#endif
return 0;
}
/**
* Release the SPI bus semaphore from ISR. Calling the SPI functions does not require this
* \param[in] spi SPI number (0 or 1)
* \param woken[in,out] If non-NULL, will be set to true if woken was false and a higher priority
* task has is now eligible to run, else unchanged
* \return 0 if no error
*/
int32_t PIOS_SPI_ReleaseBusISR(uint32_t spi_id, bool *woken)
{
#if defined(PIOS_INCLUDE_FREERTOS)
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
signed portBASE_TYPE higherPriorityTaskWoken = pdFALSE;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
xSemaphoreGiveFromISR(spi_dev->busy, &higherPriorityTaskWoken);
if (woken) {
*woken = *woken || (higherPriorityTaskWoken == pdTRUE);
}
return 0;
#else
if (woken) {
*woken = false;
}
return PIOS_SPI_ReleaseBus(spi_id);
#endif
}
/**
* Controls the RC (Register Clock alias Chip Select) pin of a SPI port
* \param[in] spi SPI number (0 or 1)
* \param[in] pin_value 0 or 1
* \return 0 if no error
*/
int32_t PIOS_SPI_RC_PinSet(uint32_t spi_id, uint32_t slave_id, uint8_t pin_value)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
PIOS_Assert(slave_id <= spi_dev->cfg->slave_count)
/* XXX multi-slave support? */
if (pin_value) {
GPIO_SetBits(spi_dev->cfg->ssel[slave_id].gpio, spi_dev->cfg->ssel[slave_id].init.GPIO_Pin);
} else {
GPIO_ResetBits(spi_dev->cfg->ssel[slave_id].gpio, spi_dev->cfg->ssel[slave_id].init.GPIO_Pin);
}
return 0;
}
/**
* Transfers a byte to SPI output and reads back the return value from SPI input
* \param[in] spi SPI number (0 or 1)
* \param[in] b the byte which should be transfered
*/
static uint8_t dummy;
int32_t PIOS_SPI_TransferByte(uint32_t spi_id, uint8_t b)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
uint8_t rx_byte;
/*
* Procedure taken from STM32F10xxx Reference Manual section 23.3.5
*/
/* Make sure the RXNE flag is cleared by reading the DR register */
dummy = spi_dev->cfg->regs->DR;
/* Start the transfer */
spi_dev->cfg->regs->DR = b;
/* Wait until there is a byte to read */
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_RXNE)) {
;
}
/* Read the rx'd byte */
rx_byte = spi_dev->cfg->regs->DR;
/* Wait until the TXE goes high */
while (!(spi_dev->cfg->regs->SR & SPI_I2S_FLAG_TXE)) {
;
}
/* Wait for SPI transfer to have fully completed */
while (spi_dev->cfg->regs->SR & SPI_I2S_FLAG_BSY) {
;
}
/* Return received byte */
return rx_byte;
}
/**
* Transfers a block of bytes via DMA.
* \param[in] spi SPI number (0 or 1)
* \param[in] send_buffer pointer to buffer which should be sent.<BR>
* If NULL, 0xff (all-one) will be sent.
* \param[in] receive_buffer pointer to buffer which should get the received values.<BR>
* If NULL, received bytes will be discarded.
* \param[in] len number of bytes which should be transfered
* \param[in] callback pointer to callback function which will be executed
* from DMA channel interrupt once the transfer is finished.
* If NULL, no callback function will be used, and PIOS_SPI_TransferBlock() will
* block until the transfer is finished.
* \return >= 0 if no error during transfer
* \return -1 if disabled SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer
*/
int32_t PIOS_SPI_TransferBlock(uint32_t spi_id, const uint8_t *send_buffer, uint8_t *receive_buffer, uint16_t len, void *callback)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
DMA_InitTypeDef dma_init;
/* Exit if ongoing transfer */
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
return -3;
}
/* Disable the SPI peripheral */
SPI_Cmd(spi_dev->cfg->regs, DISABLE);
/* Disable the DMA channels */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, DISABLE);
DMA_Cmd(spi_dev->cfg->dma.tx.channel, DISABLE);
/* Set callback function */
spi_dev->callback = callback;
/*
* Configure Rx channel
*/
/* Start with the default configuration for this peripheral */
dma_init = spi_dev->cfg->dma.rx.init;
if (receive_buffer != NULL) {
/* Enable memory addr. increment - bytes written into receive buffer */
dma_init.DMA_MemoryBaseAddr = (uint32_t)receive_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
} else {
/* Disable memory addr. increment - bytes written into dummy buffer */
spi_dev->rx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t)&spi_dev->rx_dummy_byte;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
}
if (spi_dev->cfg->use_crc) {
/* Make sure the CRC error flag is cleared before we start */
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
}
dma_init.DMA_BufferSize = len;
DMA_Init(spi_dev->cfg->dma.rx.channel, &(dma_init));
/*
* Configure Tx channel
*/
/* Start with the default configuration for this peripheral */
dma_init = spi_dev->cfg->dma.tx.init;
if (send_buffer != NULL) {
/* Enable memory addr. increment - bytes written into receive buffer */
dma_init.DMA_MemoryBaseAddr = (uint32_t)send_buffer;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
} else {
/* Disable memory addr. increment - bytes written into dummy buffer */
spi_dev->tx_dummy_byte = 0xFF;
dma_init.DMA_MemoryBaseAddr = (uint32_t)&spi_dev->tx_dummy_byte;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Disable;
}
if (spi_dev->cfg->use_crc) {
/* The last byte of the payload will be replaced with the CRC8 */
dma_init.DMA_BufferSize = len - 1;
} else {
dma_init.DMA_BufferSize = len;
}
DMA_Init(spi_dev->cfg->dma.tx.channel, &(dma_init));
/* Enable DMA interrupt if callback function active */
DMA_ITConfig(spi_dev->cfg->dma.rx.channel, DMA_IT_TC, (callback != NULL) ? ENABLE : DISABLE);
/* Flush out the CRC registers */
SPI_CalculateCRC(spi_dev->cfg->regs, DISABLE);
(void)SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
/* Make sure to flush out the receive buffer */
(void)SPI_I2S_ReceiveData(spi_dev->cfg->regs);
if (spi_dev->cfg->use_crc) {
/* Need a 0->1 transition to reset the CRC logic */
SPI_CalculateCRC(spi_dev->cfg->regs, ENABLE);
}
/* Start DMA transfers */
DMA_Cmd(spi_dev->cfg->dma.rx.channel, ENABLE);
DMA_Cmd(spi_dev->cfg->dma.tx.channel, ENABLE);
/* Reenable the SPI device */
SPI_Cmd(spi_dev->cfg->regs, ENABLE);
if (callback) {
/* User has requested a callback, don't wait for the transfer to complete. */
return 0;
}
/* Wait until all bytes have been transmitted/received */
while (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel)) {
;
}
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE))) {
;
}
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) {
;
}
/* Check the CRC on the transfer if enabled. */
if (spi_dev->cfg->use_crc) {
/* Check the SPI CRC error flag */
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
return -4;
}
}
/* No error */
return 0;
}
/**
* Check if a transfer is in progress
* \param[in] spi SPI number (0 or 1)
* \return >= 0 if no transfer is in progress
* \return -1 if disabled SPI port selected
* \return -2 if unsupported SPI port selected
* \return -3 if function has been called during an ongoing DMA transfer
*/
int32_t PIOS_SPI_Busy(uint32_t spi_id)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
/* DMA buffer has data or SPI transmit register not empty or SPI is busy*/
if (DMA_GetCurrDataCounter(spi_dev->cfg->dma.rx.channel) ||
!SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE) ||
SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) {
return -3;
}
return 0;
}
void PIOS_SPI_SetPrescalar(uint32_t spi_id, uint32_t prescaler)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid);
PIOS_Assert(IS_SPI_BAUDRATE_PRESCALER(prescaler));
spi_dev->cfg->regs->CR1 = (spi_dev->cfg->regs->CR1 & ~0x0038) | prescaler;
}
void PIOS_SPI_IRQ_Handler(uint32_t spi_id)
{
struct pios_spi_dev *spi_dev = (struct pios_spi_dev *)spi_id;
bool valid = PIOS_SPI_validate(spi_dev);
PIOS_Assert(valid)
DMA_ClearFlag(spi_dev->cfg->dma.irq.flags);
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (!(SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_TXE))) {
;
}
/* Wait for the final bytes of the transfer to complete, including CRC byte(s). */
while (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_I2S_FLAG_BSY)) {
;
}
if (spi_dev->callback != NULL) {
bool crc_ok = TRUE;
uint8_t crc_val;
if (SPI_I2S_GetFlagStatus(spi_dev->cfg->regs, SPI_FLAG_CRCERR)) {
crc_ok = FALSE;
SPI_I2S_ClearFlag(spi_dev->cfg->regs, SPI_FLAG_CRCERR);
}
crc_val = SPI_GetCRC(spi_dev->cfg->regs, SPI_CRC_Rx);
spi_dev->callback(crc_ok, crc_val);
}
}
#endif /* PIOS_INCLUDE_SPI */
/**
* @}
* @}
*/

View File

@ -0,0 +1,227 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_SYS System Functions
* @brief PIOS System Initialization code
* @{
*
* @file pios_sys.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
* @brief Sets up basic STM32 system hardware, functions are called from Main.
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_SYS
/* Private Function Prototypes */
void NVIC_Configuration(void);
void SysTick_Handler(void);
/* Local Macros */
#define MEM8(addr) (*((volatile uint8_t *)(addr)))
#define MEM16(addr) (*((volatile uint16_t *)(addr)))
#define MEM32(addr) (*((volatile uint32_t *)(addr)))
/**
* Initialises all system peripherals
*/
void PIOS_SYS_Init(void)
{
/* Setup STM32 system (RCC, clock, PLL and Flash configuration) - CMSIS Function */
SystemInit();
/* Init the delay system */
PIOS_DELAY_Init();
/* Enable GPIOA, GPIOB, GPIOC, GPIOD, GPIOE and AFIO clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |
RCC_APB2Periph_AFIO, ENABLE);
#if (PIOS_USB_ENABLED)
/* Ensure that pull-up is active on detect pin */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = PIOS_USB_DETECT_GPIO_PIN;
GPIO_Init(PIOS_USB_DETECT_GPIO_PORT, &GPIO_InitStructure);
#endif
/* Initialise Basic NVIC */
NVIC_Configuration();
}
/**
* Shutdown PIOS and reset the microcontroller:<BR>
* <UL>
* <LI>Disable all RTOS tasks
* <LI>Disable all interrupts
* <LI>Turn off all board LEDs
* <LI>Reset STM32
* </UL>
* \return < 0 if reset failed
*/
int32_t PIOS_SYS_Reset(void)
{
/* Disable all RTOS tasks */
#if defined(PIOS_INCLUDE_FREERTOS)
/* port specific FreeRTOS function to disable tasks (nested) */
portENTER_CRITICAL();
#endif
// disable all interrupts
PIOS_IRQ_Disable();
// turn off all board LEDs
#if defined(PIOS_LED_HEARTBEAT)
PIOS_LED_Off(PIOS_LED_HEARTBEAT);
#endif /* PIOS_LED_HEARTBEAT */
#if defined(PIOS_LED_ALARM)
PIOS_LED_Off(PIOS_LED_ALARM);
#endif /* PIOS_LED_ALARM */
RCC_APB2PeriphResetCmd(0xffffffff, DISABLE);
RCC_APB1PeriphResetCmd(0xffffffff, DISABLE);
NVIC_SystemReset();
while (1) {
;
}
/* We will never reach this point */
return -1;
}
/**
* Returns the CPU's flash size (in bytes)
*/
uint32_t PIOS_SYS_getCPUFlashSize(void)
{
return (uint32_t)MEM16(0x1FFFF7E0) * 1000;
}
/**
* Returns the serial number as a string
* param[out] uint8_t pointer to a string which can store at least 12 bytes
* (12 bytes returned for STM32)
* return < 0 if feature not supported
*/
int32_t PIOS_SYS_SerialNumberGetBinary(uint8_t *array)
{
int i;
/* Stored in the so called "electronic signature" */
for (i = 0; i < PIOS_SYS_SERIAL_NUM_BINARY_LEN; ++i) {
uint8_t b = MEM8(0x1ffff7e8 + i);
array[i] = b;
}
/* No error */
return 0;
}
/**
* Returns the serial number as a string
* param[out] str pointer to a string which can store at least 32 digits + zero terminator!
* (24 digits returned for STM32)
* return < 0 if feature not supported
*/
int32_t PIOS_SYS_SerialNumberGet(char *str)
{
int i;
/* Stored in the so called "electronic signature" */
for (i = 0; i < PIOS_SYS_SERIAL_NUM_ASCII_LEN; ++i) {
uint8_t b = MEM8(0x1ffff7e8 + (i / 2));
if (!(i & 1)) {
b >>= 4;
}
b &= 0x0f;
str[i] = ((b > 9) ? ('A' - 10) : '0') + b;
}
str[i] = '\0';
/* No error */
return 0;
}
/**
* Configures Vector Table base location and SysTick
*/
void NVIC_Configuration(void)
{
/* Set the Vector Table base address as specified in .ld file */
extern void *pios_isr_vector_table_base;
NVIC_SetVectorTable((uint32_t)&pios_isr_vector_table_base, 0x0);
/* 4 bits for Interrupt priorities so no sub priorities */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/* Configure HCLK clock as SysTick clock source. */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
}
#ifdef USE_FULL_ASSERT
/**
* Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* \param[in] file pointer to the source file name
* \param[in] line assert_param error line source number
* \retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* When serial debugging is implemented, use something like this. */
/* printf("Wrong parameters value: file %s on line %d\r\n", file, line); */
/* Setup the LEDs to Alternate */
#if defined(PIOS_LED_HEARTBEAT)
PIOS_LED_On(PIOS_LED_HEARTBEAT);
#endif /* PIOS_LED_HEARTBEAT */
#if defined(PIOS_LED_ALARM)
PIOS_LED_Off(PIOS_LED_ALARM);
#endif /* PIOS_LED_ALARM */
/* Infinite loop */
while (1) {
#if defined(PIOS_LED_HEARTBEAT)
PIOS_LED_Toggle(PIOS_LED_HEARTBEAT);
#endif /* PIOS_LED_HEARTBEAT */
#if defined(PIOS_LED_ALARM)
PIOS_LED_Toggle(PIOS_LED_ALARM);
#endif /* PIOS_LED_ALARM */
for (int i = 0; i < 1000000; i++) {
;
}
}
}
#endif /* ifdef USE_FULL_ASSERT */
#endif /* PIOS_INCLUDE_SYS */
/**
* @}
* @}
*/

View File

@ -0,0 +1,430 @@
#include "pios.h"
#ifdef PIOS_INCLUDE_TIM
#include "pios_tim_priv.h"
enum pios_tim_dev_magic {
PIOS_TIM_DEV_MAGIC = 0x87654098,
};
struct pios_tim_dev {
enum pios_tim_dev_magic magic;
const struct pios_tim_channel *channels;
uint8_t num_channels;
const struct pios_tim_callbacks *callbacks;
uint32_t context;
};
#if 0
static bool PIOS_TIM_validate(struct pios_tim_dev *tim_dev)
{
return tim_dev->magic == PIOS_TIM_DEV_MAGIC;
}
#endif
#if defined(PIOS_INCLUDE_FREERTOS) && 0
static struct pios_tim_dev *PIOS_TIM_alloc(void)
{
struct pios_tim_dev *tim_dev;
tim_dev = (struct pios_tim_dev *)malloc(sizeof(*tim_dev));
if (!tim_dev) {
return NULL;
}
tim_dev->magic = PIOS_TIM_DEV_MAGIC;
return tim_dev;
}
#else
static struct pios_tim_dev pios_tim_devs[PIOS_TIM_MAX_DEVS];
static uint8_t pios_tim_num_devs;
static struct pios_tim_dev *PIOS_TIM_alloc(void)
{
struct pios_tim_dev *tim_dev;
if (pios_tim_num_devs >= PIOS_TIM_MAX_DEVS) {
return NULL;
}
tim_dev = &pios_tim_devs[pios_tim_num_devs++];
tim_dev->magic = PIOS_TIM_DEV_MAGIC;
return tim_dev;
}
#endif /* if defined(PIOS_INCLUDE_FREERTOS) && 0 */
int32_t PIOS_TIM_InitClock(const struct pios_tim_clock_cfg *cfg)
{
PIOS_DEBUG_Assert(cfg);
/* Enable appropriate clock to timer module */
switch ((uint32_t)cfg->timer) {
case (uint32_t)TIM1:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
break;
case (uint32_t)TIM2:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
break;
case (uint32_t)TIM3:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
break;
case (uint32_t)TIM4:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
break;
#ifdef STM32F10X_HD
case (uint32_t)TIM5:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
break;
case (uint32_t)TIM6:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
break;
case (uint32_t)TIM7:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
break;
case (uint32_t)TIM8:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
break;
#endif
}
/* Configure the dividers for this timer */
TIM_TimeBaseInit(cfg->timer, cfg->time_base_init);
/* Configure internal timer clocks */
TIM_InternalClockConfig(cfg->timer);
/* Enable timers */
TIM_Cmd(cfg->timer, ENABLE);
/* Enable Interrupts */
NVIC_Init(&cfg->irq.init);
return 0;
}
int32_t PIOS_TIM_InitChannels(uint32_t *tim_id, const struct pios_tim_channel *channels, uint8_t num_channels, const struct pios_tim_callbacks *callbacks, uint32_t context)
{
PIOS_Assert(channels);
PIOS_Assert(num_channels);
struct pios_tim_dev *tim_dev;
tim_dev = (struct pios_tim_dev *)PIOS_TIM_alloc();
if (!tim_dev) {
goto out_fail;
}
/* Bind the configuration to the device instance */
tim_dev->channels = channels;
tim_dev->num_channels = num_channels;
tim_dev->callbacks = callbacks;
tim_dev->context = context;
/* Configure the pins */
for (uint8_t i = 0; i < num_channels; i++) {
const struct pios_tim_channel *chan = &(channels[i]);
/* Enable the peripheral clock for the GPIO */
switch ((uint32_t)chan->pin.gpio) {
case (uint32_t)GPIOA:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
break;
case (uint32_t)GPIOB:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
break;
case (uint32_t)GPIOC:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
break;
default:
PIOS_Assert(0);
break;
}
GPIO_Init(chan->pin.gpio, &chan->pin.init);
if (chan->remap) {
GPIO_PinRemapConfig(chan->remap, ENABLE);
}
}
*tim_id = (uint32_t)tim_dev;
return 0;
out_fail:
return -1;
}
static void PIOS_TIM_generic_irq_handler(TIM_TypeDef *timer)
{
/* Iterate over all registered clients of the TIM layer to find channels on this timer */
for (uint8_t i = 0; i < pios_tim_num_devs; i++) {
const struct pios_tim_dev *tim_dev = &pios_tim_devs[i];
if (!tim_dev->channels || tim_dev->num_channels == 0) {
/* No channels to process on this client */
continue;
}
/* Check for an overflow event on this timer */
bool overflow_event;
uint16_t overflow_count;
if (TIM_GetITStatus(timer, TIM_IT_Update) == SET) {
TIM_ClearITPendingBit(timer, TIM_IT_Update);
overflow_count = timer->ARR;
overflow_event = true;
} else {
overflow_count = 0;
overflow_event = false;
}
for (uint8_t j = 0; j < tim_dev->num_channels; j++) {
const struct pios_tim_channel *chan = &tim_dev->channels[j];
if (chan->timer != timer) {
/* channel is not on this timer */
continue;
}
/* Figure out which interrupt bit we should be looking at */
uint16_t timer_it;
switch (chan->timer_chan) {
case TIM_Channel_1:
timer_it = TIM_IT_CC1;
break;
case TIM_Channel_2:
timer_it = TIM_IT_CC2;
break;
case TIM_Channel_3:
timer_it = TIM_IT_CC3;
break;
case TIM_Channel_4:
timer_it = TIM_IT_CC4;
break;
default:
PIOS_Assert(0);
break;
}
bool edge_event;
uint16_t edge_count;
if (TIM_GetITStatus(chan->timer, timer_it) == SET) {
TIM_ClearITPendingBit(chan->timer, timer_it);
/* Read the current counter */
switch (chan->timer_chan) {
case TIM_Channel_1:
edge_count = TIM_GetCapture1(chan->timer);
break;
case TIM_Channel_2:
edge_count = TIM_GetCapture2(chan->timer);
break;
case TIM_Channel_3:
edge_count = TIM_GetCapture3(chan->timer);
break;
case TIM_Channel_4:
edge_count = TIM_GetCapture4(chan->timer);
break;
default:
PIOS_Assert(0);
break;
}
edge_event = true;
} else {
edge_event = false;
edge_count = 0;
}
if (!tim_dev->callbacks) {
/* No callbacks registered, we're done with this channel */
continue;
}
/* Generate the appropriate callbacks */
if (overflow_event & edge_event) {
/*
* When both edge and overflow happen in the same interrupt, we
* need a heuristic to determine the order of the edge and overflow
* events so that the callbacks happen in the right order. If we
* get the order wrong, our pulse width calculations could be off by up
* to ARR ticks. That could be bad.
*
* Heuristic: If the edge_count is < 16 ticks above zero then we assume the
* edge happened just after the overflow.
*/
if (edge_count < 16) {
/* Call the overflow callback first */
if (tim_dev->callbacks->overflow) {
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
tim_dev->context,
j,
overflow_count);
}
/* Call the edge callback second */
if (tim_dev->callbacks->edge) {
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
tim_dev->context,
j,
edge_count);
}
} else {
/* Call the edge callback first */
if (tim_dev->callbacks->edge) {
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
tim_dev->context,
j,
edge_count);
}
/* Call the overflow callback second */
if (tim_dev->callbacks->overflow) {
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
tim_dev->context,
j,
overflow_count);
}
}
} else if (overflow_event && tim_dev->callbacks->overflow) {
(*tim_dev->callbacks->overflow)((uint32_t)tim_dev,
tim_dev->context,
j,
overflow_count);
} else if (edge_event && tim_dev->callbacks->edge) {
(*tim_dev->callbacks->edge)((uint32_t)tim_dev,
tim_dev->context,
j,
edge_count);
}
}
}
}
#if 0
uint16_t val = 0;
for (uint8_t i = 0; i < pios_pwm_cfg.num_channels; i++) {
struct pios_pwm_channel channel = pios_pwm_cfg.channels[i];
if ((channel.timer == timer) && (TIM_GetITStatus(channel.timer, channel.ccr) == SET)) {
TIM_ClearITPendingBit(channel.timer, channel.ccr);
switch (channel.channel) {
case TIM_Channel_1:
val = TIM_GetCapture1(channel.timer);
break;
case TIM_Channel_2:
val = TIM_GetCapture2(channel.timer);
break;
case TIM_Channel_3:
val = TIM_GetCapture3(channel.timer);
break;
case TIM_Channel_4:
val = TIM_GetCapture4(channel.timer);
break;
}
if (CaptureState[i] == 0) {
RiseValue[i] = val;
} else {
FallValue[i] = val;
}
// flip state machine and capture value here
/* Simple rise or fall state machine */
TIM_ICInitTypeDef TIM_ICInitStructure = pios_pwm_cfg.tim_ic_init;
if (CaptureState[i] == 0) {
/* Switch states */
CaptureState[i] = 1;
/* Switch polarity of input capture */
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStructure.TIM_Channel = channel.channel;
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
} else {
/* Capture computation */
if (FallValue[i] > RiseValue[i]) {
CaptureValue[i] = (FallValue[i] - RiseValue[i]);
} else {
CaptureValue[i] = ((channel.timer->ARR - RiseValue[i]) + FallValue[i]);
}
/* Switch states */
CaptureState[i] = 0;
/* Increase supervisor counter */
CapCounter[i]++;
/* Switch polarity of input capture */
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_Channel = channel.channel;
TIM_ICInit(channel.timer, &TIM_ICInitStructure);
}
}
}
#endif /* if 0 */
/* Bind Interrupt Handlers
*
* Map all valid TIM IRQs to the common interrupt handler
* and give it enough context to properly demux the various timers
*/
void TIM1_UP_IRQHandler(void) __attribute__((alias("PIOS_TIM_1_UP_irq_handler")));
static void PIOS_TIM_1_UP_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM1);
}
void TIM1_CC_IRQHandler(void) __attribute__((alias("PIOS_TIM_1_CC_irq_handler")));
static void PIOS_TIM_1_CC_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM1);
}
void TIM2_IRQHandler(void) __attribute__((alias("PIOS_TIM_2_irq_handler")));
static void PIOS_TIM_2_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM2);
}
void TIM3_IRQHandler(void) __attribute__((alias("PIOS_TIM_3_irq_handler")));
static void PIOS_TIM_3_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM3);
}
void TIM4_IRQHandler(void) __attribute__((alias("PIOS_TIM_4_irq_handler")));
static void PIOS_TIM_4_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM4);
}
void TIM5_IRQHandler(void) __attribute__((alias("PIOS_TIM_5_irq_handler")));
static void PIOS_TIM_5_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM5);
}
void TIM6_IRQHandler(void) __attribute__((alias("PIOS_TIM_6_irq_handler")));
static void PIOS_TIM_6_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM6);
}
void TIM7_IRQHandler(void) __attribute__((alias("PIOS_TIM_7_irq_handler")));
static void PIOS_TIM_7_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM7);
}
void TIM8_UP_IRQHandler(void) __attribute__((alias("PIOS_TIM_8_UP_irq_handler")));
static void PIOS_TIM_8_UP_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM8);
}
void TIM8_CC_IRQHandler(void) __attribute__((alias("PIOS_TIM_8_CC_irq_handler")));
static void PIOS_TIM_8_CC_irq_handler(void)
{
PIOS_TIM_generic_irq_handler(TIM8);
}
#endif /* PIOS_INCLUDE_TIM */

View File

@ -0,0 +1,344 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_USART USART Functions
* @brief PIOS interface for USART port
* @{
*
* @file pios_usart.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* @brief USART commands. Inits USARTs, controls USARTs & Interupt handlers. (STM32 dependent)
* @see The GNU Public License (GPL) Version 3
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_USART
#include <pios_usart_priv.h>
/* Provide a COM driver */
static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud);
static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context);
static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context);
static void PIOS_USART_TxStart(uint32_t usart_id, uint16_t tx_bytes_avail);
static void PIOS_USART_RxStart(uint32_t usart_id, uint16_t rx_bytes_avail);
const struct pios_com_driver pios_usart_com_driver = {
.set_baud = PIOS_USART_ChangeBaud,
.tx_start = PIOS_USART_TxStart,
.rx_start = PIOS_USART_RxStart,
.bind_tx_cb = PIOS_USART_RegisterTxCallback,
.bind_rx_cb = PIOS_USART_RegisterRxCallback,
};
enum pios_usart_dev_magic {
PIOS_USART_DEV_MAGIC = 0x11223344,
};
struct pios_usart_dev {
enum pios_usart_dev_magic magic;
const struct pios_usart_cfg *cfg;
pios_com_callback rx_in_cb;
uint32_t rx_in_context;
pios_com_callback tx_out_cb;
uint32_t tx_out_context;
uint32_t rx_dropped;
};
static bool PIOS_USART_validate(struct pios_usart_dev *usart_dev)
{
return usart_dev->magic == PIOS_USART_DEV_MAGIC;
}
#if defined(PIOS_INCLUDE_FREERTOS)
static struct pios_usart_dev *PIOS_USART_alloc(void)
{
struct pios_usart_dev *usart_dev;
usart_dev = (struct pios_usart_dev *)pvPortMalloc(sizeof(struct pios_usart_dev));
if (!usart_dev) {
return NULL;
}
memset(usart_dev, 0, sizeof(struct pios_usart_dev));
usart_dev->magic = PIOS_USART_DEV_MAGIC;
return usart_dev;
}
#else
static struct pios_usart_dev pios_usart_devs[PIOS_USART_MAX_DEVS];
static uint8_t pios_usart_num_devs;
static struct pios_usart_dev *PIOS_USART_alloc(void)
{
struct pios_usart_dev *usart_dev;
if (pios_usart_num_devs >= PIOS_USART_MAX_DEVS) {
return NULL;
}
usart_dev = &pios_usart_devs[pios_usart_num_devs++];
memset(usart_dev, 0, sizeof(struct pios_usart_dev));
usart_dev->magic = PIOS_USART_DEV_MAGIC;
return usart_dev;
}
#endif /* if defined(PIOS_INCLUDE_FREERTOS) */
/* Bind Interrupt Handlers
*
* Map all valid USART IRQs to the common interrupt handler
* and provide storage for a 32-bit device id IRQ to map
* each physical IRQ to a specific registered device instance.
*/
static void PIOS_USART_generic_irq_handler(uint32_t usart_id);
static uint32_t PIOS_USART_1_id;
void USART1_IRQHandler(void) __attribute__((alias("PIOS_USART_1_irq_handler")));
static void PIOS_USART_1_irq_handler(void)
{
PIOS_USART_generic_irq_handler(PIOS_USART_1_id);
}
static uint32_t PIOS_USART_2_id;
void USART2_IRQHandler(void) __attribute__((alias("PIOS_USART_2_irq_handler")));
static void PIOS_USART_2_irq_handler(void)
{
PIOS_USART_generic_irq_handler(PIOS_USART_2_id);
}
static uint32_t PIOS_USART_3_id;
void USART3_IRQHandler(void) __attribute__((alias("PIOS_USART_3_irq_handler")));
static void PIOS_USART_3_irq_handler(void)
{
PIOS_USART_generic_irq_handler(PIOS_USART_3_id);
}
/**
* Initialise a single USART device
*/
int32_t PIOS_USART_Init(uint32_t *usart_id, const struct pios_usart_cfg *cfg)
{
PIOS_DEBUG_Assert(usart_id);
PIOS_DEBUG_Assert(cfg);
struct pios_usart_dev *usart_dev;
usart_dev = (struct pios_usart_dev *)PIOS_USART_alloc();
if (!usart_dev) {
goto out_fail;
}
/* Bind the configuration to the device instance */
usart_dev->cfg = cfg;
/* Enable the USART Pins Software Remapping */
if (usart_dev->cfg->remap) {
GPIO_PinRemapConfig(usart_dev->cfg->remap, ENABLE);
}
/* Initialize the USART Rx and Tx pins */
GPIO_Init(usart_dev->cfg->rx.gpio, &usart_dev->cfg->rx.init);
GPIO_Init(usart_dev->cfg->tx.gpio, &usart_dev->cfg->tx.init);
/* Enable USART clock */
switch ((uint32_t)usart_dev->cfg->regs) {
case (uint32_t)USART1:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
break;
case (uint32_t)USART2:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
break;
case (uint32_t)USART3:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
break;
}
/* Configure the USART */
USART_Init(usart_dev->cfg->regs, &usart_dev->cfg->init);
*usart_id = (uint32_t)usart_dev;
/* Configure USART Interrupts */
switch ((uint32_t)usart_dev->cfg->regs) {
case (uint32_t)USART1:
PIOS_USART_1_id = (uint32_t)usart_dev;
break;
case (uint32_t)USART2:
PIOS_USART_2_id = (uint32_t)usart_dev;
break;
case (uint32_t)USART3:
PIOS_USART_3_id = (uint32_t)usart_dev;
break;
}
NVIC_Init(&usart_dev->cfg->irq.init);
USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
/* Enable USART */
USART_Cmd(usart_dev->cfg->regs, ENABLE);
return 0;
out_fail:
return -1;
}
static void PIOS_USART_RxStart(uint32_t usart_id, __attribute__((unused)) uint16_t rx_bytes_avail)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
USART_ITConfig(usart_dev->cfg->regs, USART_IT_RXNE, ENABLE);
}
static void PIOS_USART_TxStart(uint32_t usart_id, __attribute__((unused)) uint16_t tx_bytes_avail)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, ENABLE);
}
/**
* Changes the baud rate of the USART peripheral without re-initialising.
* \param[in] usart_id USART name (GPS, TELEM, AUX)
* \param[in] baud Requested baud rate
*/
static void PIOS_USART_ChangeBaud(uint32_t usart_id, uint32_t baud)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
USART_InitTypeDef USART_InitStructure;
/* Start with a copy of the default configuration for the peripheral */
USART_InitStructure = usart_dev->cfg->init;
/* Adjust the baud rate */
USART_InitStructure.USART_BaudRate = baud;
/* Write back the new configuration */
USART_Init(usart_dev->cfg->regs, &USART_InitStructure);
}
static void PIOS_USART_RegisterRxCallback(uint32_t usart_id, pios_com_callback rx_in_cb, uint32_t context)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
/*
* Order is important in these assignments since ISR uses _cb
* field to determine if it's ok to dereference _cb and _context
*/
usart_dev->rx_in_context = context;
usart_dev->rx_in_cb = rx_in_cb;
}
static void PIOS_USART_RegisterTxCallback(uint32_t usart_id, pios_com_callback tx_out_cb, uint32_t context)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
/*
* Order is important in these assignments since ISR uses _cb
* field to determine if it's ok to dereference _cb and _context
*/
usart_dev->tx_out_context = context;
usart_dev->tx_out_cb = tx_out_cb;
}
static void PIOS_USART_generic_irq_handler(uint32_t usart_id)
{
struct pios_usart_dev *usart_dev = (struct pios_usart_dev *)usart_id;
bool valid = PIOS_USART_validate(usart_dev);
PIOS_Assert(valid);
/* Force read of dr after sr to make sure to clear error flags */
volatile uint16_t sr = usart_dev->cfg->regs->SR;
volatile uint8_t dr = usart_dev->cfg->regs->DR;
/* Check if RXNE flag is set */
bool rx_need_yield = false;
if (sr & USART_SR_RXNE) {
uint8_t byte = dr;
if (usart_dev->rx_in_cb) {
uint16_t rc;
rc = (usart_dev->rx_in_cb)(usart_dev->rx_in_context, &byte, 1, NULL, &rx_need_yield);
if (rc < 1) {
/* Lost bytes on rx */
usart_dev->rx_dropped += 1;
}
}
}
/* Check if TXE flag is set */
bool tx_need_yield = false;
if (sr & USART_SR_TXE) {
if (usart_dev->tx_out_cb) {
uint8_t b;
uint16_t bytes_to_send;
bytes_to_send = (usart_dev->tx_out_cb)(usart_dev->tx_out_context, &b, 1, NULL, &tx_need_yield);
if (bytes_to_send > 0) {
/* Send the byte we've been given */
usart_dev->cfg->regs->DR = b;
} else {
/* No bytes to send, disable TXE interrupt */
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
}
} else {
/* No bytes to send, disable TXE interrupt */
USART_ITConfig(usart_dev->cfg->regs, USART_IT_TXE, DISABLE);
}
}
#if defined(PIOS_INCLUDE_FREERTOS)
if (rx_need_yield || tx_need_yield) {
vPortYield();
}
#endif /* PIOS_INCLUDE_FREERTOS */
}
#endif /* PIOS_INCLUDE_USART */
/**
* @}
* @}
*/

View File

@ -0,0 +1,182 @@
/**
******************************************************************************
* @addtogroup PIOS PIOS Core hardware abstraction layer
* @{
* @addtogroup PIOS_WDG Watchdog Functions
* @brief PIOS Comamnds to initialize and clear watchdog timer
* @{
*
* @file pios_spi.c
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
* Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org)
* @brief Hardware Abstraction Layer for SPI ports of STM32
* @see The GNU Public License (GPL) Version 3
* @notes
*
* The PIOS Watchdog provides a HAL to initialize a watchdog
*
*****************************************************************************/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "pios.h"
#ifdef PIOS_INCLUDE_WDG
#include "stm32f10x_iwdg.h"
#include "stm32f10x_dbgmcu.h"
static struct wdg_configuration {
uint16_t used_flags;
uint16_t bootup_flags;
} wdg_configuration;
/**
* @brief Initialize the watchdog timer for a specified timeout
*
* It is important to note that this function returns the achieved timeout
* for this hardware. For hardware indendence this should be checked when
* scheduling updates. Other hardware dependent details may need to be
* considered such as a window time which sets a minimum update time,
* and this function should return a recommended delay for clearing.
*
* For the STM32 nominal clock rate is 32 khz, but for the maximum clock rate of
* 60 khz and a prescalar of 4 yields a clock rate of 15 khz. The delay that is
* set in the watchdog assumes the nominal clock rate, but the delay for FreeRTOS
* to use is 75% of the minimal delay.
*
* @returns Maximum recommended delay between updates based on PIOS_WATCHDOG_TIMEOUT constant
*/
uint16_t PIOS_WDG_Init()
{
uint16_t delay = ((uint32_t)PIOS_WATCHDOG_TIMEOUT * 60) / 16;
if (delay > 0x0fff) {
delay = 0x0fff;
}
#if defined(PIOS_INCLUDE_WDG)
DBGMCU_Config(DBGMCU_IWDG_STOP, ENABLE); // make the watchdog stop counting in debug mode
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_16);
IWDG_SetReload(delay);
IWDG_ReloadCounter();
IWDG_Enable();
// watchdog flags now stored in backup registers
PWR_BackupAccessCmd(ENABLE);
BKP_WriteBackupRegister(PIOS_WDG_REGISTER, 0x0);
wdg_configuration.bootup_flags = BKP_ReadBackupRegister(PIOS_WDG_REGISTER);
#endif
return delay;
}
/**
* @brief Register a module against the watchdog
*
* There are two ways to use PIOS WDG: this is for when
* multiple modules must be monitored. In this case they
* must first register against the watchdog system and
* only when all of the modules have been updated with the
* watchdog be cleared. Each module must have its own
* bit in the 16 bit
*
* @param[in] flag the bit this module wants to use
* @returns True if that bit is unregistered
*/
bool PIOS_WDG_RegisterFlag(uint16_t flag_requested)
{
// flag are being registered so we are in module initialization phase
// clear the WDG to prevent timeout while initializing modules. (OP-815)
PIOS_WDG_Clear();
/* Fail if flag already registered */
if (wdg_configuration.used_flags & flag_requested) {
return false;
}
// FIXME: Protect with semaphore
wdg_configuration.used_flags |= flag_requested;
return true;
}
/**
* @brief Function called by modules to indicate they are still running
*
* This function will set this flag in the active flags register (which is
* a backup regsiter) and if all the registered flags are set will clear
* the watchdog and set only this flag in the backup register
*
* @param[in] flag the flag to set
* @return true if the watchdog cleared, false if flags are pending
*/
bool PIOS_WDG_UpdateFlag(uint16_t flag)
{
// we can probably avoid using a semaphore here which will be good for
// efficiency and not blocking critical tasks. race condition could
// overwrite their flag update, but unlikely to block _all_ of them
// for the timeout window
uint16_t cur_flags = BKP_ReadBackupRegister(PIOS_WDG_REGISTER);
if ((cur_flags | flag) == wdg_configuration.used_flags) {
PIOS_WDG_Clear();
BKP_WriteBackupRegister(PIOS_WDG_REGISTER, flag);
return true;
} else {
BKP_WriteBackupRegister(PIOS_WDG_REGISTER, cur_flags | flag);
return false;
}
}
/**
* @brief Returns the flags that were set at bootup
*
* This is used for diagnostics, if only one flag not set this
* was likely the module that wasn't running before reset
*
* @return The active flags register from bootup
*/
uint16_t PIOS_WDG_GetBootupFlags()
{
return wdg_configuration.bootup_flags;
}
/**
* @brief Returns the currently active flags
*
* For external monitoring
*
* @return The active flags register
*/
uint16_t PIOS_WDG_GetActiveFlags()
{
return BKP_ReadBackupRegister(PIOS_WDG_REGISTER);
}
/**
* @brief Clear the watchdog timer
*
* This function must be called at the appropriate delay to prevent a reset event occuring
*/
void PIOS_WDG_Clear(void)
{
#if defined(PIOS_INCLUDE_WDG)
IWDG_ReloadCounter();
#endif
}
#endif /* PIOS_INCLUDE_WDG */