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:
parent
7eca9f80e3
commit
5a60c254ed
391
flight/pios/stm32f0x/pios_adc.c
Normal file
391
flight/pios/stm32f0x/pios_adc.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
102
flight/pios/stm32f0x/pios_bkp.c
Normal file
102
flight/pios/stm32f0x/pios_bkp.c
Normal 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
|
||||
****************************************************************************************/
|
131
flight/pios/stm32f0x/pios_bl_helper.c
Normal file
131
flight/pios/stm32f0x/pios_bl_helper.c
Normal 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 */
|
171
flight/pios/stm32f0x/pios_debug.c
Normal file
171
flight/pios/stm32f0x/pios_debug.c
Normal 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) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
176
flight/pios/stm32f0x/pios_delay.c
Normal file
176
flight/pios/stm32f0x/pios_delay.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
164
flight/pios/stm32f0x/pios_eeprom.c
Normal file
164
flight/pios/stm32f0x/pios_eeprom.c
Normal 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 */
|
331
flight/pios/stm32f0x/pios_exti.c
Normal file
331
flight/pios/stm32f0x/pios_exti.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
304
flight/pios/stm32f0x/pios_flash_internal.c
Normal file
304
flight/pios/stm32f0x/pios_flash_internal.c
Normal 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,
|
||||
§or_number,
|
||||
§or_start,
|
||||
§or_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,
|
||||
§or_number,
|
||||
§or_start,
|
||||
§or_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,
|
||||
§or_number,
|
||||
§or_start,
|
||||
§or_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 */
|
161
flight/pios/stm32f0x/pios_gpio.c
Normal file
161
flight/pios/stm32f0x/pios_gpio.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
1170
flight/pios/stm32f0x/pios_i2c.c
Normal file
1170
flight/pios/stm32f0x/pios_i2c.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
97
flight/pios/stm32f0x/pios_irq.c
Normal file
97
flight/pios/stm32f0x/pios_irq.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
284
flight/pios/stm32f0x/pios_pwm.c
Normal file
284
flight/pios/stm32f0x/pios_pwm.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
130
flight/pios/stm32f0x/pios_rtc.c
Normal file
130
flight/pios/stm32f0x/pios_rtc.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
659
flight/pios/stm32f0x/pios_spi.c
Normal file
659
flight/pios/stm32f0x/pios_spi.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
227
flight/pios/stm32f0x/pios_sys.c
Normal file
227
flight/pios/stm32f0x/pios_sys.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
430
flight/pios/stm32f0x/pios_tim.c
Normal file
430
flight/pios/stm32f0x/pios_tim.c
Normal 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 */
|
344
flight/pios/stm32f0x/pios_usart.c
Normal file
344
flight/pios/stm32f0x/pios_usart.c
Normal 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 */
|
||||
|
||||
/**
|
||||
* @}
|
||||
* @}
|
||||
*/
|
182
flight/pios/stm32f0x/pios_wdg.c
Normal file
182
flight/pios/stm32f0x/pios_wdg.c
Normal 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 */
|
Loading…
Reference in New Issue
Block a user