blob: b19f1ecf4f5e54d56ae3da8612910325b8f0a13c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <hal/hal_bsp.h>
#include <assert.h>
#include "os/mynewt.h"
#include <mcu/cmsis_nvic.h>
#include "stm32f4xx_hal_dma.h"
#include "stm32f4xx_hal_adc.h"
#include "stm32f4xx_hal_rcc.h"
#include "stm32f4xx_hal_cortex.h"
#include "stm32f4xx_hal.h"
#include "adc_stm32f4/adc_stm32f4.h"
#include "stm32f4xx_hal_dma.h"
#include "mcu/stm32f4xx_mynewt_hal.h"
#if MYNEWT_VAL(ADC_1)||MYNEWT_VAL(ADC_2)||MYNEWT_VAL(ADC_3)
#include <adc/adc.h>
#endif
#define STM32F4_IS_DMA_ADC_CHANNEL(CHANNEL) ((CHANNEL) <= DMA_CHANNEL_2)
static DMA_HandleTypeDef *dma_handle[5];
static struct adc_dev *adc_dma[5];
struct stm32f4_adc_stats {
uint16_t adc_events;
uint16_t adc_error;
uint16_t adc_dma_xfer_failed;
uint16_t adc_dma_xfer_aborted;
uint16_t adc_dma_xfer_complete;
uint16_t adc_dma_start_error;
uint16_t adc_dma_overrun;
uint16_t adc_internal_error;
};
static struct stm32f4_adc_stats stm32f4_adc_stats;
static void
stm32f4_adc_clk_enable(ADC_HandleTypeDef *hadc)
{
uintptr_t adc_addr = (uintptr_t)hadc->Instance;
switch (adc_addr) {
#if defined(ADC1)
case (uintptr_t)ADC1:
__HAL_RCC_ADC1_CLK_ENABLE();
break;
#endif
#if defined(ADC2)
case (uintptr_t)ADC2:
__HAL_RCC_ADC2_CLK_ENABLE();
break;
#endif
#if defined(ADC3)
case (uintptr_t)ADC3:
__HAL_RCC_ADC3_CLK_ENABLE();
break;
#endif
default:
assert(0);
}
}
static void
stm32f4_adc_clk_disable(ADC_HandleTypeDef *hadc)
{
uintptr_t adc_addr = (uintptr_t)hadc->Instance;
switch (adc_addr) {
#if defined(ADC1)
case (uintptr_t)ADC1:
__HAL_RCC_ADC1_CLK_DISABLE();
break;
#endif
#if defined(ADC2)
case (uintptr_t)ADC2:
__HAL_RCC_ADC2_CLK_DISABLE();
break;
#endif
#if defined(ADC3)
case (uintptr_t)ADC3:
__HAL_RCC_ADC3_CLK_DISABLE();
break;
#endif
default:
assert(0);
}
}
static int
stm32f4_resolve_adc_gpio(ADC_HandleTypeDef *adc, uint8_t cnum,
GPIO_InitTypeDef *gpio)
{
uintptr_t adc_addr = (uintptr_t)adc->Instance;
uint32_t pin;
int rc;
rc = OS_OK;
switch (adc_addr) {
#if defined(ADC1) || defined(ADC2)
#if defined(ADC1)
case (uintptr_t)ADC1:
#endif
#if defined(ADC2)
case (uintptr_t)ADC2:
#endif
switch(cnum) {
case ADC_CHANNEL_4:
pin = ADC12_CH4_PIN;
goto done;
case ADC_CHANNEL_5:
pin = ADC12_CH5_PIN;
goto done;
case ADC_CHANNEL_6:
pin = ADC12_CH6_PIN;
goto done;
case ADC_CHANNEL_7:
pin = ADC12_CH7_PIN;
goto done;
case ADC_CHANNEL_8:
pin = ADC12_CH8_PIN;
goto done;
case ADC_CHANNEL_9:
pin = ADC12_CH9_PIN;
goto done;
case ADC_CHANNEL_14:
pin = ADC12_CH14_PIN;
goto done;
case ADC_CHANNEL_15:
pin = ADC12_CH15_PIN;
goto done;
}
#endif
/*
* Falling through intentionally as ADC_3 contains seperate pins for
* Channels that ADC_1 and ADC_2 contain as well.
*/
#if defined(ADC3)
case (uintptr_t)ADC3:
switch(cnum) {
case ADC_CHANNEL_0:
pin = ADC123_CH0_PIN;
goto done;
case ADC_CHANNEL_1:
pin = ADC123_CH1_PIN;
goto done;
case ADC_CHANNEL_2:
pin = ADC123_CH2_PIN;
goto done;
case ADC_CHANNEL_3:
pin = ADC123_CH3_PIN;
goto done;
case ADC_CHANNEL_4:
pin = ADC3_CH4_PIN;
goto done;
case ADC_CHANNEL_5:
pin = ADC3_CH5_PIN;
goto done;
case ADC_CHANNEL_6:
pin = ADC3_CH6_PIN;
goto done;
case ADC_CHANNEL_7:
pin = ADC3_CH7_PIN;
goto done;
case ADC_CHANNEL_8:
pin = ADC3_CH8_PIN;
goto done;
case ADC_CHANNEL_9:
pin = ADC3_CH9_PIN;
goto done;
case ADC_CHANNEL_10:
pin = ADC123_CH10_PIN;
goto done;
case ADC_CHANNEL_11:
pin = ADC123_CH11_PIN;
goto done;
case ADC_CHANNEL_12:
pin = ADC123_CH12_PIN;
goto done;
case ADC_CHANNEL_13:
pin = ADC123_CH13_PIN;
goto done;
case ADC_CHANNEL_14:
pin = ADC3_CH14_PIN;
goto done;
case ADC_CHANNEL_15:
pin = ADC3_CH15_PIN;
goto done;
}
#endif
default:
rc = OS_EINVAL;
return rc;
}
done:
*gpio = (GPIO_InitTypeDef) {
.Pin = pin,
.Mode = GPIO_MODE_ANALOG,
.Pull = GPIO_NOPULL,
.Alternate = pin
};
return rc;
}
static IRQn_Type
stm32f4_resolve_adc_dma_irq(DMA_HandleTypeDef *hdma)
{
uintptr_t stream_addr = (uintptr_t)hdma->Instance;
assert(STM32F4_IS_DMA_ADC_CHANNEL(hdma->Init.Channel));
switch(stream_addr) {
/* DMA2 */
case (uintptr_t)DMA2_Stream0:
return DMA2_Stream0_IRQn;
case (uintptr_t)DMA2_Stream1:
return DMA2_Stream1_IRQn;
case (uintptr_t)DMA2_Stream2:
return DMA2_Stream2_IRQn;
case (uintptr_t)DMA2_Stream3:
return DMA2_Stream3_IRQn;
case (uintptr_t)DMA2_Stream4:
return DMA2_Stream4_IRQn;
default:
assert(0);
}
}
static void
dma2_stream0_irq_handler(void)
{
HAL_DMA_IRQHandler(dma_handle[0]);
}
static void
dma2_stream1_irq_handler(void)
{
HAL_DMA_IRQHandler(dma_handle[1]);
}
static void
dma2_stream2_irq_handler(void)
{
HAL_DMA_IRQHandler(dma_handle[2]);
}
static void
dma2_stream3_irq_handler(void)
{
HAL_DMA_IRQHandler(dma_handle[3]);
}
static void
dma2_stream4_irq_handler(void)
{
HAL_DMA_IRQHandler(dma_handle[4]);
}
uint32_t
stm32f4_resolve_adc_dma_irq_handler(DMA_HandleTypeDef *hdma)
{
switch((uintptr_t)hdma->Instance) {
/* DMA2 */
case (uintptr_t)DMA2_Stream0:
return (uint32_t)&dma2_stream0_irq_handler;
case (uintptr_t)DMA2_Stream1:
return (uint32_t)&dma2_stream1_irq_handler;
case (uintptr_t)DMA2_Stream2:
return (uint32_t)&dma2_stream2_irq_handler;
case (uintptr_t)DMA2_Stream3:
return (uint32_t)&dma2_stream3_irq_handler;
case (uintptr_t)DMA2_Stream4:
return (uint32_t)&dma2_stream4_irq_handler;
default:
assert(0);
}
}
static int
stm32f4_resolve_dma_handle_idx(DMA_HandleTypeDef *hdma)
{
uintptr_t stream_addr = (uintptr_t)hdma->Instance;
return ((stream_addr & 0xFF) - ((uintptr_t)DMA2_Stream0_BASE & 0xFF))/0x18;
}
void
HAL_ADC_ErrorCallback(ADC_HandleTypeDef *hadc)
{
++stm32f4_adc_stats.adc_error;
if (hadc->ErrorCode & HAL_ADC_ERROR_DMA) {
/* DMA transfer error */
++stm32f4_adc_stats.adc_dma_xfer_failed;
} else if (hadc->ErrorCode & HAL_ADC_ERROR_OVR) {
/* DMA transfer overrun */
++stm32f4_adc_stats.adc_dma_overrun;
} else if (hadc->ErrorCode & HAL_ADC_ERROR_INTERNAL) {
/* ADC IP Internal Error */
++stm32f4_adc_stats.adc_internal_error;
}
}
/**
* Callback that gets called by the HAL when ADC conversion is complete and
* the DMA buffer is full. If a secondary buffer exists it will the buffers.
*
* @param ADC Handle
*/
void
HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
int rc;
struct adc_dev *adc;
DMA_HandleTypeDef *hdma;
struct stm32f4_adc_dev_cfg *cfg;
void *buf;
assert(hadc);
hdma = hadc->DMA_Handle;
++stm32f4_adc_stats.adc_dma_xfer_complete;
adc = adc_dma[stm32f4_resolve_dma_handle_idx(hdma)];
cfg = (struct stm32f4_adc_dev_cfg *)adc->ad_dev.od_init_arg;
buf = cfg->primarybuf;
/**
* If primary buffer gets full and secondary buffer exists, swap the
* buffers and start ADC conversion with DMA with the now primary
* buffer(former secondary buffer)
* If the secondary buffer(former primary buffer) doesn't get processed
* by the application in sampling period required for the primary/secondary buffer
* i,e; (sample itvl * ADC_NUMBER_SAMPLES), the buffers would get swapped resulting
* in new sample data.
*/
if (cfg->secondarybuf) {
cfg->primarybuf = cfg->secondarybuf;
cfg->secondarybuf = buf;
if (HAL_ADC_Start_DMA(hadc, cfg->primarybuf, cfg->buflen) != HAL_OK) {
++stm32f4_adc_stats.adc_dma_start_error;
}
}
rc = adc->ad_event_handler_func(adc, NULL, ADC_EVENT_RESULT, buf,
cfg->buflen);
if (rc) {
++stm32f4_adc_stats.adc_error;
}
}
static void
stm32f4_adc_dma_init(ADC_HandleTypeDef* hadc)
{
DMA_HandleTypeDef *hdma;
assert(hadc);
hdma = hadc->DMA_Handle;
stm32f4_adc_clk_enable(hadc);
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_DMA_Init(hdma);
dma_handle[stm32f4_resolve_dma_handle_idx(hdma)] = hdma;
NVIC_SetPriority(stm32f4_resolve_adc_dma_irq(hdma),
NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
NVIC_SetVector(stm32f4_resolve_adc_dma_irq(hdma),
stm32f4_resolve_adc_dma_irq_handler(hdma));
NVIC_EnableIRQ(stm32f4_resolve_adc_dma_irq(hdma));
}
static void
stm32f4_adc_init(struct adc_dev *dev)
{
struct stm32f4_adc_dev_cfg *adc_config;
ADC_HandleTypeDef *hadc;
assert(dev);
adc_config = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = adc_config->sac_adc_handle;
stm32f4_adc_dma_init(hadc);
if (HAL_ADC_Init(hadc) != HAL_OK) {
assert(0);
}
}
static void
stm32f4_adc_uninit(struct adc_dev *dev)
{
GPIO_InitTypeDef gpio_td;
DMA_HandleTypeDef *hdma;
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
uint8_t cnum;
assert(dev);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
hdma = hadc->DMA_Handle;
cnum = dev->ad_chans->c_cnum;
__HAL_RCC_DMA2_CLK_DISABLE();
if (HAL_DMA_DeInit(hdma) != HAL_OK) {
assert(0);
}
stm32f4_adc_clk_disable(hadc);
NVIC_DisableIRQ(stm32f4_resolve_adc_dma_irq(hdma));
if (stm32f4_resolve_adc_gpio(hadc, cnum, &gpio_td)) {
goto err;
}
if (hal_gpio_deinit_stm(gpio_td.Pin, &gpio_td)) {
goto err;
}
err:
return;
}
/**
* Open the STM32F4 ADC device
*
* This function locks the device for access from other tasks.
*
* @param odev The OS device to open
* @param wait The time in MS to wait. If 0 specified, returns immediately
* if resource unavailable. If OS_WAIT_FOREVER specified, blocks
* until resource is available.
* @param arg Argument provided by higher layer to open.
*
* @return 0 on success, non-zero on failure.
*/
static int
stm32f4_adc_open(struct os_dev *odev, uint32_t wait, void *arg)
{
DMA_HandleTypeDef *hdma;
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
struct adc_dev *dev;
int rc;
assert(odev);
rc = OS_OK;
dev = (struct adc_dev *) odev;
if (os_started()) {
rc = os_mutex_pend(&dev->ad_lock, wait);
if (rc != OS_OK) {
goto err;
}
}
if (odev->od_flags & OS_DEV_F_STATUS_OPEN) {
os_mutex_release(&dev->ad_lock);
rc = OS_EBUSY;
goto err;
}
stm32f4_adc_init(dev);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
hdma = hadc->DMA_Handle;
adc_dma[stm32f4_resolve_dma_handle_idx(hdma)] = dev;
return (OS_OK);
err:
return (rc);
}
/**
* Close the STM32F4 ADC device.
*
* This function unlocks the device.
*
* @param odev The device to close.
*/
static int
stm32f4_adc_close(struct os_dev *odev)
{
struct adc_dev *dev;
dev = (struct adc_dev *) odev;
stm32f4_adc_uninit(dev);
if (os_started()) {
os_mutex_release(&dev->ad_lock);
}
return (OS_OK);
}
/**
* Configure an ADC channel on the STM32F4 ADC.
*
* @param dev The ADC device to configure
* @param cnum The channel on the ADC device to configure
* @param cfgdata An opaque pointer to channel config, expected to be
* a ADC_ChannelConfTypeDef
*
* @return 0 on success, non-zero on failure.
*/
static int
stm32f4_adc_configure_channel(struct adc_dev *dev, uint8_t cnum,
void *cfgdata)
{
int rc;
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
struct adc_chan_config *chan_cfg;
GPIO_InitTypeDef gpio_td;
rc = OS_EINVAL;
if (dev == NULL && !IS_ADC_CHANNEL(cnum)) {
goto err;
}
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
chan_cfg = &((struct adc_chan_config *)cfg->sac_chans)[cnum];
cfgdata = (ADC_ChannelConfTypeDef *)cfgdata;
if ((HAL_ADC_ConfigChannel(hadc, cfgdata)) != HAL_OK) {
goto err;
}
dev->ad_chans[cnum].c_res = chan_cfg->c_res;
dev->ad_chans[cnum].c_refmv = chan_cfg->c_refmv;
dev->ad_chans[cnum].c_configured = 1;
dev->ad_chans[cnum].c_cnum = cnum;
if (stm32f4_resolve_adc_gpio(hadc, cnum, &gpio_td)) {
goto err;
}
hal_gpio_init_stm(gpio_td.Pin, &gpio_td);
return (OS_OK);
err:
return (rc);
}
/**
* Set buffer to read data into. Implementation of setbuffer handler.
* Sets both the primary and secondary buffers for DMA.
*
* For our current implementation we are using DMA in circular mode
*
*/
static int
stm32f4_adc_set_buffer(struct adc_dev *dev, void *buf1, void *buf2,
int buflen)
{
struct stm32f4_adc_dev_cfg *cfg;
int rc;
assert(dev != NULL && buf1 != NULL);
rc = OS_OK;
buflen /= sizeof(uint32_t);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
cfg->primarybuf = buf1;
cfg->secondarybuf = buf2;
cfg->buflen = buflen;
return rc;
}
static int
stm32f4_adc_release_buffer(struct adc_dev *dev, void *buf, int buf_len)
{
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
assert(dev);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
HAL_ADC_Stop_DMA(hadc);
return (0);
}
/**
* Trigger an ADC sample.
*
* @param ADC device structure
* @return OS_OK on success, non OS_OK on failure
*/
static int
stm32f4_adc_sample(struct adc_dev *dev)
{
int rc;
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
assert(dev);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
rc = OS_EINVAL;
if (HAL_ADC_Start_DMA(hadc, cfg->primarybuf, cfg->buflen) != HAL_OK) {
++stm32f4_adc_stats.adc_dma_start_error;
goto err;
}
rc = OS_OK;
err:
return rc;
}
/**
* Blocking read of an ADC channel, returns result as an integer.
*
* @param1 ADC device structure
* @param2 channel number
* @param3 ADC result ptr
*/
static int
stm32f4_adc_read_channel(struct adc_dev *dev, uint8_t cnum, int *result)
{
ADC_HandleTypeDef *hadc;
struct stm32f4_adc_dev_cfg *cfg;
assert(dev != NULL && result != NULL);
cfg = (struct stm32f4_adc_dev_cfg *)dev->ad_dev.od_init_arg;
hadc = cfg->sac_adc_handle;
*result = HAL_ADC_GetValue(hadc);
return (OS_OK);
}
static int
stm32f4_adc_read_buffer(struct adc_dev *dev, void *buf, int buf_len, int off,
int *result)
{
assert(off < buf_len);
/*
* If secondary buffer exists the primary buf is going to be cached
* in the secondary buffer if the primary buffer is full and we
* would be reading that instead since the buffer is specified by
* the application
*/
*result = *((uint32_t *)buf + off);
return (OS_OK);
}
/**
* Callback to return size of buffer
*
* @param1 ADC device ptr
* @param2 Total number of channels
* @param3 Total number of samples
* @return Length of buffer in bytes
*/
static int
stm32f4_adc_size_buffer(struct adc_dev *dev, int chans, int samples)
{
return (sizeof(uint32_t) * chans * samples);
}
/**
* ADC device driver functions
*/
static const struct adc_driver_funcs stm32f4_adc_funcs = {
.af_configure_channel = stm32f4_adc_configure_channel,
.af_sample = stm32f4_adc_sample,
.af_read_channel = stm32f4_adc_read_channel,
.af_set_buffer = stm32f4_adc_set_buffer,
.af_release_buffer = stm32f4_adc_release_buffer,
.af_read_buffer = stm32f4_adc_read_buffer,
.af_size_buffer = stm32f4_adc_size_buffer,
};
/**
* Callback to initialize an adc_dev structure from the os device
* initialization callback. This sets up a stm32f4_adc_device(), so
* that subsequent lookups to this device allow us to manipulate it.
*
* @param1 os device ptr
* @param2 stm32f4 ADC device cfg ptr
* @return OS_OK on success
*/
int
stm32f4_adc_dev_init(struct os_dev *odev, void *arg)
{
struct stm32f4_adc_dev_cfg *sac;
struct adc_dev *dev;
sac = (struct stm32f4_adc_dev_cfg *) arg;
assert(sac != NULL);
dev = (struct adc_dev *)odev;
os_mutex_init(&dev->ad_lock);
dev->ad_chans = (void *) sac->sac_chans;
dev->ad_chan_count = sac->sac_chan_count;
OS_DEV_SETHANDLERS(odev, stm32f4_adc_open, stm32f4_adc_close);
dev->ad_funcs = &stm32f4_adc_funcs;
return (OS_OK);
}