rogerclarkmelbourne / Arduino_STM32

Arduino STM32. Hardware files to support STM32 boards, on Arduino IDE 1.8.x including LeafLabs Maple and other generic STM32F103 boards
Other
2.53k stars 1.26k forks source link

F4: updates in adc.h and adc.c #784

Closed ag88 closed 3 years ago

ag88 commented 4 years ago

initially i'm running into some errors: invalid conversion from 'const adc_dev*' while using ADC1, ADC2, ADC3

 extern const adc_dev adc1;
 extern const adc_dev adc2;
 extern const adc_dev adc3;

 #define ADC1 (&adc1)
 #define ADC2 (&adc2)
 #define ADC3 (&adc3)

those errors are resolved by defining them as follows

 extern const adc_dev adc1;
 extern const adc_dev adc2;
 extern const adc_dev adc3;

 #define ADC1 ((adc_dev*) &adc1)
 #define ADC2 ((adc_dev*) &adc2)
 #define ADC3 ((adc_dev*) &adc3)

adc.h

/**
 * @brief STM32F1/F4 ADC prescalers, as divisors of PCLK2.
 */
typedef enum adc_prescaler {
    /** PCLK2 divided by 2 */
    ADC_PRE_PCLK2_DIV_2 = 0,
    /** PCLK2 divided by 4 */
    ADC_PRE_PCLK2_DIV_4 = 1,
    /** PCLK2 divided by 6 */
    ADC_PRE_PCLK2_DIV_6 = 2,
    /** PCLK2 divided by 8 */
    ADC_PRE_PCLK2_DIV_8 = 3,
} adc_prescaler;

void adc_set_prescaler(adc_prescaler pre);

adc.c

/*
 *  @param pre, setup the ADC prescaler to one of PCLK /2, /4, /6, /8
 *         the templates per adc_prescaler enum
 */
void adc_set_prescaler(adc_prescaler pre) {

    ADC_COMMON->CCR &= ~ ADC_CCR_ADCPRE;
    ADC_COMMON->CCR |= ((pre & 3U) << ADC_CCR_ADCPRE_SHIFT);
}

adc.h

static inline void adc_awd_disable(const adc_dev * dev)
{
    dev->regs->CR1 &= ~ADC_CR1_AWDEN;
}

adc.h

//Added by bubulindo - Interrupt ID's for ADC
typedef enum {
    ADC_EOC,     // End Of Conversion interrupt.
    ADC_AWD ,    // Analog WatchDog interrupt
    ADC_JEOC,    // Injected End Of Conversion interrupt.
    ADC_OVR,     // overrun interrupt
    ADC_LAST_IRQ_ID
} adc_irq_id;

void adc_ovr_enable_irq(const adc_dev* dev);

adc.c

void adc_ovr_enable_irq(const adc_dev* dev)
{
    dev->regs->CR1 |= ADC_CR1_OVRIE;
    //nvic_irq_enable(NVIC_ADC_1_2_3);
}

void adc_attach_interrupt(const adc_dev *dev,
                            adc_irq_id irq_id,
                            voidFuncPtr handler)
{
    if (irq_id<ADC_LAST_IRQ_ID)
    {
        (*(dev->handler_p))[irq_id] = handler;
        if(irq_id == ADC_EOC)
            adc_enable_irq(dev);
        else if (irq_id == ADC_AWD)
            adc_awd_enable_irq(dev);
        else if (irq_id == ADC_OVR)
            adc_ovr_enable_irq(dev);
    }
}

/*
    ADC IRQ handler.
    added by bubulindo
*/
void adc_irq_handler(const adc_dev * dev)
{
    //End Of Conversion
    if (dev->regs->SR & ADC_SR_EOC) {
        dev->regs->SR = ~ADC_SR_EOC;
        voidFuncPtr handler = (*(dev->handler_p))[ADC_EOC];
        if (handler) {
            handler();
        }
    }
    //Injected End Of Conversion
    if (dev->regs->SR & ADC_SR_JEOC) {
        dev->regs->SR = ~ADC_SR_JEOC;
        voidFuncPtr handler = (*(dev->handler_p))[ADC_JEOC];
        if (handler) {
            handler();
        }
    }
    //Analog Watchdog
    if (dev->regs->SR & ADC_SR_AWD) {
        dev->regs->SR = ~ADC_SR_AWD;
        voidFuncPtr handler = (*(dev->handler_p))[ADC_AWD];
        if (handler) {
            handler();
        }
    }

    //ADC overrun interrupt
    if (dev->regs->SR & ADC_SR_OVR) {
        dev->regs->SR = ~ADC_SR_OVR;
        voidFuncPtr handler = (*(dev->handler_p))[ADC_OVR];
        if (handler) {
            handler();
        }
    }

}
ag88 commented 4 years ago

partly discussed in issue thread in steve's core https://github.com/stevstrong/Arduino_STM32/issues/41

stevstrong commented 4 years ago

Do you have a simple sketch to test the new functions?

ag88 commented 4 years ago

working on it (the example/test sketch), check back later ;)

ag88 commented 4 years ago

i've added a test sketch in STM32F4/libraries/STM32F4_ADC/examples/PR784F4AdcTest/

as i worked the test sketch, i actually found a bug in my code and fixed it that fix is commited here as well. as the tests include a few functionality, e.g. setting adc_prescaler, setting up awd and overrun interrupts. the tests evolved into a little app on its own, i made the sketch read the temperature sensor and use the analog watch dog to test the upper threshold which triggers when a higher temperature is detected. it responds to a single character command 'w' which toggles the watch dog on / off.

i observed something interesting during the tests, EOC may not be set if the awd triggers. i end up adding some codes, which checks for awd triggers and if the awd is triggered, i disable AWD and read the temperature sensor again. hence, the adc_awd_disable() function becomes handy.

to test adc overrun, you'd need to uncomment several lines of codes in loop(). the overruns occurs by triggering conversion ADC1->regs->CR2 |= ADC_CR2_SWSTART but not reading the ADC data register. i've added in addition a readme.md file which i hope may be helpful.

stevstrong commented 3 years ago

Seems to work ok, so I will merge it.