I am learning embedded C with Atmega128 and Atmel Studio.
I want to write code that shows the temperature and humidity from a sensor.
I am learning Input Capture and my first question is:
I get IC with value 5000(period) but I think 2500 is correct. Am I calculating it right? If so, do you have any idea why IC has the wrong value?
16000.000/64 = 250000
1 second 250000 puls
100 ms x = 2500
Board info:
avr128
16.000.000 external clock.
philips puls generator info:
Repitation 100 mili s
Duration 10 ms
5 volt
My code
volatile uint32_t iccnt=0,ovfcnt=0;
volatile uint32_t myCapt [2],lastCapt;
//__________________________
void initTimer()
{
TCNT1 =0;//Max=65535
TCCR1B |= 1<<CS10 | 1<<CS11 ;
// 1<<CS10 | 1<<CS11 ;//16000000/16=250000
//250000/65535=3.81
//TCCR1B |= 1<<WGM13 |1<<WGM12;
TIMSK |= 1<<TOIE1;
TIFR = 1<<TOV1;
sei();
}
void initInputCapture()
{
// First Capture on rising edge
TCCR1B |=1<<ICES1;
//ic intrrupt flag enable
TIMSK |= 1<< TICIE1;
// set input pin to get signal
}
void initlcd()
{
lcd_init(LCD_DISP_ON_CURSOR_BLINK);
lcd_clrscr();
}
void ShowA(unsigned int a)
{
char aa[11];
if (a >32000) {
itoa(a - 32000,aa,10);
lcd_puts("32000+");
lcd_puts(aa);
} else {
itoa(a,aa,10);
lcd_puts(aa);
}
}
ISR(TIMER1_OVF_vect)
{
ovfcnt++;
}
ISR(TIMER1_CAPT_vect)
{
iccnt++;
myCapt[iccnt % 2] = ICR1 - lastCapt;
lastCapt = ICR1;
}
void ShowCaptureTime2()
{
lcd_clrscr();
ShowA (iccnt);
lcd_puts(": \n");
ShowA(myCapt[(iccnt - 1) % 2]);
for (int j=0;j<(1000) ;j++)
{
for (int i=0;i<4000;i++)
{
}
}
}
//*******************************
//*******************************
int main(void)
{
DDRG = 0b11111111;
int cnt = 0;
initlcd();
initTimer();
initInputCapture();
while(1)
{
ShowCaptureTime2();
}
}
I could find the answer yesterday. code has no problem. Pulse generator work not correktly! incorrect input!!!! – samsoft
Related
I am new to programming in IDE. I am using built-in timer of arduino.I am using TDR method to capture reflection from fault location in cable and for that I am using timer.Timer should start as soon as output is send and stop at reflection. I have a reference code for it but I dont able to understand it, so if anyone know about it , it would be great.
void setup()
{
pinMode(stepPin, OUTPUT);
pinMode(refPin, OUTPUT);
pinMode(shutdownPin, OUTPUT);
TCCR1A = 0;
TCCR1B = (1 << ICNC1); // input capture noise canceller enabled, capture on falling edge
TIMSK1 = 0; // timer 1 interrupts disabled
ACSR = 0; // input capture from ICP1 pin
TCCR2B = (1 << CS20); // change timer 2 PWM frequency to 31.25kHz because we're using pin 11 as a DAC
Serial.begin(19200);
}
struct Step
{
unsigned int time;
unsigned int amplitude;
};
// Take a single measurement, using either a positive or negative edge from the comparator.
// The comparator reference voltage must have been set up and allowed to stablise before calling this.
unsigned int takeMeasurement(bool posEdge)
{
byte reg1b = (posEdge) ? 0 : (1 << ICES1); // input capture noise canceller csdisabled, set up input capture polarity, stop timer
reg1b |= (1 << CS10);
TCCR1B = reg1b;
TCNT1H = 0;
TCNT1L = 0; // clear timer
unsigned int capture = 0;
unsigned long start = micros(); // get the time
cli();
TCNT1H = 0;
TCNT1L = 0; // clear timer
TIFR1 = (1 << ICF1); // clear timer 1 input capture bit
PORTD |= (1 << 4); // set output high
sei();
do
{
if ((TIFR1 & (1 << ICF1)) && capture == 0)
{
byte temp = ICR1L;
capture = (ICR1H << 8) | temp;
}
} while (micros() - start < 100);
PORTD &= ~(1 << 4); // set output low
return capture;
}
size_t findSteps(bool positive, struct Step *results, size_t maxResults)
{
byte amplitude = (positive) ? 5 : 250;
analogWrite(refPin, amplitude);
delay(100); // wait 100ms for the output to stabilise
unsigned int lastReading = 0;
size_t numResults = 0;
unsigned int stepSize = 0; // 0 means not in a step
#ifdef DEBUG
Serial.print((positive) ? "pos " : "neg ");
#endif
for (int i = 0; i < 50; ++i)
{
analogWrite(refPin, amplitude);
delay(10);
unsigned int currentReading = takeMeasurement(positive);
unsigned int currentDiff = currentReading - lastReading; // diff since start of possible step
if (stepSize == 0)
{
// Not currently in a step
if (i != 0 && currentReading != 0 && currentDiff == 0)
{
// Found the start of a possible step
++stepSize;
}
lastReading = currentReading;
}
else
{
if (currentDiff > 2 || i + 1 == 50)
{
// Step has endeed, so record it if it is big enough
if (stepSize >= 2)
{
results->time = lastReading;
results->amplitude = amplitude - 5;
++results;
++numResults;
if (numResults == maxResults) break;
}
stepSize = 0;
lastReading = currentReading;
}
else if (currentDiff == 0)
{
++stepSize;
}
}
#ifdef DEBUG
if (i != 0) Serial.write(',');
Serial.print(currentReading);
#endif
if (positive)
{
amplitude += 5;
}
else
{
amplitude -= 5;
}
}
#ifdef DEBUG
Serial.println();
#endif
return numResults;
}
I am trying to program an MPPT solar charge controller for a constant output to charge the connected tubular battery. I have used an ADC pin to get the output voltage through feedback and generate the PWM signal accordingly.
From another GPIO pin(PINC6), it drives the transistor that it turn would turn MOSFET to charge the battery. When OutputVoltage > 14.0v; pin is high else it is low.
I have also put the deadtime for the MOSFETs.
The problem is that it just doesn't give me 14v at the output instead fluctuates from 6.19v to 7.39v.
controller used- Atmega 32A
#define F_CPU 8000000
const float T_DeadTime = 1.00f;
float voltageFeedback=0.0f;
uint8_t duty=0;
MAIN.c
int main(void)
{
sei();
_delay_ms(500);
DDRD |= (1 << DDD4)|(1 << DDD5);
_delay_ms(100);
PWM_Init();
_delay_ms(500);
duty=20;
while (1)
{
pwm();
_delay_ms(500);
voltageFeedback=readVoltagefeedback();
if(voltageFeedback<14.0f)
{
PORTC&=~(1<<PINC6);
}
else if(voltageFeedback>=14.0f)
{
PORTC|=1<<PINC6;
}
}
}
void initiallizePort()
{
DDRC |= (1<<PINC6);
PORTC &= ~(1<<PINC6);
}
pwm functions
void pwm()
{
if(voltageFeedback >= 14.6)
{
duty=duty-1;
if(duty<11)
{
duty=11;
}
else{
//do nothing
}
}
else if(voltageFeedback <= 14.0)
{
duty=duty+1;
if(duty>=190)
{
duty=190;
}
else{
//do nothing
}
}
PWM_mu(duty);
_delay_ms(1000);
}
ADC
#define ADC_VOLT_FBK ADC0
#define ADC0 0x40
#define OPVOLTAGE_FACTOR 0.0537f
float rawData = 0.0f;
int adcResult = 0;
int readADCValue(uint8_t adcNumber)
{
ADMUX=adcNumber;
ADCSRA = (1<<ADEN) | (1<<ADIE) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);//
ADCSRA |= (1<<ADSC);//Start Conversion
sei();
_delay_ms(5);
return adcResult;
}
float readVoltagefeedback()
{
rawData = OPVOLTAGE_FACTOR*readADCValue(ADC_VOLT_FBK);
return rawData;
}
ISR(ADC_vect)
{
adcResult = ADC;
ADCSRA |= (1<<ADSC);//Start Conversion
}
even if the condition is given as 14v yet the pinc6 turns high and low after 10v. The Adc is working fine as I checked the feedback voltage in a serial terminal through UART.
I have tried to implement TX only uart for ATTiny85 and receive the bits using arduino micro. (similiar question did not help since it is quite different to my situation)
My intend is to be able to print via attiny85 -> arduino -> console so I can debug the attiny85, since I don't have oscilloscope available.
attiny85 fuses are: "efuse:w:0xff:m -U hfuse:w:0xdf:m -U lfuse:w:0xf1:m" aka. 16MHz F_CPU
arduino also seems to have 16MHz F_CPU
Similiar to the mentioned question attiny85 sends bits via timer0 ISR one bit at time:
#ifndef F_CPU
#define F_CPU 16000000UL // 16 MHz
#endif
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define TX_PIN PB0
volatile uint16_t tx_shift_reg = 0;
ISR(TIMER0_COMPA_vect) {
uint16_t local_tx_shift_reg = tx_shift_reg;
if( local_tx_shift_reg & 0x01 ) {
PORTB |= _BV(TX_PIN);
} else {
PORTB &= ~_BV(TX_PIN);
}
local_tx_shift_reg >>= 1;
if(!local_tx_shift_reg) {
// Stop timer0.
GTCCR |= (1<<TSM) | (1<<PSR0);
}
tx_shift_reg = local_tx_shift_reg;
}
void UART_tx(char byte) {
uint16_t local_tx_shift_reg = tx_shift_reg;
local_tx_shift_reg = (0b1<<9) | ((uint16_t)byte<<1);
tx_shift_reg = local_tx_shift_reg;
TCNT0 = 0;
TCCR0B |= (1<<CS02)|(1<<CS00); // 1024 prescaler
GTCCR &= ~(1<<TSM);
}
void UART_tx_char(char c) {
UART_tx( c );
// wait until transmission is finished
while(tx_shift_reg);
}
void UART_init() {
cli()
// set TX pins as output
DDRB |= (1<<TX_PIN);
PORTB |= (1<<TX_PIN);
// set timer0 to CTC mode, keep it halted.
TCCR0A |= (1<<WGM01);
TCCR0B = 0;
// enable interrupt
TIMSK |= (1<<OCIE0A);
OCR0A = 128;
OCR0B = 128;
TCNT0 = 0;
GTCCR |= (1<<TSM);
sei();
}
void main(void)
{
UART_init();
while(1) {
for(char c = 1; c < 128; ++c) {
UART_tx_char(c);
_delay_ms(100);
}
}
}
Then arduino receives the bits:
/*
* ATtiny85 bit-bang uart monitor for ATmega32u4
*/
#define LED_PIN 13
#define RX_PIN 7
// rx_state == 0 // timer1 not running
// rx_state == 1 // receive in progress
// rx_state == 2 // data ready in rx_data_reg
volatile int rx_state = 0;
volatile int rx_bit_nro = 0;
volatile uint16_t rx_shift_reg = 0;
volatile uint16_t rx_data_reg = 0;
void rx_start_interupt() {
if(rx_state == 0) {
digitalWrite(LED_PIN, HIGH);
while(digitalRead(RX_PIN));
// Start timer1
rx_state++;
TCNT1 = 0;
TCCR1B = (1 << WGM12) | (1 <<CS12) | (1 << CS10);
}
}
ISR(TIMER1_COMPA_vect) {
uint16_t bit = digitalRead(RX_PIN) > 0;
rx_shift_reg >>= 1;
rx_shift_reg |= (bit << 7);
++rx_bit_nro;
if(rx_bit_nro == 9) {
// Stop timer.
TCCR1B = 0;
TCNT1 = 0;
rx_data_reg = rx_shift_reg;
rx_shift_reg = 0;
rx_bit_nro = 0;
rx_state++;
digitalWrite(LED_PIN, LOW);
}
}
void setup() {
noInterrupts();
// Program timer1 for UART bit bang receive.
TCCR1A = 0; // set entire TCCR1A register to 0 (stops it)
TCCR1B = 0; // same for TCCR1B
TCNT1 = 0; // initialize counter value to 0
OCR1A = 128;
TIMSK1 |= (1 << OCIE1A);
interrupts();
pinMode(LED_PIN, OUTPUT);
pinMode(RX_PIN, INPUT);
// Attach RX start interupt.
pinMode(digitalPinToInterrupt(RX_PIN), INPUT);
attachInterrupt(digitalPinToInterrupt(RX_PIN), rx_start_interupt, FALLING);
Serial.begin(9600);
while(!Serial);
Serial.print("F_CPU:");
Serial.println(F_CPU,DEC);
Serial.println("Waiting for data from attiny85...");
}
void loop() {
if(rx_state == 2) {
uint16_t local_rx_data = rx_data_reg;
rx_state = 0;
Serial.println(local_rx_data,HEX);
}
}
I have tried pretty much everything to make it work, but received bytes come back garbace.
What am I missing?
Note: I'm using timer0 on attiny85 and timer1 on arduino.
Solved: switching ISR to TIMER1_COMPAB_vect and OCR1B = 64 actually works! Yay!
I recently ran into this issue of serial comms coming out as garbage on the other end. In my case, the ATtiny85 was sending bits to my laptop using an USB to TTL UART converter which had worked beautifully in other situations, but, I was just getting garbage in the Arduino IDE serial monitor.
I found a forum post which mentioned the possibility of calibrating OSCCAL.
I get a little bit fancier in my related blog post, but I tested the theory that I should calibrate OSCCAL using this code:
#include <SoftwareSerial.h>
SoftwareSerial comm(-1, 0);
static const int anchor = 128;
void
print_osccal(int v) {
comm.println(F("********************************"));
comm.print(F("OSCCAL = "));
comm.println(v);
comm.println(F("********************************"));
}
void
setup() {
delay(5000);
comm.begin(300);
OSCCAL = anchor;
print_osccal(anchor);
delay(5000);
}
void
loop() {
int x;
for (int i = 1; i < 128; ++i) {
x = anchor + i;
OSCCAL = x;
print_osccal(x);
delay(1000);
x = anchor - i;
OSCCAL = x;
print_osccal(x);
delay(1000);
}
}
It was a revelation seeing garbage all of a sudden transform to nicely formatted messages in the serial monitor (and, of course, back to garbage as the search is an infinite loop).
Now, the ATtiny85's internal oscillator can only support 1 MHz and 8 MHz. As I understand it, OSCCAL exists because this internal oscillator is 1) not very accurate and 2) is very sensitive the temperature.
If the ATtiny85 is set to run at 16MHz, it needs a reliable, external oscillator, and no amount of fiddling with OSCCAL might help. But, it did in my case allow me to discover the value(s) which made SoftwareSerial tick at 8 MHz and a range of baud rates from 300 to 38400.
This allowed me to get the right value for the bit banging UART to work with the 1MHz clock which is not supported by SoftwareSerial.
I am interfacing LM35 with Atmega8. To display digits I use 7 segment LED anode display that I connect to AVR both ends (it handles it without transistors so why not). Strange thing happens:
res value after assigning it from adc is 237 (23.7 degrees). I want to print on my display the first digit (2).
If I leave last line in the while commented out, the display first shows digit 2 correctly but after the first delay it shows 1 instead of 2. Otherwise I get correctly digit 2. Why is this happening?
#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU
#include <avr/io.h>
#include <util/delay.h>
#define DELAY_IN_MS 500 /* 0.5 sec */
int numbers[] = {
0b01000000,
0b01110011,
0b00100100,
0b00100001,
0b00010011,
0b00001001,
0b00001000,
0b01100011,
0b00000000,
0b00000001,
0b11111111 // off
};
uint8_t digits[3];
void initADC()
{
ADMUX=(1<<REFS1)|(1<<REFS0);
ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while(!(ADCSRA & (1<<ADIF)));
//Clear ADIF by writing one to it
ADCSRA|=(1<<ADIF);
return(ADC);
}
int main()
{
DDRD = 0xFF;
PORTD = 0xFF;
DDRB = 0b00000001;
PORTB = 1;
initADC();
uint16_t adc_value;
uint16_t res;
while(1)
{
adc_value = 0;
for (int i = 0; i < 250; i++)
{
adc_value += ReadADC(0);
}
adc_value=(adc_value/25)/4;
res = adc_value;
for(int j = 2; j >= 0; j--) {
digits[j] = res%10;
res /= 10;
}
uint8_t dig = digits[0];
PORTD = numbers[dig];
_delay_ms(DELAY_IN_MS);
// if following is uncommented there blinks digit two correctly
// if commented there is unblinking digit 1
PORTD = numbers[10]; // display off
}
return 0;
}
The problem was induction.
My circuit had many wires in non-soldering-field. When the display was on, there was a lot of induction going on changing resulting voltage on ADC input/LM35 output.
There is more than one solution.
1) Software: I moved ADC conversion into the interruption function. It turns of the displays, converts value from lm35 and displays digit on proper display. It happens so fast that the eye cant perceive it.
I prefer this one for now, because it makes my circuit simpler.
2) Hardware: adding L/C or R/C filter to adc pin should resolve the issue.
Full code for 1)
#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define DELAY_IN_MS 5000 /* ms */
#define NUM_OF_MEASUREMENTS 100
#define NUM_DISPLAYS 3
int numbers[] = {
0b10000001,
0b10011111,
0b10100100,
0b10010100,
0b10011010,
0b11010000,
0b11000000,
0b10011101,
0b10000000,
0b10010000,
0b11111111 // off
};
int display = 0;
uint8_t digits[NUM_DISPLAYS];
volatile uint16_t adc_values[NUM_OF_MEASUREMENTS];
int adc_read_cycle_index = 0;
uint32_t res;
void initADC()
{
ADMUX=(1<<REFS1)|(1<<REFS0);
ADCSRA=(1<<ADEN)|(1<<ADPS2);
}
uint16_t ReadADC(uint8_t ch)
{
//Select ADC Channel ch must be 0-7
ch=ch&0b00000111;
ADMUX|=ch;
//Start Single conversion
ADCSRA|=(1<<ADSC);
//Wait for conversion to complete
while (ADCSRA & (1<<ADSC));
return(ADC);
}
void readDegrees()
{
adc_values[adc_read_cycle_index] = (ReadADC(0)*10)/4;
if(adc_read_cycle_index + 1 == NUM_OF_MEASUREMENTS) {
adc_read_cycle_index = 0;
} else {
adc_read_cycle_index++;
}
}
void fetchTemperatureDigits() {
res = 0;
for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
res += adc_values[i];
}
res /= NUM_OF_MEASUREMENTS;
for(int j = 2; j >= 0; j--) {
digits[j] = res%10;
res = res / 10;
}
}
void initTimer0()
{
// Prescaler = FCPU/64
TCCR0|=(1<<CS01);//|(1<<CS00);
//Enable Overflow Interrupt Enable
TIMSK|=(1<<TOIE0);
//Initialize Counter
TCNT0=0;
}
ISR(TIMER0_OVF_vect)
{
// turn off displays
PORTD = numbers[10];
// read ADC and convert to degrees
readDegrees();
// turn on proper anode
PORTB &= 0b11111000;
PORTB |= (1<<display);
// show digit
PORTD = numbers[digits[display]];
// show decimal point for second display (21.5 - second display shows "1.")
if(display == 1) {
PORTD &= 0b01111111;
}
// next display for next interruption
display++;
if(display == NUM_DISPLAYS) {
display = 0;
}
}
int main()
{
initADC();
for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
readDegrees();
}
DDRD = 0xFF;
PORTD = 0;
DDRB |= 0b00000111;
PORTB |= 1;
initTimer0();
sei();
while(1) {
fetchTemperatureDigits();
_delay_ms(DELAY_IN_MS);
}
return 0;
}
I am trying to make a servo controller that have a higher resolution than the ATtiny85 8-bit timer/counter. So far I have managed to get about 2000 positions on my servo (1µs/step) within a time frame of 21'000 µs. I have also managed to move 5 servos sequential and with different speed, but now I want to move them synchronous.
My biggest problem is that I don't get how I should make it happen! I have looked around on other servo codes including servo8bit library and tried to find a way. It seems that most of the examples uses compare match ISR to move the servos "at the same time", my problem is that I have a 16-bit integer that I want to compare.
Is there a way to do some magic so I can use the 8-bit compare match ISR with my 16-bit integer? Or does anyone of you have some other suggestions on how I can move my servos synchronous without using compare match ISR?
I hope my questions make sense!
Since I don't really have any code to show yet (only flawed attempts without compar match ISR that makes no sense) I post the link to my TinyServo code if it helps.
EDIT 1:
Here is the part of the code I mentioned and didn't post the first time:
void servoMove(void)
{
uint16_t nextPulse = hPulse[0];
timerSetup (); //16-bit setup for counter
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if HIGH pulse (pos) is done
{
PORTB &= ~(1 << servo[channel]);
if (i+1 < sizeof(hPulse)/sizeof(hPulse[0]))
{
nextPulse += hPulse[i+1];
}
channel++;
}
else
{
channel = 0;
oTime = 0; //resets 16-bit variable
tot_overflow = 0; //resets tot_overflow variable
TIFR |= (1 << TOV1); // clear counter1 overflow-flag
TCNT1 = 0; //resets Timer/Counter1
}
}
for (i = 0; i < sizeof(servo)/sizeof(servo[0]); i++)
{
if ( (oTime > tPulse - nextPulse) && (channel < sizeof(servo)/sizeof(servo[0])) ) //check if LOW pulse (period) is done
{
PORTB |= (1 << servo[channel]);
nextPulse -= hPulse[i];
channel++;
}
}
}
void servoPosSet(volatile uint16_t pos[], uint8_t size)
{
for (i = 0; i < size; i++)
{
hPulse[i] = pos[i];
}
}
int main(void)
{
TCCR1 |= (1 << CS12); //set Timer/Counter1 prescaler to increment every 1 µs (PCK/8)
for (channel = 0; channel < size); channel++)
{
DDRB |= (1 << servo[channel]); //sets PB0-PB4 as output pins
}
channel = 0;
uint16_t pos[] = {2000, 1500, 1900, 1300, 1700};
uint8_t size = 5;
while(1)
{
servoPosSet(pos);
servoMove();
}
}
EDIT 2:
This is an illustration of how I think the code should work:
...but it does not!
If you have nothing else to do during the pulse, you could use a busy
loop instead of interrupts:
#include <avr/io.h>
#include <util/delay_basic.h>
/* Send a pulse of width = 4*count cycles. */
void pulse(uint16_t count, uint8_t channel)
{
uint8_t mask = 1 << channel,
old_port = PORTB,
high = old_port | mask,
low = old_port & ~mask;
PORTB = high;
_delay_loop_2(count);
PORTB = low;
}
This will give you a resolution of 4 clock cycles, or 0.5 µs with a
8 MHz clock.
Sending the pulses to the 5 servos should take at most 10 ms. Since
you repeat the pulse train every 21 ms, this leaves you 11 ms
to compute the next set of positions, which should be plenty. You could
program a timer to wake you up every 21 ms, then your main() may
look like:
int main(void)
{
static uint16_t pos[] = {4000, 3000, 3800, 2600, 3400};
uint8_t i;
/* Wake up every 21 ms. */
setup_timer();
sleep_enable();
for (;;) {
/* Update the servos. */
for (i = 0; i < 5; i++) pulse(pos[i], i);
/* Compute the next set of positions. */
...
/* Wait for timer interrupt. */
sleep_cpu();
}
}