Files
IronOS/source/Core/BSP/Pinecilv2/Setup.cpp
2025-02-23 13:02:57 +11:00

230 lines
8.5 KiB
C++

/*
* Setup.c
*
* Created on: 29Aug.,2017
* Author: Ben V. Brown
*/
#include "Setup.h"
#include "BSP.h"
#include "Debug.h"
#include "FreeRTOSConfig.h"
#include "IRQ.h"
#include "Pins.h"
#include "bl702_dma.h"
#include "bl702_sec_eng.h"
#include "history.hpp"
#include <string.h>
#define ADC_NORM_SAMPLES 16
#define ADC_FILTER_LEN 4
uint16_t ADCReadings[ADC_NORM_SAMPLES]; // room for 32 lots of the pair of readings
// Heap
extern uint8_t _heap_start;
extern uint8_t _heap_size; // @suppress("Type cannot be resolved")
static HeapRegion_t xHeapRegions[] = {
{&_heap_start, (unsigned int)&_heap_size},
{ NULL, 0}, /* Terminates the array. */
{ NULL, 0} /* Terminates the array. */
};
// Functions
void setup_timer_scheduler(void);
void setup_pwm(void);
void setup_adc(void);
void hardware_init() {
vPortDefineHeapRegions(xHeapRegions);
HBN_Set_XCLK_CLK_Sel(HBN_XCLK_CLK_XTAL);
// Set capcode
{
uint32_t tmpVal = 0;
tmpVal = BL_RD_REG(AON_BASE, AON_XTAL_CFG);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, AON_XTAL_CAPCODE_IN_AON, 33);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, AON_XTAL_CAPCODE_OUT_AON, 33);
BL_WR_REG(AON_BASE, AON_XTAL_CFG, tmpVal);
}
Sec_Eng_Trng_Enable();
gpio_set_mode(OLED_RESET_Pin, GPIO_OUTPUT_MODE);
gpio_set_mode(KEY_A_Pin, GPIO_INPUT_PD_MODE);
gpio_set_mode(KEY_B_Pin, GPIO_INPUT_PD_MODE);
gpio_set_mode(TMP36_INPUT_Pin, GPIO_HZ_MODE);
gpio_set_mode(TIP_TEMP_Pin, GPIO_HZ_MODE);
gpio_set_mode(VIN_Pin, GPIO_HZ_MODE);
gpio_set_mode(TIP_RESISTANCE_SENSE, GPIO_OUTPUT_MODE);
gpio_write(TIP_RESISTANCE_SENSE, 0);
MSG((char *)"Pine64 Pinecilv2 Starting\r\n");
setup_timer_scheduler();
setup_adc();
setup_pwm();
I2C_SetSclSync(I2C0_ID, 1);
I2C_SetDeglitchCount(I2C0_ID, 1); // Turn on de-glitch
// Note on I2C clock rate @ 100Khz the screen update == 20ms which is too long for USB-PD to work
// 200kHz and above works
I2C_ClockSet(I2C0_ID, 300000); // Sets clock to around 25 kHz less than set here
TIMER_SetCompValue(TIMER_CH0, TIMER_COMP_ID_0, 0);
}
void setup_pwm(void) {
// Setup PWM we use for driving the tip
PWM_CH_CFG_Type cfg = {
PWM_Channel, // channel
PWM_CLK_XCLK, // Clock
PWM_STOP_ABRUPT, // Stop mode
PWM_POL_NORMAL, // Normal Polarity
60, // Clock Div
100, // Period
0, // Thres 1 - start at beginning
50, // Thres 2 - turn off at 50%
0, // Interrupt pulse count
};
PWM_Channel_Init(&cfg);
PWM_Channel_Disable(PWM_Channel);
}
const ADC_Chan_Type adc_tip_pos_chans[] = {TMP36_ADC_CHANNEL, TIP_TEMP_ADC_CHANNEL, VIN_ADC_CHANNEL, TIP_TEMP_ADC_CHANNEL,
TMP36_ADC_CHANNEL, TIP_TEMP_ADC_CHANNEL, VIN_ADC_CHANNEL, TIP_TEMP_ADC_CHANNEL};
const ADC_Chan_Type adc_tip_neg_chans[] = {ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND, ADC_CHAN_GND};
static_assert(sizeof(adc_tip_pos_chans) == sizeof(adc_tip_neg_chans));
void setup_adc(void) {
//
ADC_CFG_Type adc_cfg = {};
ADC_FIFO_Cfg_Type adc_fifo_cfg = {};
// Please also see PR #1529 for even more context
/*
A note on ADC settings
The bl70x ADC seems to be very sensitive to various analog settings.
It has been a challenge to determine what is the most correct way to
configure it in order to get accurate readings that can be transformed
into millivolts, for accurate measurements.
This latest set of ADC parameters, matches the latest configuration from
the upstream bl_mcu_sdk repository from commit hash:
9e189b69cbc0a75ffa170f600a28820848d56432
except for one difference.
(Note: bl_mcu_sdk has been heavily refactored since it has been imported into IronOS.)
You can make it match exactly by defining ENABLE_MIC2_DIFF, see the code
#ifdef ENABLE_MIC2_DIFF below.
I have decided to not apply this change because it appeared to make the
lower end of the input less precise.
Note that this configuration uses an ADC trimming value that is stored in the Efuse
of the bl70x chip. The actual reading is divided by this "coe" value.
We have found the following coe values on 3 different chips:
0.9629, 0.9438, 0.9876
Additional note for posterity:
PGA = programmable gain amplifier.
We would have expected to achieve the highest accuracy by disabling this amplifier,
however we found that not to be the case, and in almost all cases we have found
that there is a scaling error compared to the ideal Vref.
The only other configuration we have found to be accurate was if we had:
PGA disabled + Vref=2V + biasSel=AON + without trimming from the efuse.
But we can't use it because a Vref=2V limits the higher end of temperature and voltage readings.
Also we don't know if this other configuration is really accurate on all chips, or only
happened to be accurate on the one chip on which it has been found.
*/
adc_cfg.clkDiv = ADC_CLK_DIV_4;
adc_cfg.vref = ADC_VREF_3P2V;
adc_cfg.resWidth = ADC_DATA_WIDTH_14_WITH_16_AVERAGE;
adc_cfg.inputMode = ADC_INPUT_SINGLE_END;
adc_cfg.v18Sel = ADC_V18_SEL_1P82V;
adc_cfg.v11Sel = ADC_V11_SEL_1P1V;
adc_cfg.gain1 = ADC_PGA_GAIN_1;
adc_cfg.gain2 = ADC_PGA_GAIN_1;
adc_cfg.chopMode = ADC_CHOP_MOD_AZ_PGA_ON;
adc_cfg.biasSel = ADC_BIAS_SEL_MAIN_BANDGAP;
adc_cfg.vcm = ADC_PGA_VCM_1P2V;
adc_cfg.offsetCalibEn = DISABLE;
adc_cfg.offsetCalibVal = 0;
ADC_Disable();
ADC_Enable();
ADC_Reset();
ADC_Init(&adc_cfg);
#ifdef ENABLE_MIC2_DIFF
// This is the change that enables MIC2_DIFF, for now deciding not to enable it, since it seems to make results slightly worse
{
uint32_t tmpVal;
tmpVal = BL_RD_REG(AON_BASE, AON_GPADC_REG_CMD);
tmpVal = BL_SET_REG_BITS_VAL(tmpVal, AON_GPADC_MIC2_DIFF, 1);
BL_WR_REG(AON_BASE, AON_GPADC_REG_CMD, tmpVal);
}
#endif
#if 1
// this sets the CVSP field (ADC conversion speed)
{
uint32_t regCfg2;
regCfg2 = BL_RD_REG(AON_BASE, AON_GPADC_REG_CONFIG2);
regCfg2 = BL_SET_REG_BITS_VAL(regCfg2, AON_GPADC_DLY_SEL, 0x02);
BL_WR_REG(AON_BASE, AON_GPADC_REG_CONFIG2, regCfg2);
}
#endif
adc_fifo_cfg.dmaEn = DISABLE;
adc_fifo_cfg.fifoThreshold = ADC_FIFO_THRESHOLD_1;
ADC_FIFO_Cfg(&adc_fifo_cfg);
ADC_MIC_Bias_Disable();
ADC_Tsen_Disable();
ADC_Gain_Trim();
ADC_Stop();
ADC_FIFO_Clear();
ADC_Scan_Channel_Config(adc_tip_pos_chans, adc_tip_neg_chans, sizeof(adc_tip_pos_chans) / sizeof(ADC_Chan_Type), DISABLE);
}
void setup_timer_scheduler() {
TIMER_Disable(TIMER_CH0);
TIMER_CFG_Type cfg = {
TIMER_CH0, // Channel
TIMER_CLKSRC_32K, // Clock source
TIMER_PRELOAD_TRIG_COMP2, // Trigger; reset after trigger 0
TIMER_COUNT_PRELOAD, // Counter mode
22, // Clock div
(uint16_t)(powerPWM), // CH0 compare (pwm out)
(uint16_t)(powerPWM + holdoffTicks), // CH1 compare (adc)
(uint16_t)(powerPWM + holdoffTicks + tempMeasureTicks), // CH2 compare end of cycle
0, // Preload, copied to counter on trigger of comp2
};
TIMER_Init(&cfg);
Timer_Int_Callback_Install(TIMER_CH0, TIMER_INT_COMP_0, timer0_comp0_callback);
Timer_Int_Callback_Install(TIMER_CH0, TIMER_INT_COMP_1, timer0_comp1_callback);
Timer_Int_Callback_Install(TIMER_CH0, TIMER_INT_COMP_2, timer0_comp2_callback);
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_0);
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_1);
TIMER_ClearIntStatus(TIMER_CH0, TIMER_COMP_ID_2);
TIMER_IntMask(TIMER_CH0, TIMER_INT_COMP_0, UNMASK);
TIMER_IntMask(TIMER_CH0, TIMER_INT_COMP_1, UNMASK);
TIMER_IntMask(TIMER_CH0, TIMER_INT_COMP_2, UNMASK);
CPU_Interrupt_Enable(TIMER_CH0_IRQn);
TIMER_Enable(TIMER_CH0);
}
void setupFUSBIRQ() {
gpio_set_mode(FUSB302_IRQ_Pin, GPIO_SYNC_FALLING_TRIGER_INT_MODE);
CPU_Interrupt_Disable(GPIO_INT0_IRQn);
Interrupt_Handler_Register(GPIO_INT0_IRQn, GPIO_IRQHandler);
CPU_Interrupt_Enable(GPIO_INT0_IRQn);
gpio_irq_enable(FUSB302_IRQ_Pin, ENABLE);
}