445 lines
13 KiB
C
445 lines
13 KiB
C
#include <stdio.h>
|
|
#include <stm32f4xx.h>
|
|
|
|
#include "misc.h"
|
|
#include "nlr.h"
|
|
#include "mpconfig.h"
|
|
#include "qstr.h"
|
|
#include "obj.h"
|
|
#include "adc.h"
|
|
|
|
/* ADC defintions */
|
|
#define ADCx (ADC1)
|
|
#define ADCx_CLK (RCC_APB2Periph_ADC1)
|
|
#define ADC_NUM_CHANNELS (16)
|
|
|
|
/* Internally connected ADC channels Temp/VBAT/VREF*/
|
|
#if defined (STM32F40XX) || defined(STM32F41XX) || defined(STM32F40_41xxx)
|
|
#define ADC_TEMP_CHANNEL (16)
|
|
#define ADC_VBAT_CHANNEL (18)
|
|
#define ADC_VREF_CHANNEL (17)
|
|
#elif defined (STM32F42XX) || defined(STM32F43XX)
|
|
#define ADC_TEMP_CHANNEL (18)
|
|
#define ADC_VBAT_CHANNEL (18) /* same channel as TEMP */
|
|
#define ADC_VREF_CHANNEL (17)
|
|
#endif
|
|
|
|
/* Core temperature sensor definitions */
|
|
#define CORE_TEMP_V25 (943) /* (0.76v/3.3v)*(2^ADC resoultion) */
|
|
#define CORE_TEMP_AVG_SLOPE (3) /* (2.5mv/3.3v)*(2^ADC resoultion) */
|
|
|
|
/* VBAT divider */
|
|
#if defined (STM32F40XX) || defined(STM32F41XX) || defined(STM32F40_41xxx)
|
|
#define VBAT_DIV (2)
|
|
#elif defined (STM32F42XX) || defined(STM32F43XX)
|
|
#define VBAT_DIV (4)
|
|
#endif
|
|
|
|
/* GPIO struct */
|
|
typedef struct {
|
|
GPIO_TypeDef* port;
|
|
uint32_t pin;
|
|
} gpio_t;
|
|
|
|
/* ADC GPIOs */
|
|
static const gpio_t adc_gpio[] = {
|
|
{GPIOA, GPIO_Pin_0}, /* ADC123_IN0 */
|
|
{GPIOA, GPIO_Pin_1}, /* ADC123_IN1 */
|
|
{GPIOA, GPIO_Pin_2}, /* ADC123_IN2 */
|
|
{GPIOA, GPIO_Pin_3}, /* ADC123_IN3 */
|
|
{GPIOA, GPIO_Pin_4}, /* ADC12_IN4 */
|
|
{GPIOA, GPIO_Pin_5}, /* ADC12_IN5 */
|
|
{GPIOA, GPIO_Pin_6}, /* ADC12_IN6 */
|
|
{GPIOA, GPIO_Pin_7}, /* ADC12_IN7 */
|
|
{GPIOB, GPIO_Pin_0}, /* ADC12_IN8 */
|
|
{GPIOB, GPIO_Pin_1}, /* ADC12_IN9 */
|
|
{GPIOC, GPIO_Pin_0}, /* ADC123_IN10 */
|
|
{GPIOC, GPIO_Pin_1}, /* ADC123_IN11 */
|
|
{GPIOC, GPIO_Pin_2}, /* ADC123_IN12 */
|
|
{GPIOC, GPIO_Pin_3}, /* ADC123_IN13 */
|
|
{GPIOC, GPIO_Pin_4}, /* ADC12_IN14 */
|
|
{GPIOC, GPIO_Pin_5}, /* ADC12_IN15 */
|
|
|
|
};
|
|
|
|
void adc_init_all(uint32_t resolution) {
|
|
ADC_InitTypeDef ADC_InitStructure;
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
ADC_CommonInitTypeDef ADC_CommonInitStructure;
|
|
|
|
/* Enable ADCx, DMA and GPIO clocks */
|
|
#if 0
|
|
/* GPIO clocks enabled in main */
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
|
|
RCC_AHB1Periph_GPIOB |
|
|
RCC_AHB1Periph_GPIOC, ENABLE);
|
|
#endif
|
|
RCC_APB2PeriphClockCmd(ADCx_CLK, ENABLE);
|
|
|
|
/* ADC Common Init */
|
|
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
|
|
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
|
|
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
|
|
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
|
|
ADC_CommonInit(&ADC_CommonInitStructure);
|
|
|
|
/* Configure ADC GPIOs */
|
|
for (int i=0; i<ADC_NUM_CHANNELS; i++) {
|
|
GPIO_InitStructure.GPIO_Pin = adc_gpio[i].pin;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
GPIO_Init(adc_gpio[i].port, &GPIO_InitStructure);
|
|
}
|
|
|
|
/* ADCx Init */
|
|
// ADC_DeInit();
|
|
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
|
|
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
|
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
|
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
|
|
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
|
|
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
|
ADC_InitStructure.ADC_NbrOfConversion = 1;
|
|
ADC_Init(ADCx, &ADC_InitStructure);
|
|
|
|
/* Enable ADCx */
|
|
ADC_Cmd(ADCx, ENABLE);
|
|
|
|
/* Enable VBAT/VREF monitor */
|
|
ADC_VBATCmd(ENABLE);
|
|
|
|
/* Enable temperature sensor */
|
|
ADC_TempSensorVrefintCmd(ENABLE);
|
|
}
|
|
|
|
void adc_init_single(uint32_t channel) {
|
|
ADC_InitTypeDef ADC_InitStructure;
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
ADC_CommonInitTypeDef ADC_CommonInitStructure;
|
|
|
|
/* Enable ADCx, DMA and GPIO clocks */
|
|
#if 0
|
|
/* GPIO clocks enabled in main */
|
|
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA |
|
|
RCC_AHB1Periph_GPIOB |
|
|
RCC_AHB1Periph_GPIOC, ENABLE);
|
|
#endif
|
|
RCC_APB2PeriphClockCmd(ADCx_CLK, ENABLE);
|
|
|
|
/* ADC Common Init */
|
|
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
|
|
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
|
|
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
|
|
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
|
|
ADC_CommonInit(&ADC_CommonInitStructure);
|
|
|
|
/* Configure ADC GPIO for the single channel */
|
|
GPIO_InitStructure.GPIO_Pin = adc_gpio[channel].pin;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
|
|
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
|
|
GPIO_Init(adc_gpio[channel].port, &GPIO_InitStructure);
|
|
|
|
/* ADCx Init */
|
|
// ADC_DeInit();
|
|
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
|
|
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
|
|
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
|
|
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
|
|
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
|
|
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
|
|
ADC_InitStructure.ADC_NbrOfConversion = 1;
|
|
ADC_Init(ADCx, &ADC_InitStructure);
|
|
|
|
/* Enable ADCx */
|
|
ADC_Cmd(ADCx, ENABLE);
|
|
|
|
/* Enable VBAT/VREF monitor */
|
|
ADC_VBATCmd(ENABLE);
|
|
|
|
/* Enable temperature sensor */
|
|
ADC_TempSensorVrefintCmd(ENABLE);
|
|
}
|
|
|
|
uint32_t adc_read_channel(int channel)
|
|
{
|
|
int timeout = 10000;
|
|
|
|
if (channel > (ADC_NUM_CHANNELS-1)) {
|
|
return 0;
|
|
}
|
|
|
|
/* ADC regular channel config ADC/Channel/SEQ Rank/Sample time */
|
|
ADC_RegularChannelConfig(ADCx, channel, 1, ADC_SampleTime_15Cycles);
|
|
|
|
/* Start ADC single conversion */
|
|
ADC_SoftwareStartConv(ADCx);
|
|
|
|
/* Wait for conversion to be complete*/
|
|
while(!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) && --timeout >0) {
|
|
}
|
|
|
|
/* ADC conversion timed out */
|
|
if (timeout == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Return converted data */
|
|
return ADC_GetConversionValue(ADCx);
|
|
}
|
|
|
|
int adc_read_core_temp()
|
|
{
|
|
int timeout = 10000;
|
|
|
|
/* ADC temperature sensor channel config ADC/Channel/SEQ Rank/Sample time */
|
|
/* Note: sample time must be higher than minimum sample time */
|
|
ADC_RegularChannelConfig(ADCx, ADC_TEMP_CHANNEL, 1, ADC_SampleTime_480Cycles);
|
|
|
|
/* Start ADC single conversion */
|
|
ADC_SoftwareStartConv(ADCx);
|
|
|
|
/* Wait for conversion to be complete*/
|
|
while(!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) && --timeout >0) {
|
|
}
|
|
|
|
/* ADC conversion timed out */
|
|
if (timeout == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Convert ADC reading to temperature */
|
|
/* Temperature formula from datasheet P.411 */
|
|
return ((ADC_GetConversionValue(ADCx) - CORE_TEMP_V25) / CORE_TEMP_AVG_SLOPE) + 25;
|
|
}
|
|
|
|
float adc_read_core_vbat()
|
|
{
|
|
int timeout = 10000;
|
|
|
|
/* ADC VBAT channel config ADC/Channel/SEQ Rank/Sample time */
|
|
/* Note: sample time must be higher than minimum sample time */
|
|
ADC_RegularChannelConfig(ADCx, ADC_VBAT_CHANNEL, 1, ADC_SampleTime_144Cycles);
|
|
|
|
/* Start ADC single conversion */
|
|
ADC_SoftwareStartConv(ADCx);
|
|
|
|
/* Wait for conversion to be complete */
|
|
while(!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) && --timeout >0) {
|
|
}
|
|
|
|
/* ADC conversion timed out */
|
|
if (timeout == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Convert ADC reading to voltage, VBAT pin is
|
|
internally connected to a bridge divider by VBAT_DIV */
|
|
return ADC_GetConversionValue(ADCx)*VBAT_DIV/4096.0f*3.3f;
|
|
}
|
|
|
|
float adc_read_core_vref()
|
|
{
|
|
int timeout = 10000;
|
|
|
|
/* ADC VBAT channel config ADC/Channel/SEQ Rank/Sample time */
|
|
/* Note: sample time must be higher than minimum sample time */
|
|
ADC_RegularChannelConfig(ADCx, ADC_VREF_CHANNEL, 1, ADC_SampleTime_112Cycles);
|
|
|
|
/* Start ADC single conversion */
|
|
ADC_SoftwareStartConv(ADCx);
|
|
|
|
/* Wait for conversion to be complete*/
|
|
while(!ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) && --timeout >0) {
|
|
}
|
|
|
|
/* ADC conversion timed out */
|
|
if (timeout == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Convert ADC reading to voltage */
|
|
return ADC_GetConversionValue(ADCx)/4096.0f*3.3f;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* Micro Python bindings : adc_all object */
|
|
|
|
typedef struct _pyb_obj_adc_all_t {
|
|
mp_obj_base_t base;
|
|
bool is_enabled;
|
|
} pyb_obj_adc_all_t;
|
|
|
|
static void adc_all_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
print(env, "<ADC all>");
|
|
}
|
|
|
|
static mp_obj_t adc_all_read_channel(mp_obj_t self_in, mp_obj_t channel) {
|
|
pyb_obj_adc_all_t *self = self_in;
|
|
|
|
if (self->is_enabled) {
|
|
uint32_t chan = mp_obj_get_int(channel);
|
|
uint32_t data = adc_read_channel(chan);
|
|
return mp_obj_new_int(data);
|
|
} else {
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
static mp_obj_t adc_all_read_core_temp(mp_obj_t self_in) {
|
|
pyb_obj_adc_all_t *self = self_in;
|
|
|
|
if (self->is_enabled) {
|
|
int data = adc_read_core_temp();
|
|
return mp_obj_new_int(data);
|
|
} else {
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
static mp_obj_t adc_all_read_core_vbat(mp_obj_t self_in) {
|
|
pyb_obj_adc_all_t *self = self_in;
|
|
|
|
if (self->is_enabled) {
|
|
float data = adc_read_core_vbat();
|
|
return mp_obj_new_float(data);
|
|
} else {
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
static mp_obj_t adc_all_read_core_vref(mp_obj_t self_in) {
|
|
pyb_obj_adc_all_t *self = self_in;
|
|
|
|
if (self->is_enabled) {
|
|
float data = adc_read_core_vref();
|
|
return mp_obj_new_float(data);
|
|
} else {
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
static MP_DEFINE_CONST_FUN_OBJ_2(adc_all_read_channel_obj, adc_all_read_channel);
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_temp_obj, adc_all_read_core_temp);
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_vbat_obj, adc_all_read_core_vbat);
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(adc_all_read_core_vref_obj, adc_all_read_core_vref);
|
|
|
|
static const mp_method_t adc_all_methods[] = {
|
|
{ "read_channel", &adc_all_read_channel_obj},
|
|
{ "read_core_temp", &adc_all_read_core_temp_obj},
|
|
{ "read_core_vbat", &adc_all_read_core_vbat_obj},
|
|
{ "read_core_vref", &adc_all_read_core_vref_obj},
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static const mp_obj_type_t adc_all_type = {
|
|
{ &mp_const_type },
|
|
"ADC_all",
|
|
.print = adc_all_print,
|
|
.methods = adc_all_methods,
|
|
};
|
|
|
|
mp_obj_t pyb_ADC_all(mp_obj_t resolution) {
|
|
/* init ADC */
|
|
adc_init_all(mp_obj_get_int(resolution));
|
|
|
|
pyb_obj_adc_all_t *o = m_new_obj(pyb_obj_adc_all_t);
|
|
o->base.type = &adc_all_type;
|
|
o->is_enabled = true;
|
|
return o;
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_ADC_all_obj, pyb_ADC_all);
|
|
|
|
/******************************************************************************/
|
|
/* Micro Python bindings : adc object (single channel) */
|
|
|
|
typedef struct _pyb_obj_adc_t {
|
|
mp_obj_base_t base;
|
|
mp_obj_t pin_name;
|
|
int channel;
|
|
bool is_enabled;
|
|
} pyb_obj_adc_t;
|
|
|
|
static void adc_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
pyb_obj_adc_t *self = self_in;
|
|
print(env, "<ADC on ");
|
|
mp_obj_print_helper(print, env, self->pin_name, PRINT_STR);
|
|
print(env, " channel=%lu>", self->channel);
|
|
}
|
|
|
|
static mp_obj_t adc_read(mp_obj_t self_in) {
|
|
pyb_obj_adc_t *self = self_in;
|
|
|
|
if (self->is_enabled) {
|
|
uint32_t data = adc_read_channel(self->channel);
|
|
return mp_obj_new_int(data);
|
|
} else {
|
|
return mp_const_none;
|
|
}
|
|
}
|
|
|
|
static MP_DEFINE_CONST_FUN_OBJ_1(adc_read_obj, adc_read);
|
|
|
|
static const mp_method_t adc_methods[] = {
|
|
{ "read", &adc_read_obj},
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static const mp_obj_type_t adc_type = {
|
|
{ &mp_const_type },
|
|
"ADC",
|
|
.print = adc_print,
|
|
.methods = adc_methods,
|
|
};
|
|
|
|
mp_obj_t pyb_ADC(mp_obj_t pin_name_obj) {
|
|
|
|
pyb_obj_adc_t *o = m_new_obj(pyb_obj_adc_t);
|
|
o->base.type = &adc_type;
|
|
o->pin_name = pin_name_obj;
|
|
|
|
// work out the channel from the pin name
|
|
const char *pin_name = mp_obj_str_get_str(pin_name_obj);
|
|
GPIO_TypeDef *port;
|
|
switch (pin_name[0]) {
|
|
case 'A': case 'a': port = GPIOA; break;
|
|
case 'B': case 'b': port = GPIOB; break;
|
|
case 'C': case 'c': port = GPIOC; break;
|
|
default: goto pin_error;
|
|
}
|
|
uint pin_num = 0;
|
|
for (const char *s = pin_name + 1; *s; s++) {
|
|
if (!('0' <= *s && *s <= '9')) {
|
|
goto pin_error;
|
|
}
|
|
pin_num = 10 * pin_num + *s - '0';
|
|
}
|
|
if (!(0 <= pin_num && pin_num <= 15)) {
|
|
goto pin_error;
|
|
}
|
|
|
|
int i;
|
|
for (i = 0; i < ADC_NUM_CHANNELS; i++) {
|
|
if (adc_gpio[i].port == port && adc_gpio[i].pin == (1 << pin_num)) {
|
|
o->channel = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == ADC_NUM_CHANNELS) {
|
|
nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "pin %s does not have ADC capabilities", pin_name));
|
|
}
|
|
|
|
// init ADC just for this channel
|
|
adc_init_single(o->channel);
|
|
|
|
o->is_enabled = true;
|
|
|
|
return o;
|
|
|
|
pin_error:
|
|
nlr_jump(mp_obj_new_exception_msg_varg(MP_QSTR_ValueError, "pin %s does not exist", pin_name));
|
|
}
|
|
|
|
MP_DEFINE_CONST_FUN_OBJ_1(pyb_ADC_obj, pyb_ADC);
|