Addition of a better way to deal with the Onboard ADC.

This modification adds support for a STM32ADC library. There are a
couple of examples on how to use it. The library allows usage of the
ADC with DMA in circular mode or scan mode, it allows the ADC to be
used in scan mode. And also to use the EOC interrupt.
More functionality needs to be added and will be in time.

Eventually the util files will be moved into the adc files in the core.
This commit is contained in:
Bubulindo 2016-09-23 16:35:18 +01:00
parent 7317d26c14
commit 5c13d8b0d4
18 changed files with 1075 additions and 31 deletions

8
STM32F1/cores/maple/libmaple/adc.c Normal file → Executable file
View File

@ -44,7 +44,7 @@
*
* @param dev ADC peripheral to initialize
*/
void adc_init(const adc_dev *dev) {
void adc_init(adc_dev *dev) {
rcc_clk_enable(dev->clk_id);
rcc_reset_dev(dev->clk_id);
}
@ -55,7 +55,7 @@ void adc_init(const adc_dev *dev) {
* @param event Event used to trigger the start of conversion.
* @see adc_extsel_event
*/
void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
void adc_set_extsel(adc_dev *dev, adc_extsel_event event) {
uint32 cr2 = dev->regs->CR2;
cr2 &= ~ADC_CR2_EXTSEL;
cr2 |= event;
@ -71,7 +71,7 @@ void adc_set_extsel(const adc_dev *dev, adc_extsel_event event) {
* @param smp_rate sample rate to set
* @see adc_smp_rate
*/
void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) {
void adc_set_sample_rate(adc_dev *dev, adc_smp_rate smp_rate) {
uint32 adc_smpr1_val = 0, adc_smpr2_val = 0;
int i;
@ -95,7 +95,7 @@ void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate) {
* @param channel channel to convert
* @return conversion result
*/
uint16 adc_read(const adc_dev *dev, uint8 channel) {
uint16 adc_read(adc_dev *dev, uint8 channel) {
adc_reg_map *regs = dev->regs;
adc_set_reg_seqlen(dev, 1);

115
STM32F1/cores/maple/libmaple/adc_f1.c Normal file → Executable file
View File

@ -34,6 +34,7 @@
#include <libmaple/adc.h>
#include <libmaple/gpio.h>
#include <libmaple/nvic.h>//Added by bubulindo.
/*
* Devices
@ -42,26 +43,126 @@
adc_dev adc1 = {
.regs = ADC1_BASE,
.clk_id = RCC_ADC1,
.handlers = {[3]=0}, //added by bubulindo. EOC, JEOC, AWD
.irq_num = NVIC_ADC_1_2,
};
/** ADC1 device. */
const adc_dev *ADC1 = &adc1;
adc_dev *ADC1 = &adc1;
adc_dev adc2 = {
.regs = ADC2_BASE,
.clk_id = RCC_ADC2,
};
/** ADC2 device. */
const adc_dev *ADC2 = &adc2;
adc_dev *ADC2 = &adc2;
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
adc_dev adc3 = {
.regs = ADC3_BASE,
.clk_id = RCC_ADC3,
.handlers = {[3]=0}, //added by bubulindo. EOC, JEOC, AWD
.irq_num = NVIC_ADC_3,//added by bubulindo.
};
/** ADC3 device. */
const adc_dev *ADC3 = &adc3;
adc_dev *ADC3 = &adc3;
#endif
/*
adc irq routine.
Added by bubulindo.
*/
void __irq_adc() {
//get status
uint32 adc_sr = ADC1->regs->SR;
//End Of Conversion
if (adc_sr & (1U << ADC_SR_EOC_BIT)) {
ADC1->regs->SR &= ~(1<<ADC_SR_EOC_BIT);
void (*handler)(void) = ADC1->handlers[ADC_EOC];
if (handler) {
handler();
}
}
//Injected End Of Conversion
if (adc_sr & (1U << ADC_SR_JEOC_BIT)) {
ADC1->regs->SR &= ~(1<<ADC_SR_JEOC_BIT);
void (*handler)(void) = ADC1->handlers[ADC_JEOC];
if (handler) {
handler();
}
}
//Analog Watchdog
if (adc_sr & (1U << ADC_SR_AWD_BIT)) {
ADC1->regs->SR &= ~(1<<ADC_SR_AWD_BIT);
void (*handler)(void) = ADC1->handlers[ADC_AWD];
if (handler) {
handler();
}
}
};//end of adc irq
/*
ADC3 IRQ handler.
added by bubulindo
*/
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
void __irq_adc3() {
//get status
uint32 adc_sr = ADC3->regs->SR;
//End Of Conversion
if (adc_sr & (1U << ADC_SR_EOC_BIT)) {
ADC3->regs->SR &= ~(1<<ADC_SR_EOC_BIT);
void (*handler)(void) = ADC3->handlers[ADC_EOC];
if (handler) {
handler();
}
}
//Injected End Of Conversion
if (adc_sr & (1U << ADC_SR_JEOC_BIT)) {
ADC3->regs->SR &= ~(1<<ADC_SR_JEOC_BIT);
void (*handler)(void) = ADC3->handlers[ADC_JEOC];
if (handler) {
handler();
}
}
//Analog Watchdog
if (adc_sr & (1U << ADC_SR_AWD_BIT)) {
ADC3->regs->SR &= ~(1<<ADC_SR_AWD_BIT);
void (*handler)(void) = ADC3->handlers[ADC_AWD];
if (handler) {
handler();
}
}
};//end of ADC3 irq
#endif
/*
enable interrupts on the ADC:
use ADC_EOC, ADC_JEOC, ADC_AWD
This will set up the interrupt bit in the ADC as well as in the NVIC.
added by bubulindo
*/
void adc_enable_irq(adc_dev* dev, uint8 interrupt) {//ADC1 for now.
dev->regs->CR1 |= (1U<<(interrupt +ADC_CR1_EOCIE_BIT));
nvic_irq_enable(dev->irq_num);
}
/*
attach interrupt functionality for ADC
use ADC_EOC, ADC_JEOC, ADC_AWD
added by bubulindo
*/
void adc_attach_interrupt(adc_dev *dev,
uint8 interrupt,
voidFuncPtr handler) {
dev->handlers[interrupt] = handler;
adc_enable_irq(dev, interrupt);
//enable_irq(dev, interrupt); //I need to create this function. to enable NVIC
// nvic_irq_enable()
}
/*
* STM32F1 routines
*/
@ -73,7 +174,7 @@ const adc_dev *ADC3 = &adc3;
*
* @param dev adc device
*/
void adc_calibrate(const adc_dev *dev) {
void adc_calibrate(adc_dev *dev) {
__io uint32 *rstcal_bit = bb_perip(&(dev->regs->CR2), 3);
__io uint32 *cal_bit = bb_perip(&(dev->regs->CR2), 2);
@ -94,7 +195,7 @@ void adc_set_prescaler(adc_prescaler pre) {
rcc_set_prescaler(RCC_PRESCALER_ADC, (uint32)pre);
}
void adc_foreach(void (*fn)(const adc_dev*)) {
void adc_foreach(void (*fn)(adc_dev*)) {
fn(ADC1);
fn(ADC2);
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
@ -102,11 +203,11 @@ void adc_foreach(void (*fn)(const adc_dev*)) {
#endif
}
void adc_config_gpio(const adc_dev *ignored, gpio_dev *gdev, uint8 bit) {
void adc_config_gpio(adc_dev *ignored, gpio_dev *gdev, uint8 bit) {
gpio_set_mode(gdev, bit, GPIO_INPUT_ANALOG);
}
void adc_enable_single_swstart(const adc_dev *dev) {
void adc_enable_single_swstart(adc_dev *dev) {
adc_init(dev);
adc_set_extsel(dev, ADC_SWSTART);
adc_set_exttrig(dev, 1);

2
STM32F1/cores/maple/wirish_analog.cpp Normal file → Executable file
View File

@ -38,7 +38,7 @@
* to INPUT_ANALOG. That's faster, but it does require some extra work
* on the user's part. Not too much, we think ;). */
uint16 analogRead(uint8 pin) {
const adc_dev *dev = PIN_MAP[pin].adc_device;
adc_dev *dev = PIN_MAP[pin].adc_device;
if (dev == NULL) {
return 0;
}

2
STM32F1/cores/maple/wirish_types.h Normal file → Executable file
View File

@ -51,7 +51,7 @@
typedef struct stm32_pin_info {
gpio_dev *gpio_device; /**< Maple pin's GPIO device */
timer_dev *timer_device; /**< Pin's timer device, if any. */
const adc_dev *adc_device; /**< ADC device, if any. */
adc_dev *adc_device; /**< ADC device, if any. */
uint8 gpio_bit; /**< Pin's GPIO port bit. */
uint8 timer_channel; /**< Timer channel, or 0 if none. */
uint8 adc_channel; /**< Pin ADC channel, or ADCx if none. */

View File

@ -0,0 +1,73 @@
/*
This example shows how to use the ADC library to continuously sample
several channels/pins.
The acquisition of the channels is done using DMA in circular mode.
*/
#include <STM32ADC.h>
STM32ADC myADC(ADC1);
#define BOARD_LED D33 //this is for Maple Mini
//Channels to be acquired.
uint8 pins[] = {11,10,9,8,7,6,5,4};
const int maxSamples = 8; // 8 channels
// Array for the ADC data
uint16_t dataPoints[maxSamples];
void setup() {
Serial.begin(19200);
pinMode(BOARD_LED, OUTPUT);
pinMode(D32, INPUT);
//startup blink... good idea from Pig-O-Scope
digitalWrite(BOARD_LED, HIGH);
delay(1000);
digitalWrite(BOARD_LED, LOW);
delay(1000);
//calibrate ADC
myADC.calibrate();
// Set up our analog pin(s)
for (unsigned int j = 0; j <8; j++)
pinMode(pins[j], INPUT_ANALOG);
myADC.setSampleRate(ADC_SMPR_1_5);//set the Sample Rate
myADC.setScanMode(); //set the ADC in Scan mode.
myADC.setPins(pins, 8); //set how many and which pins to convert.
myADC.setContinuous(); //set the ADC in continuous mode.
//set the DMA transfer for the ADC.
//in this case we want to increment the memory side and run it in circular mode
//By doing this, we can read the last value sampled from the channels by reading the dataPoints array
myADC.setDMA(dataPoints, 8, (DMA_MINC_MODE | DMA_CIRC_MODE), NULL);
//start the conversion.
//because the ADC is set as continuous mode and in circular fashion, this can be done
//on setup().
myADC.startConversion();
}
void loop(){
//send the latest data acquired when the button is pushed.
if(digitalRead(D32) == 1 ) {
Serial.println("begin");
// Take our samples
for(unsigned int i = 0; i < maxSamples; i ++) {
Serial.print("sample[");
Serial.print(i);
Serial.print("] = ");
Serial.println(dataPoints[i]);
}
while(digitalRead(D32) == 1); //stay here.
}
}; //end loop

View File

@ -0,0 +1,66 @@
/*
This example shows how to use the ADC library to sample several
channels/pins in one go.
This example attaches an interrupt to the DMA completion to notify the user.
The DMA must be set together with the start conversion, so both setDMA and startConversion
Must be called.
*/
#include <STM32ADC.h>
#define BOARD_LED D33 //PB0
uint8 pins[] = {11,10,9,8,7,6,5,4};
const int maxSamples = 8; // 8 channels
// Array for the ADC data
uint16 dataPoints[maxSamples];
volatile static bool dma1_ch1_Active; //flag for interrupt
STM32ADC myADC(ADC1);
void setup() {
//start Serial
Serial.begin(19200);
//Start up blink from Pig-O-Scope
pinMode(BOARD_LED, OUTPUT);
pinMode(D32, INPUT);
digitalWrite(BOARD_LED, HIGH);
delay(1000);
digitalWrite(BOARD_LED, LOW);
delay(1000);
//calibrate ADC.
myADC.calibrate();
myADC.setSampleRate(ADC_SMPR_1_5); //sample ratio
myADC.setPins(pins, maxSamples); //pins to be converted
myADC.setScanMode(); //Set the ADC in Scan Mode
}
void loop(){
//start acquisition on button push.
if(digitalRead(D32) == 1 ) {
Serial.println("begin");
dma1_ch1_Active = 1;
myADC.setDMA(dataPoints, 8, (DMA_MINC_MODE | DMA_TRNS_CMPLT), DMA1_CH1_Event);
myADC.startConversion();
while (dma1_ch1_Active == 1); //wait for DMA to complete.
for(unsigned int i = 0; i < maxSamples; i ++) {
Serial.print("sample[");
Serial.print(i);
Serial.print("] = ");
Serial.println(dataPoints[i]);
}
while(digitalRead(D32) == 1); //stay here. Another button push is required.
}
}; //end loop
/*
Interrupt handler. eh eh
*/
static void DMA1_CH1_Event() {
dma1_ch1_Active = 0;
}

View File

@ -0,0 +1,17 @@
/*
This example shows how to use the ADC library to continuously sample
one channel/pin.
*/
#include <STM32ADC.h>
ADC myAdc(ADC1);
void setup() {
Serial.begin(19200);
}
void loop(){
}; //end loop

View File

@ -0,0 +1,25 @@
/*
This example shows how to use the ADC library sample
one channels/pin at a time.
A bit like analogRead, but with the possibility of having an interrupt.
*/
#include <STM32ADC.h>
ADC myAdc(ADC1);
uint8 pin[] = {D11};
void int_func() {
Serial.println(adc_result);
};
void setup() {
Serial.begin(19200);
myADC.setTrigger(ADC_EXT_EV_SWSTART);//start on SWStart bit
myADC.setChannels(pin, 1); //this is actually the pin you want to measure
myADC.attachADCInterrupt(int_func);
}; //end setup
void loop(){
}; //end loop

View File

@ -0,0 +1,50 @@
#include <STM32ADC.h>
STM32ADC myADC(ADC1);
uint8 pin = D7;
#define BOARD_LED D33 //this is for Maple Mini
volatile static bool triggered = 0;
uint32 dado_adc = 0;
void int_func() {
triggered = 1;
dado_adc = myADC.getData();
}
void setup() {
Serial.begin(19200);
pinMode(BOARD_LED, OUTPUT);
pinMode(D32, INPUT);
pinMode(D11, INPUT_ANALOG);//AD pin.
//startup blink... good idea from Pig-O-Scope
digitalWrite(BOARD_LED, HIGH);
delay(1000);
digitalWrite(BOARD_LED, LOW);
delay(1000);
myADC.calibrate();
myADC.setSampleRate(ADC_SMPR_1_5);
myADC.attachInterrupt(int_func, ADC_EOC);
myADC.setPins(&pin, 1);
}
void loop() {
if(digitalRead(D32) == 1 ) {
Serial.println("begin");
// Take our samples
myADC.startConversion();
while (triggered == 0); //wait here...
Serial.print("Readin: ");
Serial.println(dado_adc);
}
}

View File

@ -0,0 +1,42 @@
#######################################
# Syntax Coloring Map RTClock
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
Encoder KEYWORD1
start KEYWORD2
stop KEYWORD2
resetCounts KEYWORD2
value KEYWORD2
getTurns KEYWORD2
reset KEYWORD2
getAngle KEYWORD2
getDirection KEYWORD2
Polarity KEYWORD2
setPrescaler KEYWORD2
getPrescaler KEYWORD2
setPPR KEYWORD2
getPPR KEYWORD2
attachInterrupt KEYWORD2
detachInterrupt KEYWORD2
setFilter KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################
COUNT_CH2 LITERAL1
COUNT_CH1 LITERAL1
COUNT_BOTH_CHANNELS LITERAL1
RADIANS LITERAL1
GRADES LITERAL1
DEGREES LITERAL1
NORMAL LITERAL1
INVERTED LITERAL1
POSITIVE LITERAL1
NEGATIVE LITERAL1

View File

@ -0,0 +1,10 @@
name=STM32ADC
version=1.0
author=Cardoso
email=
sentence=Analog to Digital Converter Driver
paragraph=ADC for STM32F1
url=
architectures=STM32F1
maintainer=
category=Timing

View File

@ -0,0 +1,232 @@
#include "STM32ADC.h"
#include "boards.h"
/*
Constructor
Choose which ADC to use.
Start it up...
*/
STM32ADC::STM32ADC (adc_dev * dev){
_dev = dev;
//adc_calibrate(_dev);//get this out of the way.
}
uint32 STM32ADC::getData() {
return _dev->regs->DR;
};
/*
Set the ADC Sampling Rate.
ADC_SMPR_1_5, < 1.5 ADC cycles
ADC_SMPR_7_5, < 7.5 ADC cycles
ADC_SMPR_13_5, < 13.5 ADC cycles
ADC_SMPR_28_5, < 28.5 ADC cycles
ADC_SMPR_41_5, < 41.5 ADC cycles
ADC_SMPR_55_5, < 55.5 ADC cycles
ADC_SMPR_71_5, < 71.5 ADC cycles
ADC_SMPR_239_5, < 239.5 ADC cycles
*/
void STM32ADC::setSampleRate(adc_smp_rate SampleRate){
adc_set_sample_rate(_dev, SampleRate);
}
/*
Attach an interrupt to the ADC completion.
*/
void STM32ADC::attachInterrupt(voidFuncPtr func, uint8 interrupt){
adc_attach_interrupt(_dev,interrupt, func);
}
/*
This will enable the internal readings. Vcc and Temperature
*/
void STM32ADC::enableInternalReading(){
enable_internal_reading(_dev);
}
/*
This will read the Vcc and return something useful.
Polling is being used.
*/
float STM32ADC::readVcc(){
unsigned int result = 0;
float vcc = 0.0;
result = adc_read(_dev, 17);
vcc = (float)result * 1.1; //to be done later...
return vcc;
}
/*
This will read the Temperature and return something useful.
Polling is being used.
*/
float STM32ADC::readTemp(){
unsigned int result = 0;
float temperature = 0.0;
result = adc_read(_dev, 16);
temperature = (float)((_V25-result)/_AverageSlope)+ 25.0;
return temperature;
}
/*
This function will set the number of Pins to sample and which PINS to convert.
This uses the Maple Pin numbers and not the ADC channel numbers. Do not confuse.
*/
void STM32ADC::setPins(uint8 *pins, uint8 length){
//convert pins to channels.
uint8 channels[length];
unsigned int records[3] = {0,0,0};
unsigned char i = 0, j = 0;
for (unsigned char i = 0; i < length; i++) { //convert the channels from pins to ch.
channels[i] = PIN_MAP[pins[i]].adc_channel;
}
//run away protection
if (length > 16) length = 16;
//write the length
records[2] |= (length - 1) << 20;
//i goes through records, j goes through variables.
for (i = 0, j = 0; i < length; i++) {//go through the channel list.
if (i!=0 && i%6 == 0) j++;//next variable, please!!
records[j] |= (channels[i] << ((i%6)*5));
}
//update the registers inside with the scan sequence.
_dev->regs->SQR1 = records[2];
_dev->regs->SQR2 = records[1];
_dev->regs->SQR3 = records[0];
}
/*
This function will set the number of channels to convert
And which channels.
This is the ADC channels and not the Maple Pins!!! Important!!
Also, this will allow you to sample the AD and Vref channels.
*/
void STM32ADC::setChannels(uint8 *channels, uint8 length){
adc_set_reg_seq_channel(_dev, channels, length);
}
/*
This function will set the trigger to start the conversion
Timer, SWStart, etc...
*/
void STM32ADC::setTrigger(adc_extsel_event trigger){
adc_set_extsel(_dev, trigger);
}
/*
this function will set the continuous conversion bit.
*/
void STM32ADC::setContinuous(){
_dev->regs->CR2 |= ADC_CR2_CONT;
};
/*
this function will reset the continuous bit.
*/
void STM32ADC::resetContinuous(){
_dev->regs->CR2 &= ~ADC_CR2_CONT;
};
/*
This will be used to start conversions
*/
void STM32ADC::startConversion(){
_dev->regs->CR2 |= ADC_CR2_SWSTART;
}
/*
This will set the Scan Mode on.
This will use DMA.
*/
void STM32ADC::setScanMode(){
_dev->regs->CR1 |= ADC_CR1_SCAN;
}
void STM32ADC::calibrate() {
adc_calibrate(_dev);
}
/*
This function is used to setup DMA with the ADC.
It will be independent of the mode used. It will either be used in continuous or scan mode
or even both... go figure. :)
The reason why this is a uint16 is that I am not ready for dual mode.
*/
void STM32ADC::setDMA(uint16 * Buf, uint16 BufLen, uint32 dmaFlags, voidFuncPtr func) {
//initialize DMA
dma_init(DMA1);
//if there is an int handler to be called...
if (func != NULL)
dma_attach_interrupt(DMA1, DMA_CH1, func);
//enable ADC DMA transfer
//adc_dma_enable(ADC1);
_dev->regs->CR2 |= ADC_CR2_DMA;
//set it up...
dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_16BITS, Buf, DMA_SIZE_16BITS, dmaFlags);// Receive buffer DMA
//how many are we making??
dma_set_num_transfers(DMA1, DMA_CH1, BufLen);
//enable dma.
dma_enable(DMA1, DMA_CH1); // Enable the channel and start the transfer.
}
/*
This function is used to setup DMA with the ADC.
It will be independent of the mode used. It will either be used in continuous or scan mode
or even both...
This function is to be used with Dual ADC (the difference is to use 32bit buffers).
*/
void STM32ADC::setDualDMA(uint32 * Buf, uint16 BufLen, uint32 Flags){
dma_init(DMA1);
adc_dma_enable(_dev);
dma_setup_transfer(DMA1, DMA_CH1, &_dev->regs->DR, DMA_SIZE_32BITS,//(DMA_MINC_MODE | DMA_CIRC_MODE)
Buf, DMA_SIZE_32BITS, Flags);// Receive buffer DMA
dma_set_num_transfers(DMA1, DMA_CH1, BufLen);
dma_enable(DMA1, DMA_CH1); // Enable the channel and start the transfer.
}
/*
This will set the Scan Mode on.
This will use DMA.
*/
void STM32ADC::attachDMAInterrupt(voidFuncPtr func){
_DMA_int = func;
dma_attach_interrupt(DMA1, DMA_CH1, func);
}
/*
This will set an Analog Watchdog on a channel.
It must be used with a channel that is being converted.
*/
void STM32ADC::setAnalogWatchdog(uint8 channel, uint32 HighLimit, uint32 LowLimit){
set_awd_low_limit(_dev, LowLimit);
set_awd_high_limit(_dev, HighLimit);
set_awd_channel(_dev, channel);
}
/*
check analog watchdog
Poll the status on the watchdog. This will return and reset the bit.
*/
uint8 STM32ADC::getAnalogWatchdog(){
return 1;
}
/*
Attach an interrupt to the Watchdog...
This can possibly be set together in one function and determine which peripheral
it relates to.
*/
void STM32ADC::attachAnalogWatchdogInterrupt(voidFuncPtr func){
_AWD_int = func;
}

View File

@ -0,0 +1,158 @@
#include "utility/util_adc.h"
#include "libmaple/dma.h"
class STM32ADC{
public:
/*
Constructor
Choose which ADC to use.
Start it up...
*/
STM32ADC (adc_dev * dev);
/*
Set the ADC Sampling Rate.
*/
void setSampleRate(adc_smp_rate SampleRate);
/*
Attach an interrupt to the ADC completion.
*/
void attachInterrupt(voidFuncPtr func, uint8 interrupt);
void calibrate();
/*
This function is used to setup DMA with the ADC.
It will be independent of the mode used. It will either be used in continuous or scan mode
or even both... go figure. :)
The reason why this is a uint16 is that I am not ready for dual mode.
*/
void setDMA(uint16 * Buf, uint16 BufLen, uint32 dmaFlags, voidFuncPtr func);
/*
This function is used to setup DMA with the ADC.
It will be independent of the mode used. It will either be used in continuous or scan mode
or even both...
This function is to be used with Dual ADC (the difference is to use 32bit buffers).
*/
void setDualDMA(uint32 * Buf, uint16 BufLen, uint32 Flags);
/*
This will enable the internal readings. Vcc and Temperature
*/
void enableInternalReading();
/*
This will read the Vcc and return something useful.
Polling is being used.
*/
float readVcc();
/*
This will read the Temperature and return something useful.
Polling is being used.
*/
float readTemp();
/*
This function will set the number of channels to convert
And which channels.
For pin numbers, see setPins below
*/
void setChannels(uint8 *pins, uint8 length);
/*
This function will set the number of pins to convert
And which pins.
*/
void setPins(uint8 *pins, uint8 length);
/*
This function will set the trigger to start the conversion
Timer, SWStart, etc...
Possible triggers:
ADC_EXT_EV_TIM1_CC1
ADC_EXT_EV_TIM1_CC2
ADC_EXT_EV_TIM2_CC2
ADC_EXT_EV_TIM3_TRGO
ADC_EXT_EV_TIM4_CC4
ADC_EXT_EV_EXTI11
ADC_EXT_EV_TIM1_CC3
ADC_EXT_EV_SWSTART
ADC_EXT_EV_TIM3_CC1
ADC_EXT_EV_TIM2_CC3
ADC_EXT_EV_TIM8_CC1
ADC_EXT_EV_ADC3_TIM8_TRGO
ADC_EXT_EV_TIM5_CC1
ADC_EXT_EV_ADC12_TIM8_TRGO
ADC_EXT_EV_TIM5_CC3
*/
void setTrigger(adc_extsel_event trigger);
/*
this function will set the continuous conversion bit.
*/
void setContinuous();
/*
this function will reset the continuous bit.
*/
void resetContinuous();
/*
This will be used to start conversions
*/
void startConversion();
/*
This will set the Scan Mode on.
This will use DMA.
*/
void setScanMode();
/*
This will set the Scan Mode on.
This will use DMA.
*/
void attachDMAInterrupt(voidFuncPtr func);
/*
This will set an Analog Watchdog on a channel.
It must be used with a channel that is being converted.
*/
void setAnalogWatchdog(uint8 channel, uint32 HighLimit, uint32 LowLimit);
/*
check analog watchdog
Poll the status on the watchdog. This will return and reset the bit.
*/
uint8 getAnalogWatchdog();
/*
Attach an interrupt to the Watchdog...
This can possibly be set together in one function and determine which peripheral
it relates to.
*/
void attachAnalogWatchdogInterrupt(voidFuncPtr func);
/*
Retrieve the contents of the DR register.
*/
uint32 getData();
private:
adc_dev * _dev;
voidFuncPtr _DMA_int;
voidFuncPtr _ADC_int;
voidFuncPtr _AWD_int;
static const float _AverageSlope = 4.3; // mV/oC //4.0 to 4.6
static const float _V25 = 1.43; //Volts //1.34 - 1.52
};

View File

@ -0,0 +1,205 @@
#include "util_adc.h"
#include<libmaple/nvic.h>
//#include "boards.h"
#include <libmaple/dma.h>
/*
Interrupt function.
This handles Analog watchdog and ADC1 and 2.
*/
extern volatile unsigned int adc_result = 0;
/*
Starts a single conversion in one channel previously defined.
Results must be read through interrupt or polled outside this function.
*/
void start_single_convert(adc_dev* dev, uint8 channel) {
// int pinMapADCin = PIN_MAP[analogInPin].adc_channel;
adc_set_reg_seqlen(dev, 1);
dev->regs->SQR3 = channel;//use channels next time.
dev->regs->CR2 |= ADC_CR2_SWSTART;
}
/*
Starts the continuous mode on one channel of the AD.
Results must be read through interrupt or polled outside this function.
*/
void start_continuous_convert(adc_dev* dev, uint8 channel){
// int pinMapADCin = PIN_MAP[analogInPin].adc_channel;
adc_set_reg_seqlen(dev, 1);
dev->regs->SQR3 = channel;
dev->regs->CR2 |= ADC_CR2_CONT;
dev->regs->CR2 |= ADC_CR2_SWSTART;
}
/*
Enable end of conversion interrupt on the ADC.
This is for regular conversion, not injected.
*/
void enable_adc_irq(adc_dev* dev) {//ADC1 for now.
dev->regs->CR1 |= (1U<<ADC_CR1_EOCIE_BIT);
nvic_irq_enable(NVIC_ADC_1_2 );
}
/*
Enable the reading of the internal variables (Temperature and Vref).
*/
void enable_internal_reading(adc_dev *dev) {
dev->regs->CR2 |= ADC_CR2_TSEREFE;
}
/*
Read internal variables.
Channels are:
16 - Temperature
17 - VrefInt
Results must be read through interrupt or polled outside this function.
*/
void internalRead(adc_dev *dev, uint8 channel) {
adc_reg_map *regs = dev->regs;
adc_set_reg_seqlen(dev, 1);
regs->SQR3 = channel;
regs->CR2 |= ADC_CR2_SWSTART;
}
/*
Enable the Analog Watchdog interrupt
*/
void enable_awd_irq(adc_dev * dev){
dev->regs->CR1 |= (1U<<ADC_CR1_AWDIE_BIT);
nvic_irq_enable(NVIC_ADC_1_2 );
}
/*
Set Analog Watchdog Low Limit.
Results must be read through interrupt or polled outside this function.
*/
void set_awd_low_limit(adc_dev * dev, uint32 limit) {
dev->regs->LTR = limit;
}
/*
Set Analog Watchdog High Limit.
Results must be read through interrupt or polled outside this function.
*/
void set_awd_high_limit(adc_dev * dev, uint32 limit) {
dev->regs->HTR = limit;
}
/*
Enable the Watchdog function on the ADC.
*/
void set_awd_channel(adc_dev * dev, uint8 awd_channel){
dev->regs->CR1 |= (awd_channel & ADC_CR1_AWDCH);
}
/*
Enable the Watchdog function on the ADC.
*/
void enable_awd(adc_dev * dev){
dev->regs->CR1 |= ADC_CR1_AWDEN;
}
/*
Used to configure the sequence and length of the scan mode.
Can be used instead of adc_set_reg_seqlen() as it sets both information
The channels are not the pin numbers, but ADC channels.
*/
void adc_set_reg_seq_channel(adc_dev * dev, unsigned char *channels, unsigned char len){
unsigned int records[3] = {0,0,0};
unsigned char i = 0, j = 0;
//run away protection
if (len > 16) len = 16;
//write the length
records[2] |= (len - 1) << 20;
//i goes through records, j goes through variables.
for (i = 0, j = 0; i < len; i++) {//go through the channel list.
if (i!=0 && i%6 == 0) j++;//next variable, please!!
records[j] |= (channels[i] << ((i%6)*5));
}
//update the registers inside with the scan sequence.
dev->regs->SQR1 = records[2];
dev->regs->SQR2 = records[1];
dev->regs->SQR3 = records[0];
};
void adc_dma_disable(adc_dev * dev) {
bb_peri_set_bit(&dev->regs->CR2, ADC_CR2_DMA_BIT, 0);
}
void adc_dma_enable(adc_dev * dev) {
bb_peri_set_bit(&dev->regs->CR2, ADC_CR2_DMA_BIT, 1);
}
uint8 poll_adc_convert(adc_dev *dev) {
return bb_peri_get_bit(dev->regs->SR, ADC_SR_EOC_BIT);
}
//uint8 maxSamples = 32;
//uint32_t dataPoints32[maxSamples / 2];
//uint16_t *dataPoints = (uint16_t *)&dataPoints32;
/*
//fast interleaved mode
void setADCs (uint8 analogInPin)
{
// const adc_dev *dev = PIN_MAP[analogInPin].adc_device;
int pinMapADCin = PIN_MAP[analogInPin].adc_channel;
adc_set_sample_rate(ADC1, ADC_SMPR_1_5); //=0,58uS/sample. ADC_SMPR_13_5 = 1.08uS - use this one if Rin>10Kohm,
adc_set_sample_rate(ADC2, ADC_SMPR_1_5); // if not may get some sporadic noise. see datasheet.
// adc_reg_map *regs = dev->regs;
adc_set_reg_seqlen(ADC1, 1);
ADC1->regs->SQR3 = pinMapADCin;
ADC1->regs->CR2 |= ADC_CR2_CONT; // | ADC_CR2_DMA; // Set continuous mode and DMA
ADC1->regs->CR1 |= ADC_CR1_FASTINT; // Interleaved mode
ADC1->regs->CR2 |= ADC_CR2_SWSTART;
ADC2->regs->CR2 |= ADC_CR2_CONT; // ADC 2 continuos
ADC2->regs->SQR3 = pinMapADCin;
}
*/
/*
This is run inside the loop() function.
It stays active and polls for the end of the transfer.
*/
/*
void takeSamples ()
{
// This loop uses dual interleaved mode to get the best performance out of the ADCs
//
unsigned long samplingTime=0;
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH1, DMA1_CH1_Event);
adc_dma_enable(ADC1);
dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_32BITS,
dataPoints32, DMA_SIZE_32BITS, (DMA_MINC_MODE | DMA_TRNS_CMPLT));// Receive buffer DMA
dma_set_num_transfers(DMA1, DMA_CH1, maxSamples / 2);
dma1_ch1_Active = 1;
// regs->CR2 |= ADC_CR2_SWSTART; //moved to setADC
dma_enable(DMA1, DMA_CH1); // Enable the channel and start the transfer.
//adc_calibrate(ADC1);
//adc_calibrate(ADC2);
samplingTime = micros();
while (dma1_ch1_Active);
samplingTime = (micros() - samplingTime);
dma_disable(DMA1, DMA_CH1); //End of trasfer, disable DMA and Continuous mode.
// regs->CR2 &= ~ADC_CR2_CONT;
}
*/

View File

@ -0,0 +1,30 @@
#include <libmaple/adc.h>
void start_single_convert(adc_dev* dev, uint8 channel);
void start_continuous_convert(adc_dev* dev, uint8 channel);
void enable_adc_irq(adc_dev* dev);
void enable_internal_reading(adc_dev *dev);
void internalRead(adc_dev *dev, uint8 channel);
void enable_awd_irq( adc_dev * dev);
void set_awd_low_limit( adc_dev * dev, uint32 limit);
void set_awd_high_limit( adc_dev * dev, uint32 limit);
void enable_awd( adc_dev * dev);
void set_awd_channel( adc_dev * dev, uint8 awd_channel);
void adc_set_reg_seq_channel( adc_dev * dev, unsigned char *channels, unsigned char len);
void set_continuous( adc_dev * dev);
uint8 poll_adc_convert(adc_dev *dev);
void adc_dma_enable(adc_dev * dev);

43
STM32F1/system/libmaple/include/libmaple/adc.h Normal file → Executable file
View File

@ -42,6 +42,7 @@ extern "C"{
#include <libmaple/libmaple.h>
#include <libmaple/bitband.h>
#include <libmaple/rcc.h>
#include <libmaple/nvic.h>
/* We include the series header below, after defining the register map
* and device structs. */
@ -73,12 +74,30 @@ typedef struct adc_reg_map {
__io uint32 DR; ///< Regular data register
} adc_reg_map;
/** ADC device type. */
typedef struct adc_dev {
adc_reg_map *regs; /**< Register map */
rcc_clk_id clk_id; /**< RCC clock information */
adc_reg_map *regs; /**< Register map */
rcc_clk_id clk_id; /**< RCC clock information */
nvic_irq_num irq_num; /* Added by bubulindo */
voidFuncPtr handlers[]; /* Added by bubulindo EOC, JEOC, AWD Interrupts*/
} adc_dev;
//Added by bubulindo - Interrupt ID's for ADC
typedef enum adc_interrupt_id {
ADC_EOC, /**< Update interrupt. */
ADC_AWD , /**< Capture/compare 1 interrupt. */
ADC_JEOC,
//ADC_JSTRT,
//ADC_STRT, /**Analog WatchDog interrupt */
} adc_interrupt_id;
//Added by bubulindo
void adc_enable_irq(adc_dev* dev, uint8 interrupt);
void adc_attach_interrupt(adc_dev *dev, uint8 interrupt, voidFuncPtr handler);
/* Pull in the series header (which may need the above struct
* definitions).
*
@ -244,10 +263,10 @@ typedef struct adc_dev {
* Routines
*/
void adc_init(const adc_dev *dev);
void adc_set_extsel(const adc_dev *dev, adc_extsel_event event);
void adc_set_sample_rate(const adc_dev *dev, adc_smp_rate smp_rate);
uint16 adc_read(const adc_dev *dev, uint8 channel);
void adc_init(adc_dev *dev);
void adc_set_extsel(adc_dev *dev, adc_extsel_event event);
void adc_set_sample_rate(adc_dev *dev, adc_smp_rate smp_rate);
uint16 adc_read(adc_dev *dev, uint8 channel);
/**
* @brief Set the ADC prescaler.
@ -260,7 +279,7 @@ extern void adc_set_prescaler(adc_prescaler pre);
* @brief Call a function on all ADC devices.
* @param fn Function to call on each ADC device.
*/
extern void adc_foreach(void (*fn)(const adc_dev*));
extern void adc_foreach(void (*fn)(adc_dev*));
struct gpio_dev;
/**
@ -270,7 +289,7 @@ struct gpio_dev;
* @param gdev GPIO device to configure.
* @param bit Bit on gdev to configure for ADC conversion.
*/
extern void adc_config_gpio(const struct adc_dev *dev,
extern void adc_config_gpio(struct adc_dev *dev,
struct gpio_dev *gdev,
uint8 bit);
@ -284,7 +303,7 @@ extern void adc_config_gpio(const struct adc_dev *dev,
* @param dev Device to enable.
* @see adc_read()
*/
extern void adc_enable_single_swstart(const adc_dev* dev);
extern void adc_enable_single_swstart(adc_dev* dev);
/**
* @brief Set the regular channel sequence length.
@ -295,7 +314,7 @@ extern void adc_enable_single_swstart(const adc_dev* dev);
* @param dev ADC device.
* @param length Regular channel sequence length, from 1 to 16.
*/
static inline void adc_set_reg_seqlen(const adc_dev *dev, uint8 length) {
static inline void adc_set_reg_seqlen(adc_dev *dev, uint8 length) {
uint32 tmp = dev->regs->SQR1;
tmp &= ~ADC_SQR1_L;
tmp |= (length - 1) << 20;
@ -306,7 +325,7 @@ static inline void adc_set_reg_seqlen(const adc_dev *dev, uint8 length) {
* @brief Enable an adc peripheral
* @param dev ADC device to enable
*/
static inline void adc_enable(const adc_dev *dev) {
static inline void adc_enable(adc_dev *dev) {
*bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 1;
}
@ -314,7 +333,7 @@ static inline void adc_enable(const adc_dev *dev) {
* @brief Disable an ADC peripheral
* @param dev ADC device to disable
*/
static inline void adc_disable(const adc_dev *dev) {
static inline void adc_disable(adc_dev *dev) {
*bb_perip(&dev->regs->CR2, ADC_CR2_ADON_BIT) = 0;
}

26
STM32F1/system/libmaple/stm32f1/include/series/adc.h Normal file → Executable file
View File

@ -43,12 +43,12 @@
* Devices
*/
extern adc_dev adc1;
extern const struct adc_dev *ADC1;
extern struct adc_dev *ADC1;
extern adc_dev adc2;
extern const struct adc_dev *ADC2;
extern struct adc_dev *ADC2;
#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
extern adc_dev adc3;
extern const struct adc_dev *ADC3;
extern struct adc_dev *ADC3;
#endif
/*
@ -87,8 +87,24 @@ extern const struct adc_dev *ADC3;
#define ADC_CR2_DMA (1U << ADC_CR2_DMA_BIT)
#define ADC_CR2_ALIGN (1U << ADC_CR2_ALIGN_BIT)
#define ADC_CR2_JEXTSEL 0x7000
#define ADC_CR2_JEXTSEL_TIM1_TRGO (0x0 << 12)
#define ADC_CR2_JEXTSEL_TIM1_CC4 (0x1 << 12)
#define ADC_CR2_JEXTSEL_TIM2_TRGO (0x2 << 12)
#define ADC_CR2_JEXTSEL_TIM2_CC1 (0x3 << 12)
#define ADC_CR2_JEXTSEL_TIM3_CC4 (0x4 << 12)
#define ADC_CR2_JEXTSEL_TIM4_TRGO (0x5 << 12)
#define ADC_CR2_JEXTSEL_EXTI15 (0x6 << 12)
#define ADC_CR2_JEXTSEL_JSWSTART (0x7 << 12)
#define ADC_CR2_JEXTTRIG (1U << ADC_CR2_JEXTTRIG_BIT)
#define ADC_CR2_EXTSEL 0xE0000
#define ADC_CR2_EXTSEL_TIM1_CC1 (0x0 << 17)
#define ADC_CR2_EXTSEL_TIM1_CC2 (0x1 << 17)
#define ADC_CR2_EXTSEL_TIM1_CC3 (0x2 << 17)
#define ADC_CR2_EXTSEL_TIM2_CC2 (0x3 << 17)
#define ADC_CR2_EXTSEL_TIM3_TRGO (0x4 << 17)
#define ADC_CR2_EXTSEL_TIM4_CC4 (0x5 << 17)
#define ADC_CR2_EXTSEL_EXTI11 (0x6 << 17)
#define ADC_CR2_EXTSEL_SWSTART (0x7 << 17)
#define ADC_CR2_EXTTRIG (1U << ADC_CR2_EXTTRIG_BIT)
#define ADC_CR2_JSWSTART (1U << ADC_CR2_JSWSTART_BIT)
#define ADC_CR2_SWSTART (1U << ADC_CR2_SWSTART_BIT)
@ -237,7 +253,7 @@ typedef enum adc_prescaler {
* Routines
*/
void adc_calibrate(const adc_dev *dev);
void adc_calibrate(adc_dev *dev);
/**
* @brief Set external trigger conversion mode event for regular channels
@ -248,7 +264,7 @@ void adc_calibrate(const adc_dev *dev);
* @param enable If 1, conversion on external events is enabled; if 0,
* disabled.
*/
static inline void adc_set_exttrig(const adc_dev *dev, uint8 enable) {
static inline void adc_set_exttrig(adc_dev *dev, uint8 enable) {
*bb_perip(&dev->regs->CR2, ADC_CR2_EXTTRIG_BIT) = !!enable;
}

2
STM32F1/variants/maple_mini/wirish/boards.cpp Normal file → Executable file
View File

@ -179,7 +179,7 @@ nvic_init((uint32)VECT_TAB_ADDR, 0);
*/
}
static void adc_default_config(const adc_dev *dev) {
static void adc_default_config(adc_dev *dev) {
adc_enable_single_swstart(dev);
adc_set_sample_rate(dev, wirish::priv::w_adc_smp);
}