initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,666 @@
//*****************************************************************************
//
//! @file adc_vbatt.c
//!
//! @brief Example of ADC sampling VBATT voltage divider, BATT load, and temperature.
//!
//! Purpose: This example initializes the ADC, and a timer. Two times per second it
//! reads the VBATT voltage divider and temperature sensor and prints them.
//! It monitors button 0 and if pressed, it turns on the BATT LOAD resistor.
//! One should monitor MCU current to see when the load is on or off.
//!
//! Printing takes place over the ITM at 1M Baud.
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include "am_mcu_apollo.h"
#include "am_bsp.h"
#include "am_util.h"
//*****************************************************************************
//
// Global Variables
//
//*****************************************************************************
//
// ADC Device Handle.
//
static void *g_ADCHandle;
//
// Sample Count semaphore from ADC ISR to base level.
//
uint32_t g_ui32SampleCount;
//
// ADC code for voltage divider from ADC ISR to base level.
//
uint16_t g_ui16ADCVDD_code;
//
// ADC code for temperature sensor from ADC ISR to base level.
//
uint16_t g_ui16ADCTEMP_code;
//*****************************************************************************
//
// ADC Configuration
//
//*****************************************************************************
const static am_hal_adc_config_t g_sADC_Cfg =
{
//
// Select the ADC Clock source.
//
.eClock = AM_HAL_ADC_CLKSEL_HFRC_DIV2,
//
// Polarity
//
.ePolarity = AM_HAL_ADC_TRIGPOL_RISING,
//
// Select the ADC trigger source using a trigger source macro.
//
.eTrigger = AM_HAL_ADC_TRIGSEL_SOFTWARE,
//
// Select the ADC reference voltage.
//
.eReference = AM_HAL_ADC_REFSEL_INT_1P5,
.eClockMode = AM_HAL_ADC_CLKMODE_LOW_POWER,
//
// Choose the power mode for the ADC's idle state.
//
.ePowerMode = AM_HAL_ADC_LPMODE1,
//
// Enable repeating samples using Timer3A.
//
.eRepeat = AM_HAL_ADC_REPEATING_SCAN
};
//*****************************************************************************
//
// Timer configurations.
//
//*****************************************************************************
am_hal_ctimer_config_t g_sTimer3 =
{
// do not link A and B together to make a long 32-bit counter.
0,
// Set up timer 3A to drive the ADC
(AM_HAL_CTIMER_FN_PWM_REPEAT |
AM_HAL_CTIMER_LFRC_32HZ),
// Timer 3B is not used in this example.
0,
};
//*****************************************************************************
//
// ADC Interrupt Service Routine (ISR)
//
//*****************************************************************************
void
am_adc_isr(void)
{
uint32_t ui32IntStatus;
//
// Clear timer 3 interrupt.
//
am_hal_adc_interrupt_status(g_ADCHandle, &ui32IntStatus, true);
am_hal_adc_interrupt_clear(g_ADCHandle, ui32IntStatus);
//
// Toggle LED 3.
//
am_devices_led_toggle(am_bsp_psLEDs, 3);
//
// Keep grabbing samples from the ADC FIFO until it goes empty.
//
#if 0
//! @param pHandle - handle for the module instance.
//! @param ui32SlotNumber - desired slot number to filter samples on.
//! If set to AM_HAL_ADC_MAX_SLOTS then all
//! values will be provided.
//! @param ui32BufferSize - number of entries in the sample buffer.
//! If 0 then samples will be read directly
//! from the FIFO.
//! @param pui32SampleBuffer - pointer to the input sample buffer.
//! @param pui32NumberSamples - returns the number of samples found.
//! @param pui32Samples - pointer to a sample buffer to process.
//! If NULL then samples will be read directly
//! from the FIFO.
uint32_t am_hal_adc_samples_read(void *pHandle,
uint32_t *pui32InSampleBuffer,
uint32_t *pui32InOutNumberSamples,
am_hal_adc_sample_t *pui32OutBuffer)
typedef struct
{
uint32_t ui32Sample;
uint32_t ui32Slot;
} am_hal_adc_sample_t;
#endif
uint32_t ui32NumSamples = 1;
am_hal_adc_sample_t sSample;
//
// Go get the sample.
//
#if 0 // Need to get the FULL sample. The HAL call doesn't currently return the full.
uint32_t ui32fifodata = ADC->FIFOPR;
//
// Select which one of the two enabled slots is here right now.
//
if ( AM_HAL_ADC_FIFO_SLOT(ui32fifodata) == 5)
{
//
// Just grab the ADC code for the battery voltage divider.
//
g_ui16ADCVDD_code = AM_HAL_ADC_FIFO_FULL_SAMPLE(ui32fifodata);
}
else
{
//
// Just grab the ADC code for the temperature sensor.
// We need the integer part in the low 16-bits.
//
g_ui16ADCTEMP_code = AM_HAL_ADC_FIFO_FULL_SAMPLE(ui32fifodata) & 0xFFC0;
}
#elif 1
//
// Emtpy the FIFO, we'll just look at the last one read.
//
while ( AM_HAL_ADC_FIFO_COUNT(ADC->FIFO) )
{
ui32NumSamples = 1;
am_hal_adc_samples_read(g_ADCHandle, true, NULL, &ui32NumSamples, &sSample);
//
// Determine which slot it came from?
//
if (sSample.ui32Slot == 5 )
{
//
// The returned ADC sample is for the battery voltage divider.
//
g_ui16ADCVDD_code = AM_HAL_ADC_FIFO_SAMPLE(sSample.ui32Sample);
}
else
{
//
// The returned ADC sample is for the temperature sensor.
// We need the integer part in the low 16-bits.
//
g_ui16ADCTEMP_code = sSample.ui32Sample & 0xFFC0;
}
}
#else
#endif
#if 0
uint32_t ui32fifodata, ui32IntStatus;
while ( AM_HAL_ADC_FIFO_COUNT(am_hal_adc_fifo_peek()) )
{
ui32fifodata = am_hal_adc_fifo_pop();
//
// Select which one of the two enabled slots is here right now.
//
if ( AM_HAL_ADC_FIFO_SLOT(ui32fifodata) == 5)
{
//
// Just grab the ADC code for the battery voltage divider.
//
g_ui16ADCVDD_code = AM_HAL_ADC_FIFO_FULL_SAMPLE(ui32fifodata);
}
else
{
//
// Just grab the ADC code for the temperature sensor.
// We need the integer part in the low 16-bits.
//
g_ui16ADCTEMP_code = AM_HAL_ADC_FIFO_FULL_SAMPLE(ui32fifodata) & 0xFFC0;
}
}
#endif
//
// Signal interrupt arrival to base level.
//
g_ui32SampleCount++;
}
//*****************************************************************************
//
// ADC INIT Function
//
//*****************************************************************************
void
adc_init(void)
{
am_hal_adc_slot_config_t sSlotCfg;
//
// Initialize the ADC and get the handle.
//
if ( AM_HAL_STATUS_SUCCESS != am_hal_adc_initialize(0, &g_ADCHandle) )
{
am_util_stdio_printf("Error - reservation of the ADC instance failed.\n");
}
//
// Power on the ADC.
//
if (AM_HAL_STATUS_SUCCESS != am_hal_adc_power_control(g_ADCHandle,
AM_HAL_SYSCTRL_WAKE,
false) )
{
am_util_stdio_printf("Error - ADC power on failed.\n");
}
//
// Configure the ADC.
//
if ( am_hal_adc_configure(g_ADCHandle, (am_hal_adc_config_t*)&g_sADC_Cfg) != AM_HAL_STATUS_SUCCESS )
{
am_util_stdio_printf("Error - configuring ADC failed.\n");
}
sSlotCfg.bEnabled = false;
sSlotCfg.bWindowCompare = false;
sSlotCfg.eChannel = AM_HAL_ADC_SLOT_CHSEL_SE0; // 0
sSlotCfg.eMeasToAvg = AM_HAL_ADC_SLOT_AVG_1; // 0
sSlotCfg.ePrecisionMode = AM_HAL_ADC_SLOT_14BIT; // 0
am_hal_adc_configure_slot(g_ADCHandle, 0, &sSlotCfg); // Unused slot
am_hal_adc_configure_slot(g_ADCHandle, 1, &sSlotCfg); // Unused slot
am_hal_adc_configure_slot(g_ADCHandle, 2, &sSlotCfg); // Unused slot
am_hal_adc_configure_slot(g_ADCHandle, 3, &sSlotCfg); // Unused slot
am_hal_adc_configure_slot(g_ADCHandle, 4, &sSlotCfg); // Unused slot
am_hal_adc_configure_slot(g_ADCHandle, 6, &sSlotCfg); // Unused slot
sSlotCfg.bEnabled = true;
sSlotCfg.bWindowCompare = true;
sSlotCfg.eChannel = AM_HAL_ADC_SLOT_CHSEL_BATT;
sSlotCfg.eMeasToAvg = AM_HAL_ADC_SLOT_AVG_1;
sSlotCfg.ePrecisionMode = AM_HAL_ADC_SLOT_14BIT;
am_hal_adc_configure_slot(g_ADCHandle, 5, &sSlotCfg); // BATT
sSlotCfg.bEnabled = true;
sSlotCfg.bWindowCompare = true;
sSlotCfg.eChannel = AM_HAL_ADC_SLOT_CHSEL_TEMP;
sSlotCfg.eMeasToAvg = AM_HAL_ADC_SLOT_AVG_1;
sSlotCfg.ePrecisionMode = AM_HAL_ADC_SLOT_10BIT;
am_hal_adc_configure_slot(g_ADCHandle, 7, &sSlotCfg); // TEMP
//
// Enable the ADC.
//
am_hal_adc_enable(g_ADCHandle);
}
//*****************************************************************************
//
// Enable the ADC INIT TIMER 3A function and set for 0.5 second period.
//
//*****************************************************************************
static void
timer_init(void)
{
//
// Only CTIMER 3 supports the ADC.
//
#define TIMERNUM 3
uint32_t ui32Period = 2000; // Set for 2 second (2000ms) period
//
// LFRC has to be turned on for this example because we are running this
// timer off of the LFRC.
//
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_LFRC_START, 0);
//
// Set up timer 3A so start by clearing it.
//
am_hal_ctimer_clear(TIMERNUM, AM_HAL_CTIMER_TIMERA);
//
// Configure the timer to count 32Hz LFRC clocks but don't start it yet.
//
am_hal_ctimer_config(TIMERNUM, &g_sTimer3);
//
// Compute CMPR value needed for desired period based on a 32HZ clock.
//
ui32Period = ui32Period * 32 / 1000;
am_hal_ctimer_period_set(TIMERNUM, AM_HAL_CTIMER_TIMERA,
ui32Period, (ui32Period >> 1));
#if 0
//
// // Enable the timer output "pin". This refers to the pin as seen from
// // inside the timer. The actual GPIO pin is neither enabled nor driven.
// am_hal_ctimer_pin_enable(TIMERNUM, AM_HAL_CTIMER_TIMERA);
#endif
#if 0
//
// CTimer A3 is available on the following pads: 5, 22, 31, 43, 48, 37.
// On the apollo3_evb, only pin 31 is unused, so we'll pick it.
//
am_hal_ctimer_output_config(TIMERNUM, AM_HAL_CTIMER_TIMERA, 31,
AM_HAL_CTIMER_OUTPUT_FORCE0,
AM_HAL_GPIO_PIN_DRIVESTRENGTH_2MA);
#endif
//
// Set up timer 3A as the trigger source for the ADC.
//
am_hal_ctimer_adc_trigger_enable();
#if 0
//
// Clear the timer Interrupt
//
am_hal_ctimer_int_clear(AM_HAL_CTIMER_INT_TIMERA0 << (TIMERNUM * 2));
#endif
//
// Start timer 3A.
//
am_hal_ctimer_start(TIMERNUM, AM_HAL_CTIMER_TIMERA);
} // timer_init()
//*****************************************************************************
//
// Main function.
//
//*****************************************************************************
int
main(void)
{
bool bMeasured;
float fTempF;
int32_t i32BaseLevelCount;
const float fReferenceVoltage = 1.5;
float fVBATT;
float fADCTempVolts;
float fADCTempDegreesC;
// float fTemp, fVoltage, fOffset;
float fTrims[4];
uint32_t ui32Retval;
//
// Set the clock frequency.
//
am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_SYSCLK_MAX, 0);
//
// Set the default cache configuration
//
am_hal_cachectrl_config(&am_hal_cachectrl_defaults);
am_hal_cachectrl_enable();
//
// Configure the board for low power operation.
//
am_bsp_low_power_init();
//
// Initialize device drivers for the LEDs on the board.
//
am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS);
//
// Configure the button pin.
//
am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_AM_BSP_GPIO_BUTTON0);
//
// Initialize the printf interface for ITM output
//
am_bsp_itm_printf_enable();
//
// Clear the terminal screen, and print a quick message to show that we're
// alive.
//
am_util_stdio_terminal_clear();
am_util_stdio_printf("ADC VBATT and Temperature Sensing Example.\n");
//
// Enable floating point.
//
am_hal_sysctrl_fpu_enable();
am_hal_sysctrl_fpu_stacking_enable(true);
//
// Initialize the ADC.
//
adc_init();
//
// Initialize CTIMER 3A to trigger the ADC every 0.5 seconds.
//
timer_init();
//
// Print out ctimer initial register state.
//
am_util_stdio_printf("\n");
am_util_stdio_printf("CTIMER3=0x%08X @ 0x%08X\n",
AM_REGVAL(CTIMERADDRn(CTIMER, 3, TMR0)),
CTIMERADDRn(CTIMER, 3, TMR0));
am_util_stdio_printf("CTIMER3=0x%08X @ 0x%08X\n",
AM_REGVAL(CTIMERADDRn(CTIMER, 3, CMPRA0)),
CTIMERADDRn(CTIMER, 3, CMPRA0));
am_util_stdio_printf("CTIMER3=0x%08X @ 0x%08X\n",
AM_REGVAL(CTIMERADDRn(CTIMER, 3, CMPRB0)),
CTIMERADDRn(CTIMER, 3, CMPRB0));
am_util_stdio_printf("CTIMER3=0x%08X @ 0x%08X\n",
AM_REGVAL(CTIMERADDRn(CTIMER, 3, CTRL0)),
CTIMERADDRn(CTIMER, 3, CTRL0));
//
// Print out ADC initial register state.
//
am_util_stdio_printf("\n");
am_util_stdio_printf("ADC REGISTERS @ 0x%08X\n", (uint32_t)REG_ADC_BASEADDR);
am_util_stdio_printf("ADC CFG = 0x%08X\n", ADC->CFG);
am_util_stdio_printf("ADC SLOT0 = 0x%08X\n", ADC->SL0CFG);
am_util_stdio_printf("ADC SLOT1 = 0x%08X\n", ADC->SL1CFG);
am_util_stdio_printf("ADC SLOT2 = 0x%08X\n", ADC->SL2CFG);
am_util_stdio_printf("ADC SLOT3 = 0x%08X\n", ADC->SL3CFG);
am_util_stdio_printf("ADC SLOT4 = 0x%08X\n", ADC->SL4CFG);
am_util_stdio_printf("ADC SLOT5 = 0x%08X\n", ADC->SL5CFG);
am_util_stdio_printf("ADC SLOT6 = 0x%08X\n", ADC->SL6CFG);
am_util_stdio_printf("ADC SLOT7 = 0x%08X\n", ADC->SL7CFG);
//
// Print out the temperature trim values as recorded in OTP.
//
fTrims[0] = fTrims[1] = fTrims[2] = 0.0F;
fTrims[3] = -123.456f;
am_hal_adc_control(g_ADCHandle, AM_HAL_ADC_REQ_TEMP_TRIMS_GET, fTrims);
bMeasured = fTrims[3] ? true : false;
am_util_stdio_printf("\n");
am_util_stdio_printf("TRIMMED TEMP = %.3f\n", fTrims[0]);
am_util_stdio_printf("TRIMMED VOLTAGE = %.3f\n", fTrims[1]);
am_util_stdio_printf("TRIMMED Offset = %.3f\n", fTrims[2]);
am_util_stdio_printf("Note - these trim values are '%s' values.\n",
bMeasured ? "calibrated" : "uncalibrated default");
am_util_stdio_printf("\n");
//
// Enable the ADC interrupt in the NVIC.
//
NVIC_EnableIRQ(ADC_IRQn);
am_hal_interrupt_master_enable();
//
// Enable the ADC interrupts in the ADC.
//
am_hal_adc_interrupt_enable(g_ADCHandle, AM_HAL_ADC_INT_WCINC |
AM_HAL_ADC_INT_WCEXC |
AM_HAL_ADC_INT_FIFOOVR2 |
AM_HAL_ADC_INT_FIFOOVR1 |
AM_HAL_ADC_INT_SCNCMP |
AM_HAL_ADC_INT_CNVCMP);
//
// Reset the sample count which will be incremented by the ISR.
//
g_ui32SampleCount = 0;
//
// Kick Start Timer 3 with an ADC software trigger in REPEAT used.
//
am_hal_adc_sw_trigger(g_ADCHandle);
//
// Track buffer depth for progress messages.
//
i32BaseLevelCount = g_ui32SampleCount;
//
// Wait here for the ISR to grab a buffer of samples.
//
while (1)
{
//
// Print the battery voltage and temperature for each interrupt
//
if (g_ui32SampleCount > i32BaseLevelCount)
{
i32BaseLevelCount = g_ui32SampleCount;
//
// Compute the voltage divider output.
//
fVBATT = ((float)g_ui16ADCVDD_code) * 3.0f * fReferenceVoltage / (1024.0f / 64.0f);
//
// Print the voltage divider output.
//
am_util_stdio_printf("VBATT = <%.3f> (0x%04X) ",
fVBATT, g_ui16ADCVDD_code);
//
// Convert and scale the temperature.
// Temperatures are in Fahrenheit range -40 to 225 degrees.
// Voltage range is 0.825V to 1.283V
// First get the ADC voltage corresponding to temperature.
//
fADCTempVolts = ((float)g_ui16ADCTEMP_code) * fReferenceVoltage / (1024.0f * 64.0f);
//
// Now call the HAL routine to convert volts to degrees Celsius.
//
float fVT[3];
fVT[0] = fADCTempVolts;
fVT[1] = 0.0f;
fVT[2] = -123.456;
// fADCTempDegreesC = am_hal_adc_volts_to_celsius(fADCTempVolts);
ui32Retval = am_hal_adc_control(g_ADCHandle, AM_HAL_ADC_REQ_TEMP_CELSIUS_GET, fVT);
if ( ui32Retval == AM_HAL_STATUS_SUCCESS )
{
fADCTempDegreesC = fVT[1]; // Get the temperature
//
// print the temperature value in Celsius.
//
am_util_stdio_printf("TEMP = %.2f C (0x%04X) ",
fADCTempDegreesC, g_ui16ADCTEMP_code);
//
// Print the temperature value in Fahrenheit.
//
fTempF = (fADCTempDegreesC * (180.0f / 100.0f)) + 32.0f;
am_util_stdio_printf(" %.2f F", fTempF);
}
else
{
am_util_stdio_printf("Error: am_haL_adc_control returned %d\n", ui32Retval);
}
//
// Use button 0 to turn on or off the battery load resistor.
//
#if AM_PART_APOLLO
if (!am_hal_gpio_input_bit_read(AM_BSP_GPIO_BUTTON0))
{
am_util_stdio_printf("BATTERY LOAD RESISTOR ON\n");
am_hal_adc_batt_load_enable();
am_devices_led_on(am_bsp_psLEDs, 2);
}
else
{
am_util_stdio_printf("\n");
am_hal_adc_batt_load_disable();
am_devices_led_off(am_bsp_psLEDs, 2);
}
#else
am_util_stdio_printf("\n");
#endif
}
//
// Sleep here until the next ADC interrupt comes along.
//
am_devices_led_off(am_bsp_psLEDs, 0);
am_hal_sysctrl_sleep(AM_HAL_SYSCTRL_SLEEP_DEEP);
am_devices_led_on(am_bsp_psLEDs, 0);
}
}