Skip to content

Commit

Permalink
Support for STM32L4
Browse files Browse the repository at this point in the history
This change adds initial support for ADC on STM32.
For now, only STM32L4 is supported, STM32Fxxx have somewhat different ADC and need more work.

CL: adc: Support for STM32L4
  • Loading branch information
rojer committed Oct 6, 2019
1 parent b1d3bf6 commit 7015783
Show file tree
Hide file tree
Showing 7 changed files with 456 additions and 7 deletions.
12 changes: 6 additions & 6 deletions include/mgos_adc.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@
* limitations under the License.
*/

#ifndef CS_MOS_LIBS_ADC_SRC_MGOS_ADC_H_
#define CS_MOS_LIBS_ADC_SRC_MGOS_ADC_H_
#pragma once

#include <stdbool.h>

#if CS_PLATFORM == CS_P_ESP32
#include "esp32/esp32_adc.h"
#endif
#if CS_PLATFORM == CS_P_STM32
#include "stm32/stm32_adc.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif

/* Configure and enable ADC */
bool mgos_adc_enable(int pin);
Expand All @@ -42,6 +44,4 @@ int mgos_adc_read_voltage(int pin);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* CS_MOS_LIBS_ADC_SRC_MGOS_ADC_H_ */
#endif
81 changes: 81 additions & 0 deletions include/stm32/stm32_adc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2019 Deomid "rojer" Ryabkov
* All rights reserved
*
* Licensed 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.
*/

#pragma once

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

// Value of the internal voltage reference.
#ifndef STM32_ADC_VREFINT_VALUE_MV
#define STM32_ADC_VREFINT_VALUE_MV 1212
#endif

struct stm32_adc_config {
// Resolution, in bits; 6, 8, 10 or 12; default is 12.
uint8_t resolution;
};

// Configures and enabled the ADC.
bool stm32_adc_configure(int unit_no, const struct stm32_adc_config *cfg);

// Enable particular ADC input.
bool stm32_adc_enable_input(int unit_no, int input_no);

// Performs single conversion of a particular input (0-18)
int stm32_adc_read(int unit_no, int input_no);

/* Read the internal temperature sensor.
* Returns temperature in degrees Celsius. */
int stm32_adc_read_temp_c(void);

// Returns reference value used for mgos_adc_read_voltage calculation.
int stm32_adc_get_vref(void);

// Set reference voltage value, used in mgos_adc_read_voltage calculation.
void stm32_adc_set_vref(int vref_mv);

// Sets vref by measuring Vrefint.
// Returns calculated value or -1 in case of error.
int stm32_adc_set_vref_from_vrefint(void);

// ADC "pseudo-pins" for internal sources.
#define STM32_ADC_PIN_VREFINT 0xff0000 // Internal voltage reference
#define STM32_ADC_PIN_TEMP 0xff0001 // Package temperature sensor
#define STM32_ADC_PIN_VBAT 0xff0002 // Battery voltage rail

#define STM32_ADC1_M (1 << 1)
#define STM32_ADC2_M (1 << 2)
#define STM32_ADC3_M (1 << 3)

struct stm32_adc_input_def {
int pin; // GPIO pin (port, number).
uint8_t input_no; // Input number.
uint8_t
adc_m; // Which ADC units have it enabled, combination of STM32_ADCx_M.
};

const struct stm32_adc_input_def *stm32_adc_get_input_def(int input_no);
const struct stm32_adc_input_def *stm32_adc_get_input_def_by_pin(int pin);

#ifdef __cplusplus
}
#endif
2 changes: 1 addition & 1 deletion mos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type: lib
description: ADC support
version: 1.0.0

platforms: [ esp32, esp8266 ]
platforms: [ esp32, esp8266, stm32 ]

includes:
- include
Expand Down
96 changes: 96 additions & 0 deletions src/stm32/stm32_adc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2019 Deomid "rojer" Ryabkov
* All rights reserved
*
* Licensed 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 "mgos_adc.h"

#include "mgos.h"

#include "stm32_sdk_hal.h"

static int s_vref_mv = -1;

static ADC_TypeDef *const s_regs[] = {
NULL,
#ifdef ADC1
ADC1,
#else
NULL,
#endif

#ifdef ADC2
ADC2,
#else
NULL,
#endif

#ifdef ADC3
ADC3,
#else
NULL,
#endif
};

// Vbat is connected to ADC through a divider.
// The ratio is different for different device families.
#if defined(STM32L4)
#define STM32_ADC_VBAT_DIV 3
#endif
#if defined(STM32F2)
#define STM32_ADC_VBAT_DIV 2
#endif
#if defined(STM32F4) || defined(STM32F7)
#define STM32_ADC_VBAT_DIV 4
#endif
#ifndef STM32_ADC_VBAT_DIV
#error STM32_ADC_VBAT_DIV not defined for this device
#endif

volatile ADC_TypeDef *stm32_adc_get_regs(int unit_no) {
if (unit_no >= (int) ARRAY_SIZE(s_regs)) return NULL;
return s_regs[unit_no];
}

bool mgos_adc_enable(int pin) {
const struct stm32_adc_input_def *ind = stm32_adc_get_input_def_by_pin(pin);
if (ind == NULL) return false;
if ((ind->adc_m & STM32_ADC1_M) == 0) return false;
struct stm32_adc_config cfg = {.resolution = 12};
if (!stm32_adc_configure(1, &cfg)) return false;
return stm32_adc_enable_input(1, ind->input_no);
}

int mgos_adc_read(int pin) {
const struct stm32_adc_input_def *ind = stm32_adc_get_input_def_by_pin(pin);
if (ind == NULL) return -1;
if ((ind->adc_m & STM32_ADC1_M) == 0) return false;
return stm32_adc_read(1, ind->input_no);
}

int mgos_adc_read_voltage(int pin) {
int mval = mgos_adc_read(pin);
if (mval < 0) return mval;
if (pin == STM32_ADC_PIN_VBAT) mval *= STM32_ADC_VBAT_DIV;
return (s_vref_mv * mval) / 4095;
}

int stm32_adc_get_vref(void) {
return s_vref_mv;
}

void stm32_adc_set_vref(int vref_mv) {
s_vref_mv = vref_mv;
}
101 changes: 101 additions & 0 deletions src/stm32/stm32_adc_inputs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright (c) 2019 Deomid "rojer" Ryabkov
* All rights reserved
*
* Licensed 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 "stm32_adc.h"

#include "mgos.h"

// clang-format off

#ifdef STM32F205xx
static const struct stm32_adc_input_def s_stm32_adc_inputs[] = {
// TODO
{ .pin = 0, .input_no = 0, .adc_m = 0 },
};
#define STM32_INPUTS_DEFINED
#endif

#ifdef STM32F746xx
static const struct stm32_adc_input_def s_stm32_adc_inputs[] = {
{ .pin = STM32_GPIO('A', 0), .input_no = 0, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('A', 1), .input_no = 1, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('A', 2), .input_no = 2, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('A', 3), .input_no = 3, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('A', 4), .input_no = 4, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 5), .input_no = 5, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 6), .input_no = 6, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 7), .input_no = 7, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('B', 0), .input_no = 8, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('B', 1), .input_no = 9, .adc_m = STM32_ADC1_M | STM32_ADC2_M },

{ .pin = STM32_GPIO('C', 0), .input_no = 10, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 1), .input_no = 11, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 2), .input_no = 12, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 3), .input_no = 13, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },

{ .pin = STM32_GPIO('F', 4), .input_no = 14, .adc_m = STM32_ADC3_M },
{ .pin = STM32_GPIO('F', 5), .input_no = 15, .adc_m = STM32_ADC3_M },

{ .pin = 0, .input_no = 16, .adc_m = 0 },

{ .pin = STM32_ADC_PIN_VREFINT, .input_no = 17, .adc_m = STM32_ADC1_M },
{ .pin = STM32_ADC_PIN_TEMP, .input_no = 18, .adc_m = STM32_ADC1_M }, // T sensor and Vbat share the same channel.
{ .pin = STM32_ADC_PIN_VBAT, .input_no = 18, .adc_m = STM32_ADC1_M },
};
#define STM32_INPUTS_DEFINED
#endif

#ifdef STM32L475xx
static const struct stm32_adc_input_def s_stm32_adc_inputs[] = {
{ .pin = STM32_ADC_PIN_VREFINT, .input_no = 0, .adc_m = STM32_ADC1_M },
{ .pin = STM32_GPIO('C', 0), .input_no = 1, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 1), .input_no = 2, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 2), .input_no = 3, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('C', 3), .input_no = 4, .adc_m = STM32_ADC1_M | STM32_ADC2_M | STM32_ADC3_M },
{ .pin = STM32_GPIO('A', 0), .input_no = 5, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 1), .input_no = 6, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 2), .input_no = 7, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 3), .input_no = 8, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 4), .input_no = 9, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 5), .input_no = 10, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 6), .input_no = 11, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('A', 7), .input_no = 12, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('C', 4), .input_no = 13, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('C', 5), .input_no = 14, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('B', 0), .input_no = 15, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_GPIO('B', 1), .input_no = 16, .adc_m = STM32_ADC1_M | STM32_ADC2_M },
{ .pin = STM32_ADC_PIN_TEMP, .input_no = 17, .adc_m = STM32_ADC1_M | STM32_ADC3_M },
{ .pin = STM32_ADC_PIN_VBAT, .input_no = 18, .adc_m = STM32_ADC1_M | STM32_ADC3_M },
};
#define STM32_INPUTS_DEFINED
#endif

#ifndef STM32_INPUTS_DEFINED
#error STM32 pin to ADC input mapping is not defined for this device
#endif

const struct stm32_adc_input_def *stm32_adc_get_input_def(int input_no) {
if (input_no >= (int) ARRAY_SIZE(s_stm32_adc_inputs)) return NULL;
return &s_stm32_adc_inputs[input_no];
}

const struct stm32_adc_input_def *stm32_adc_get_input_def_by_pin(int pin) {
for (int i = 0; i < (int) ARRAY_SIZE(s_stm32_adc_inputs); i++) {
if (s_stm32_adc_inputs[i].pin == pin) return &s_stm32_adc_inputs[i];
}
return NULL;
}
31 changes: 31 additions & 0 deletions src/stm32/stm32f_adc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 Deomid "rojer" Ryabkov
* All rights reserved
*
* Licensed 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 "stm32_adc.h"

#include "stm32_sdk_hal.h"

extern volatile ADC_TypeDef *stm32_adc_get_regs(int unit_no);

#if defined(STM32F2) || defined(STM32F4) || defined(STM32F7)

// TODO

bool mgos_adc_init(void) {
return true;
}
#endif // defined(STM32F2) || defined(STM32F4) || defined(STM32F7)
Loading

0 comments on commit 7015783

Please sign in to comment.