I am testing ADC0 on ATtiny3226.
I am referencing the Microchip tech note TB3256:
How to Use the 12-Bit Differential ADC with PGA in Single Mode
I want to optimize the speed of reading a 12 bit single ended, single sample for some crude DSP I want to do on a low frequency audio signal.
I am using VDD as the reference voltage.
When I ground the ADC, if I use a sample duration of above about 6, I get a stable reading of 0. All good.
When I connect the ADC pin directly to VDD with a jumper patch cable, I never quite get 4095, the full 12 bit value.
My 5v supply comes from one of those phone charger blocks and is additionally decoupled with a 470uF Electrolytic and 100nF ceramic capacitor.
The ATtiny3226 has a 100nF decoupling capacitor mounted directly on its DIL carrier board.
I get close - in the range 3980 - 4080.
I believe that the voltage is measured by getting a capacitor to charge up so I would understand if I’m not giving this time to happen.
I’ve tried increasing the sample duration (value of ADC0_CTRLE) all the way to 255 but this seems to have no affect.
I’ve increased the prescaler divider from 2 to 4 but this made no difference.
I don’t see why collecting a set of samples and converting them into a result will make any difference - this will just slow down the final result.
I want the ADC to be able to read 4095 so I can use this to measure when an input is ‘too loud’ and hence, out of range.
Can you think of what I am doing wrong?
Do you have any ideas how to enable the device to measure up to VDD and yield 4095?
I used Arduino IDE to ‘burn the bootloader’ with settings as per comments - specifically F_CPU at 20MHz (and this was tuned with the megaTinyCore tuner).
OK, the code’s a bit clunky but it’s a stripped down version of a much bigger project. Here is the demo code:
// cSpell:includeRegExp CStyleComment
// ATtiny3226_Help
// Arduino Bootloader:
// megaTinyCore
// 20MHz tuned - with megaTinyTuner
// RESET on PB4
// Millis() on TCB1 as I want to use TCA0 for servo (ESC actually) motor control
// Set up of ADC using Microchip Tech Note TB3256 "How to Use the 12-Bit Differential ADC with PGA in Single Mode"
// include external code section
#include <Arduino.h>
#include <math.h> // from the Microchip Tech Note
// pre compile definitions
#define BAUD 115200 // selected Serial BAUD rate
// pin map
//
// ATtiny3226 / ARDUINO Pins
// _____
// VDD 1|* |20 GND
// PA4 0 2| |19 16 PA3
// PA5 1 3| |18 15 PA2
// PA6 2 4| |17 14 PA1
// PA7 3 5| |16 17 PA0 (~RESET/UPDI)
// PB5 4 6| |15 13 PC3
// PB4 5 7| |14 12 PC2
// PB3 6 8| |13 11 PC1
// PB2 7 9| |12 10 PC0
// PB1 8 10|_____|11 9 PB0
#define ADC_bp 1 // PA1 as AIN1 for ADC0
#define LED_bp 3 // PA3
#define TXD_bp 2 // PB2 as TXD for USART0
#define RXD_bp 3 // PB3 as RXD for USART0
#define TIMEBASE_VALUE ((uint8_t) ceil(F_CPU*0.000001)) // from the Microchip Tech Note
// put global variables here:
unsigned long last_print = millis(); // for non blocking delay timer
volatile uint16_t sample_variable; // data type from the Microchip Tech Note
// put drivers here:
// put function declarations here:
void system_init();
void setup() {
// put your setup code here, to run once:
system_init();
Serial.begin(BAUD);
Serial.print(F("\r\n\r\nStart\r\nF_CPU "));
Serial.println(F_CPU);
}
void loop() {
// put your main code here, to run repeatedly:
if ((millis() - last_print > 500)){ // only print a sample every 500ms
Serial.println(sample_variable);
last_print = millis();
}
}
// put function definitions here:
void mcu_init(void){
/* On AVR devices all peripherals are enable from power on reset, this
* disables all peripherals to save power. Driver shall enable
* peripheral if used */
/* Set all PORTA pins to low power mode and input disabled */
for (uint8_t i = 0; i < 8; i++){ // PA0 - PA7 on ATtiny3226
*((uint8_t *)&PORTA + 0x10 + i) |= (1 << PORT_PULLUPEN_bp);
}
/* Set all PORTB pins to low power mode and input disabled */
for (uint8_t i = 0; i < 6; i++){ // PB0 - PB5 on ATtiny3226
*((uint8_t *)&PORTB + 0x10 + i) |= (1 << PORT_PULLUPEN_bp);
}
/* Set all PORTC pins to low power mode and input disabled */
for (uint8_t i = 0; i < 4; i++){ // PC0 - PC3 on ATtiny2326
*((uint8_t *)&PORTC + 0x10 + i) |= (1 << PORT_PULLUPEN_bp);
}
}
void disablePORTA(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + pin); // for pin
*port_pin_ctrl = (*port_pin_ctrl & ~PORT_ISC_gm) | PORT_ISC_INPUT_DISABLE_gc; // disable input
}
void disablePullUpPORTA(uint8_t pin){
uint8_t *port_pin_ctrl = ((uint8_t *)&PORTA + 0x10 + pin); // for pin
*port_pin_ctrl &= ~(1 << PORT_PULLUPEN_bp); // remove pull up
}
void pin_init(void){ /* PIN initialization */
/* See <https://github.com/SpenceKonde/megaTinyCore/blob/master/megaavr/extras/Ref_DirectPortManipulation.md> */
PORTA_DIRSET = (1<<LED_bp); // Output pin for debug to see ADC running
disablePullUpPORTA(LED_bp); // disable pull up on output pin
disablePullUpPORTA(ADC_bp); // disable pull up on ADC input pin
disablePORTA(ADC_bp); // disable digital port on ADC input pin
}
void CPUINT_init(void){ // Interrupt initialisation
sei(); // Enable interrupts
}
void ADC0_init(void){ // ACD initialization
ADC0_PGACTRL = ADC_GAIN_1X_gc | ADC_PGABIASSEL_1X_gc | ADC_ADCPGASAMPDUR_6CLK_gc | ( 0 << ADC_PGAEN_bp); // no gain used
ADC0_INTCTRL = (1 << ADC_SAMPRDY_bp); // generate an interrupt on sample ready
ADC0_CTRLA = ( 1<< ADC_RUNSTDBY_bp) | (1 << ADC_LOWLAT_bp) | (1 << ADC_ENABLE_bp);
ADC0_CTRLB = ADC_PRESC_DIV2_gc; // select the fastest
ADC0_CTRLC = ADC_REFSEL_VDD_gc | (TIMEBASE_VALUE << ADC_TIMEBASE_gp); // set VDD as reference voltage adn set the time base value (from Microchip[ Tech note)
ADC0_CTRLE = 255; // Sample Duration: (10 + 0.5) / 20 MHz 10 is ~min for a stable reading of 0v
ADC0_CTRLF = (1 << ADC_FREERUN_bp); // set free running mode
ADC0_MUXPOS = ADC_MUXNEG_AIN1_gc; // use AIN1 - PA1 as input
ADC0_COMMAND = ADC_MODE_SINGLE_12BIT_gc | ADC_START_IMMEDIATE_gc; // 12 bit samples and start the first one now
}
void system_init(){ // from Microchip Studio 7 code generator
mcu_init();
pin_init();
ADC0_init();
CPUINT_init();
}
// put ISR here:
ISR(ADC0_SAMPRDY_vect){
PORTA_OUTTGL = (1 << LED_bp); // some debug flashing on PA1
sample_variable = ADC0_SAMPLE; // read the sample into a variable
// ADC0_INTFLAGS = ADC_SAMPRDY_bm; // can clear interrupt by reading the sample so this not needed
}
And the platformio.ini
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:ATtiny3226]
platform = atmelmegaavr
board = ATtiny3226
framework = arduino
build_unflags = -DMILLIS_USE_TIMERA0 ; stop megaTinyCore using TCA0
build_flags = -DMILLIS_USE_TIMERB0 ; have it use TIMERB0
upload_speed = 115200
upload_port = /dev/ttyUSB0
monitor_port = /dev/ttyUSB1
monitor_speed = 115200
board_build.f_cpu = 20000000L
upload_flags =
--tool
uart
--device
ATtiny3226
--uart
$UPLOAD_PORT
--clk
$UPLOAD_SPEED
upload_command = pymcuprog write --erase $UPLOAD_FLAGS --filename $SOURCE
I changed my ADC0_init() definition to use 8 bit and left adjusted sample and I measured 0v as 0 and VDD as 255. I was also able to reduce the sample duration to 3. Maybe I’ll stick with 8 bit but I’d still be keen to know what may be wrong with my 12 bit sampling regime.
Here’s my modified 8 bit sampling code snippet…
void ADC0_init(void){ // ACD initialization
ADC0_PGACTRL = ADC_GAIN_1X_gc | ADC_PGABIASSEL_1X_gc | ADC_ADCPGASAMPDUR_6CLK_gc | ( 0 << ADC_PGAEN_bp); // no gain used
ADC0_INTCTRL = (1 << ADC_SAMPRDY_bp); // generate an interrupt on sample ready
ADC0_CTRLA = ( 1<< ADC_RUNSTDBY_bp) | (1 << ADC_LOWLAT_bp) | (1 << ADC_ENABLE_bp);
ADC0_CTRLB = ADC_PRESC_DIV2_gc; // select the fastest
ADC0_CTRLC = ADC_REFSEL_VDD_gc | (TIMEBASE_VALUE << ADC_TIMEBASE_gp); // set VDD as reference voltage adn set the time base value (from Microchip[ Tech note)
ADC0_CTRLE = 3; // Sample Duration: (10 + 0.5) / 20 MHz 10 is ~min for a stable reading of 0v
ADC0_CTRLF = (1 << ADC_FREERUN_bp) | (1 << ADC_LEFTADJ_bp); // set free running mode and left shift 8 bit result
ADC0_MUXPOS = ADC_MUXNEG_AIN1_gc; // use AIN1 - PA1 as input
ADC0_COMMAND = ADC_MODE_SINGLE_8BIT_gc | ADC_START_IMMEDIATE_gc; // 8 bit samples and start the first one now
}