I am new to microcontroller. The following code measures the period of a square wave. I have marked some lines which I haven't understood. The code is as follows:
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER1_CAPT_vect)
{
int counter_value = ICR1; //16 bit value
PORTB = (counter_value >> 7); // What has been done here?
TCNT1 = 0; // why this line?
}
int main(void)
{
DDRB = 0xFF;
TCCR1A = 0x00;
TCCR1B = 0b11000010;
TIMSK = 0b00100000;
sei();
while(1);
cli();
}
What has actually been done in those lines?
ISR(TIMER1_CAPT_vect)
{
int counter_value = ICR1; //16 bit value
PORTB = (counter_value >> 7); // What has been done here?
PORTB is a set of 8 output lines. Presumably, they are connected by a bus to some device you haven't mentioned. Maybe even a set of LEDS to display a binary number.
The result from the counter is 16 bits. To get the most significant bits, shift the result to the right to discard the less significant bits. (This operation loses precision, but you only have 8 bits of output, not 16.) As to why the shift is only 7 instead of 8, or why the unsigned value of the counter is saved as a signed int first, I don't know. I suspect it is a mistake. I would have done PORTB = (ICR1 >> 8); instead.
TCNT1 = 0; // why this line?
Since we have recorded the time of the capture and sent it out PORTB, we now want to reset the timer for the next capture.
}
Related
I am trying to follow, adapt, understand (and clean up a bit) a variation around the code available there, for the Arduino Due: https://forum.arduino.cc/index.php?topic=589213.0 . I do not like the forum format, as things end up buried deep, so asking here instead. Unfortunately this means that there is quite a lot of explanations before the question. If you think this is wrong to post it here, let me know, and I can move.
Basically, the idea is to log several ADC channels in a buffer, using timer-based triggering. There is a bit of setup:
// sample rate in Hz
constexpr int sample_rate = 1000;
constexpr uint8_t channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_channels = sizeof(channels);
Then time counter 0 channel 2 is set at the right frequency for triggering the ADC conversion:
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
Finally this can be used to trigger the ADC:
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several channels in a row one after the other
// report finished conversion using ADC interrupt
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(1) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << channels[nbr_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
and the ADC output can be captured in the corresponding ISR:
void ADC_Handler() {
for (size_t i = 0; i < nbr_channels; i++)
{
SOME_BUFFER[i] = static_cast<volatile uint16_t>( * (ADC->ADC_CDR + channels[i]) & 0x0FFFF ); // get the output
}
}
I think this is quite understandable, but I have one question: the setting of the pre-scaler.
if I understand well discussions online, the pre-scaler should be set so that frq_ADC >= sample_rate * nbr_channels, basically because the chip is just multiplexing the ADC through several channels
if I understand well, we want to set such pre-scaler value as high as possible given the previous constraint, so that the ADC frequency is as low as possible, because this improves ADC conversion quality
Is that right?
The problem is that I am confused about how to set the pre-scaler, and what value corresponds to what, because what I find in the datasheet disagree with some other online responses I read.
From the datasheet https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf : "The ADC clock range is between MCK/2, if PRESCAL is 0, and MCK/512, if PRESCAL is set to 255 (0xFF).". This is consistent with what I find on page 1334: "ADCClock = MCK / ( (PRESCAL+1) * 2 )". But page 1318, it is written that the conversion rate is 1MHz. Then how is that compatible with having a MCK frequency of 84MHz on the Due? 84/2 = 48MHz, 84/512 = 0.164MHz, the high freq value is too high.
Then to add to the confusion I have found this issue: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due/21054#21054 that also seem to conflict with the 1MHz upper bound.
Any idea where I misunderstand something? (and any more comments around the general working of the program?).
Ok, so I did some tests with the code, checking when I was missing some conversions depending on the timer frequency and the prescaler value. The code is a bit long, so I post it at the end of the answer. Basically:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
I think this indicates that:
the pre-scaler value is well an 8 bits int, between 0 and 255, as it wraps up at 256
I have difficultie matching the results to the formula in the datasheet. I guess this is because there is some overhead switching channels etc (?). For example:
the results are consistent with ```ADC_freq = 1MHz / ( ps ) at the highest frequencies, but I suppose this is because there is a bit of overhead switching channels
the results are consistent with ```ADC_freq = 2MHz / ( ps ) at 10 kHz, and at 1kHz, even using the highest prescaler is fine.
The code I was using is the following, and the criterion for deciding that things fail is that the code reports a drop in the effective sample frequency over the 5 channels:
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// timer driven ADC convertion captured by interrupt on n adc_channels for Arduino Due
//
// this is for Arduino Due only!
//
// the interrupt based ADC measurement is adapted from:
// https://forum.arduino.cc/index.php?topic=589213.0
// i.e. adc_setup(), tc_setup(), ADC_handler() are inspired from the discussion there.
//
// written with VSCode + Platformio and Due board setup
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// make my linter happy
#include "Arduino.h"
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some vital ADC grabbing setup
// sample rate in Hz, should be able to go up to several 10s ok kHz at least
constexpr int adc_sample_rate = 1000;
// size of the data buffers "in time"
// i.e. how many consecutive measurements we buffer for each channel
constexpr size_t adc_buffer_nbr_consec_meas = 5;
// the adc_channels to read, in uC reference, NOT in Arduino Due pinout reference
// for a mapping, see: https://components101.com/microcontrollers/arduino-due
// i.e. A0 is AD7
// A1 AD6
// A2 AD5
// A3 AD4
// A4 AD3
// A5 AD2
// A6 AD1
// A7 AD0
constexpr uint8_t adc_channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_adc_channels = sizeof(adc_channels);
// the buffer containing the measurements for all adc_channels over several measurements in time
volatile uint16_t adc_meas_buffer[adc_buffer_nbr_consec_meas][nbr_adc_channels];
// flag when a full vector of conversions is available
volatile bool adc_flag_conversion = false;
// time index of the current measurement in the adc reads buffer
volatile size_t crrt_adc_meas_buffer_idx = 0;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some non-vital printing config
// a bit of time tracking, just to analyze how good performance
unsigned long current_us = 0;
unsigned long previous_us = 0;
unsigned long delta_us = 0;
float delta_us_as_s = 0;
float delta_us_as_ms = 0;
int nbr_readings_since_reduced_time_stats = 0;
unsigned long current_reduced_time_stats_us = 0;
unsigned long previous_reduced_time_stats_us = 0;
float delta_reduced_time_stats_us_as_s = 0;
float effective_logging_frequency = 0;
// decide what to print on serial
constexpr bool print_reduced_time_stats = true;
constexpr bool print_time_stats = false;
constexpr bool print_full_buffer = false;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// low level functions for setting clock and ADC
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several adc_channels in a row one after the other
// report finished conversion using ADC interrupt
// tests about pre-scaler: formula should be:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
// CCL: use ps 2 at 100kHz with 5 channels, 20 at 10kHz, 200 at 1kHz
void adc_setup()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(200) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
// unclear, asked: https://stackoverflow.com/questions/64243073/setting-right-adc-prescaler-on-the-arduino-due-in-timer-and-interrupt-driven-mul
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_adc_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << adc_channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << adc_channels[nbr_adc_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to adc_sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup()
{
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / adc_sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
// ISR for the ADC ready readout interrupt
// push the current ADC data on all adc_channels to the buffer
// update the time index
// set flag conversion ready
void ADC_Handler()
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
adc_meas_buffer[crrt_adc_meas_buffer_idx][i] = static_cast<volatile uint16_t>(*(ADC->ADC_CDR + adc_channels[i]) & 0x0FFFF);
}
crrt_adc_meas_buffer_idx = (crrt_adc_meas_buffer_idx + 1) % adc_buffer_nbr_consec_meas;
adc_flag_conversion = true;
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// a simple script: setup and print information
void setup()
{
Serial.begin(115200);
delay(100);
adc_setup();
tc_setup();
}
void loop()
{
if (adc_flag_conversion == true)
{
adc_flag_conversion = false;
if (print_reduced_time_stats)
{
nbr_readings_since_reduced_time_stats += 1;
if (nbr_readings_since_reduced_time_stats == adc_sample_rate)
{
current_reduced_time_stats_us = micros();
delta_reduced_time_stats_us_as_s = static_cast<float>(current_reduced_time_stats_us - previous_reduced_time_stats_us) / 1000000.0;
effective_logging_frequency = static_cast<float>(adc_sample_rate) / delta_reduced_time_stats_us_as_s;
previous_reduced_time_stats_us = current_reduced_time_stats_us;
Serial.print(F("Effective logging freq over nbr spls that should correspond to 1 second: "));
Serial.println(effective_logging_frequency);
nbr_readings_since_reduced_time_stats = 0;
}
}
if (print_time_stats)
{
current_us = micros();
delta_us = current_us - previous_us;
delta_us_as_s = static_cast<float>(delta_us) / 1000000.0;
delta_us_as_ms = static_cast<float>(delta_us) / 1000.0;
Serial.println(F("ADC avail at uS"));
Serial.println(micros());
Serial.println(F("elapsed us"));
Serial.println(delta_us);
Serial.println(F("elapsed ms"));
Serial.println(delta_us_as_ms);
Serial.println(F("elapsed s"));
Serial.println(delta_us_as_s);
Serial.println(F("updated idx:"));
size_t last_modified_buffer_idx;
if (crrt_adc_meas_buffer_idx > 0){
last_modified_buffer_idx = crrt_adc_meas_buffer_idx - 1;
}
else{
last_modified_buffer_idx = nbr_adc_channels - 1;
}
Serial.println(last_modified_buffer_idx);
previous_us = current_us;
}
if (print_full_buffer)
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
Serial.print(F(" ADC "));
Serial.print(adc_channels[i]);
Serial.println(F(" meas in time:"));
for (size_t j = 0; j < adc_buffer_nbr_consec_meas; j++)
{
Serial.print(adc_meas_buffer[j][i]);
Serial.print(F(" "));
}
Serial.println();
}
}
}
}
Trying to understand timing / dimming and interrupts using an Arduino Uno (or any other AVR) is being made very difficult by a serious lack of example code. Having found a sketch that starts from zero and ramps up the brightness, I have tried to adapt the code to prevent the continuous loop which occurs when the 16-bit register overflows.
The attached sketch starts up from zero light output and increases over a period of time - currently using the delay() function.
Attempting to adapt the code to prevent the loop from starting the entire process again and to allow the led to remain at the "top" brightness output for x (variable) number of hours has proved to be most elusive. As one of the contributors have noted this area of coding is one of the most difficult to master.
Any advice or guidance which will put me in the right direction will be most appreciated.
...
//fade over 65535 steps
// 16 bit PWM on any pin
// Example uses built in LED on pin 13 (PORTB bit 5)
// https://forum.arduino.cc/index.php?topic=348170.0
void setup() {
pinMode(13, OUTPUT);
cli(); // Disable all interrupts
TCCR1A = 0; // Clear all flags in control register A
TCCR1B = 0; // Clear all flags in control register B
TCNT1 = 0; // Zero timer 1 count
OCR1A = 32768; // Preload compare match register (50% duty cycle)
// No prescaler
//TCCR1B |= _BV(CS12);
//TCCR1B |= _BV(CS11);
TCCR1B |= _BV(CS10);
TIMSK1 |= _BV(OCIE1A); // Enable timer compare interrupt
TIMSK1 |= _BV(TOIE1); // Enable timer overflow interrupt
sei(); // enable all interrupts
}
void loop() {
for (unsigned int x = 1; x < 65535; x++) {
//cli();
OCR1A = x;
//sei();
delay(20);
}
}
ISR(TIMER1_OVF_vect) { // Timer1 overflow interrupt service routine
PORTB |= _BV(PORTB5); // Turn LED (pin 13) on
}
ISR(TIMER1_COMPA_vect) { // Timer1 compare interrupt service routine
PORTB &= ~_BV(PORTB5); // Turn LED off
}
...
I am well familiar with PWM generation in Atmega128 and its family microcontrollers. I have been using prescalar and other registers for generating frequency. But I have to generate 20KHz pwm signal. I tried but I could not get the desired output. Can anyone suggest me or help me how to do it ?
As far as I know, in atmega128, 1 instruction takes 1 cycle. Using 16MHz crystal, 1 instruction completes in 1/16M sec.
I tried to generate 20Khz signal (50 us)with 25us duty cycle. But I get different frequency (277.78 Hz) in oscilloscope which is far less than 20KHz
My calculation was
16MH = 20000Hz * 800.
for 0-399 count, I made port high and
399-799 count, I made port low.
void frequency(void){ // 20kHz Frequency
if (cnt1 <= 399){
PORTB |= (1<<7);
} else {
PORTB &= ~(1<<7);
}
cnt1++;
if (cnt1 >= 800) cnt1 = 0;
}
I don't have access to the 128 but verified its 16-bit Timer 1 is similar to that in the 328 and 32U4 so the following should work with minor modification (the main sticking point is probably looking up what pin the overflow register is bound to):
#include <avr/io.h>
#include <util/delay.h>
struct CTC1
{
static void setup()
{
// CTC mode with TOP-OCR1A
TCCR1A = 0;
TCCR1B = _BV(WGM12);
// toggle channel A on compare match
TCCR1A = (TCCR1A & ~(_BV(COM1A1) | _BV(COM1A0))) | _BV(COM1A0);
// set channel A bound pin PB1 to output mode
#if defined(__AVR_ATmega32U4__)
DDRB |= _BV(5);
#else
DDRB |= _BV(1);
#endif
}
static void set_freq(float f)
{
static const float f1 = min_freq(1), f8 = min_freq(8), f64 = min_freq(64), f256 = min_freq(256);
uint16_t n;
if (f >= f1) n = 1;
else if (f >= f8) n = 8;
else if (f >= f64) n = 64;
else if (f >= f256) n = 256;
else n = 1024;
prescale(n);
OCR1A = static_cast<uint16_t>(round(F_CPU / (2 * n * f) - 1));
}
static void prescale(uint16_t n)
{
uint8_t bits = 0;
switch (n)
{
case 1: bits = _BV(CS10); break;
case 8: bits = _BV(CS11); break;
case 64: bits = _BV(CS11) | _BV(CS10); break;
case 256: bits = _BV(CS12); break;
case 1024: bits = _BV(CS12) | _BV(CS10); break;
default: bits = 0;
}
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10))) | bits;
}
static inline float min_freq(uint16_t n)
{
return ceil(F_CPU / (2 * n * 65536));
}
};
void setup()
{
CTC1::setup();
CTC1::set_freq(20e3);
}
void loop()
{
// do whatever
_delay_ms(1);
}
int main()
{
setup();
for (;;)
loop();
}
I tested on my scope and measure exactly 20kHz off a 328p running at 16MHz. If 20kHz is the only frequency you need then you can simplify this substantially. Translating the code to use one of the 8-bit timers is also straightforward though I haven't verified that it's possible to hit exactly 20kHz with those.
It's not a good idea to use counter in C to implement the PWM or anything time critical really. Although C converts your code to specific machine code, you don't really know how much time it will take.
Your code does not translate to:
make port B high 400 times (PORTB |= (1<<7);)
make port B low 400 times (PORTB &= ~(1<<7);)
, but rather something like this (simplification, human-readable):
load variable cnt1 to memA;
load 399 to memB
compare mem A to memB
put result to memC
if memC eq "somthing indicating <=" do PORTB |= (1<<7);
if memC something else do PORTB &= ~(1<<7);
load cnt1 to memD and increment;
write memD to cnt1;
load 800 to memE
load cnt1 to memF
compare memF to memE
put result to memG
if memG eq "somthing indicating <=" do memF = 0, write memF to cnt1;
if memG something else go to start;
If you look at this from "C" point of view you need to do at least:
1. comare cnt1-399
2. if ok - do / else
3. port high / port low
4. add one to cnt1
5. compare cnt1 and 800
It then depends on you compiler how good it is at optimizing all the loads and writes (usually quite good).
You can have control on what the delays will be if you really know your compiler and don't use to much optimization (it is usually to complex to follow) or by writing the code in assembler. But then you will have to use logic similar to my explanation of the machine code (assembler is close to human-readable machine code).
I think the solution for you are timer interrupts. There's a good tutorial for atmega128 this here.
Also what do you mean with:
I tried to generate 20Khz signal (50 us)with 25us duty cycle.
Do you mean 20kHz signal with 50% duty cycle? so 25us low, 25 us high?
If this is the case you can do this with one timer interrupt and one (binary) counter.
Exactly the "8 bit timer example" you can read about in the provided link.
I'm trying to use an arduino uno to show some students how to make their own 'auto tune' however the code that I wrote is not outputting any signal. The goal is to sample values into an array at one rate and output the data from the array(FIFO)at a slower rate. My understanding is that TCNT1 increments each clock tick, I'm using 16 MHz in my case, and that I can base if logic on the value of TCNT1, I use a mod function here to take and store a single adc value and then play that value to the dac at a later time. acdT dacT represent my timing logic. I've built an external DAC to read only 8 (of 10) bit values from d0-d7 (PORTD). Why am I not seeing a signal?
int i = 0;
int j = 0;
int adcT = 328; // 329 clock tics
int dacT = 349; // 350 clock tics
int buff[15]; // 16 length buffer to store adc values
void setup ()
{
PRR &= ~(1<<PRADC); //ADC turned on
ADMUX = 0x60; //AVcc, left adjusted, ADC0 pin
ADCSRA = 0xC0;//ADC Enabled, no auto trigger
DDRD=0xFF; // set portd to d0 thru d7 digital pins
DDRC=0x00; // accept input from any analog input
TCCR1B |= 1<<CS10; // sets the clock to the system clock ie no pre scaler
}
void loop ()
{
if((TCNT1%acdT == 0) || TCNT1 == 0) // execute at 0 and mod329 clock tics
{
ADCSRA|=(1<<ADSC); // take one adc reading
while(!(ADCSRA & (1<<ADIF))); // wait until the reading is complete
ADCSRA|=(1<<ADIF); //reset adc for next command
buff[i] = ADCH; // take the adc value into the array
i++ // increment
}
if((TCNT1%dacT == 0)) %% TCNT1 ~= 0// execute at mod350 clock tics
{
PORTD = buff[j]; // send the adc reading to digital output
j++;
}
if(TCNT1 == 5262 ) // LCM/3 of 329(16samples) and 350(15samples)
{
TCNT1 = 0;// reset ticker
i = 0;
j = 0;
}
if(TCNT1 == 336)
{
PORTD = buff[15]; // play 16th adc sample to clear array
}
}
TCCR1B |= 1<<CS10; // sets the clock to the system clock ie no pre scaler
And there's your problem. You're attempting to find the modulus of a counter that runs faster than your code. Use the output capture and other features of the timer to trigger interrupts and reset the timer at the appropriate times instead of trying to catch a passing bullet with your bare hands.
Hi i'm new to Arduino and i'm trying to create a 1 second delay but i don't know how i can generate a code that completes the delay_ms function
I've tried to work it out but doesn't seem to get any better
Thank you
void setup()
{
word millisecs;
Serial.begin(9600);
}
void setup() asm volatile(
" ldi r16,0x3F ; r16 = 00111111\n"
" out 4,r16 ; set pins 8-13 as outputs in DDRB\n"
::: "r16");
millisecs = 1000; // 1s blink delay
Serial.begin(9600);
}
void loop()
{
long starttime = millis(); // make a note of the start time
asm volatile(
// jump to "blink" - ie jump around the delay_ms subroutine
" rjmp blink%= ; relative jump to 'blink' \n"
" ldi r16,0x3F ; r16 = 00111111\n"
" out 4,r16 ; set pins 8-13 as outputs in DDRB\n"
::: "r16");
registers used:
r31 - millisecond count (lo byte)
r30 - millisecond count (hi byte)
r17 - 100 microsecond count
r16 - 1 microsecond count
Overall delay (ms) = r30:r31 * r17 * r16
---------------------------------------------------------------------*/
"delay_ms%=: nop ; code to replace nop \n"
"delay_100us%=: nop ; code to replace nop \n"
"delay_1us%=: nop ; code to replace nop \n"
" sbiw r30,1 ; decrement ms count (r31:r30)\n"
" brne delay_ms%= ; loop to delay_ms while > 0 \n"
" ret ; return from subroutine \n"
There is a built in to do this
http://arduino.cc/en/Reference/delay
you just give it the number of ms to delay. So for one second
delay(1000)
You are working way too hard for something so simple. See BlinkWithoutDelay (code below). That code is also part of the Arduino examples which get installed when you install the Arduino IDE. Lots of good learning examples in there. (Oh - the code uses a long for the var previousMillis. That is an error (imo). It would be better to use an unsigned long.)
You also have the option of using Timer interrupts but for a simple second delay (esp. if you don't have much experience), I would suggest the method above and do it all in code that is simple to understand.
/* Blink without Delay
Turns on and off a light emitting diode(LED) connected to a digital
pin, without using the delay() function. This means that other code
can run at the same time without being interrupted by the LED code.
The circuit:
* LED attached from pin 13 to ground.
* Note: on most Arduinos, there is already an LED on the board
that's attached to pin 13, so no hardware is needed for this example.
created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen
This example code is in the public domain.
http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
*/
// constants won't change. Used here to
// set pin numbers:
const int ledPin = 13; // the number of the LED pin
// Variables will change:
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0; // will store last time LED was updated
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}
void loop()
{
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}