ADC is not working!

Go To Last Post
136 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

 

I know I'm doing a stupid mistake right now, but I couldn't find it. I tried to look for codes online.

 

I just tested my hardware with Arduino code and the ADC is working so the wiring is OK for a start.

 

This is my code:

void adc_init(void)
{
	ADCSRA|=0x80;								// enable ADC
	DIDR0=0x01;								// disable PC0 dig buffer
}

uint16_t adc_read(void)
{
	uint16_t joystick_AN_x;
	ADMUX=0x40;								// vRef & CH0
	ADCSRA|=0x40;							        // start ADC
	while(!(ADCSRA&(1<<ADIF)));       					// wait until process is finished
	ADCSRA |= (1<<ADIF);							// clear flag bit
	return ADC;
}

 

This topic has a solution.

Last Edited: Fri. Sep 14, 2018 - 04:19 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
This is my code:

Show a complete test program.  Tell toolchain and build settings.  Show schematic, and tell connections.  Tell clock speed and Vcc level.  Tell how you are testing; tell what you expect to happen; tell what >>is<< happening.

 

Your code doesn't look too bad, except for the gratuitous |= in the ADC initialization.  Also, for any useful results (depending on your clock speed) you will need a prescaler for the ADC clock.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why do you intermix bit names (ADIF) with hard numbers (0x80 0x40)?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

theusch wrote:

Why do you intermix bit names (ADIF) with hard numbers (0x80 0x40)?

I was going to ask that question but was too lazy.  So now I'll second it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

It would help if you specified which AVR8 you are using. (The ADC modules in the AVR8 family are very similar, but .... )
Anyway.,

Quote:
ADMUX=0x40; // vRef & CH0
is probably selecting the AVCC supply as the Vref. Is your AVCC connected ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Show a complete test program.  Tell toolchain and build settings.  Show schematic, and tell connections.  Tell clock speed and Vcc level.  Tell how you are testing; tell what you expect to happen; tell what >>is<< happening.

 

Your code doesn't look too bad, except for the gratuitous |= in the ADC initialization.  Also, for any useful results (depending on your clock speed) you will need a prescaler for the ADC clock.

 

Sorry, the test program:

#include "sensors.h"             // this library has the adc functions

uint16_t adc_val;                // for the adc data

int  main(){
  Serial.begin(9600);            // Arduino serial init
  adc_init();                    // adc init
  while(1){
    adc_val=adc_read();          // call adc function
    Serial.println(adc_val);     // print data on serial monitor
  }
}

Tool chain is AVR-GCC but written in Arduino IDE.

Clock is 16MHz and VCC is 5V are Arduino uno board values.

 

The schematics and connections are OK, because when I test the my setup with Arduino code it works.

 

Image result for arduino joystick

 

It's the same as this diagram, except I'm only testing the x-axis just to get my code work.

 

 

 

mikech wrote:
It would help if you specified which AVR8 you are using.
 

 

It's the Atmega328p of the Arduino uno board.

 

Quote:
ADMUX=0x40; // vRef & CH0
is probably selecting the AVCC supply as the Vref. Is your AVCC connected ?

 

Is the AVCC an external one? I'm guessing it's the internal one. If it's not, then this could be the problem.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

REFS1 REFS0 Voltage Reference Selection
0 0 AREF, Internal Vref turned off
0 1 AVCC with external capacitor at AREF pin
1 0 Reserved
1 1 Internal 1.1V Voltage Reference with external capacitor at AREF pin

 

According to the datasheet, if the AN voltage is external then I should clear the ADMUX register. Modifying the original code to:

void adc_init(void)
{
	ADCSRA|=(1<<ADEN);										// enable ADC
	DIDR0|=(1<<ADC0D);									// disable PC0 dig buffer
}

uint16_t adc_read(void)
{
	uint16_t joystick_AN_x;
	ADMUX=0x00;											// vRef & CH0
	ADCSRA|=(1<<ADSC);									// start ADC
	while(!(ADCSRA&(1<<ADIF)));       					// wait until process is finished
	ADCSRA |= (1<<ADIF);								// clear flag bit
	return ADC;
}

 

With the same previous application code. But the same result.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

We keep getting parts and pieces.

 

Show the >>complete test program<< you are using.

 

For a sanity check, show the >>Arduino program<< that you say works.

 

Your test program is still an Arduino sketch, right?  Does Arduino initialize the ADC?  Then indeed the mentioned |= becomes important.

 

What voltage is on the channel 0 pin?  What results do you get with the Arduino sketch?  What results do you get with your test program?

 

I cannot see the connections from that picture.

 

Do you really have functions in your .H file?

 

Arduino people:  Is the chip-include already done, so the source sketch does not need i

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Sep 14, 2018 - 12:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

while(!(ADCSRA&(1<<ADIFADSC)));

 

BR,

M

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think what Menahem is trying to tell you is that you are better off blocking on ADSC low rather than ADIF high. So this:

	ADCSRA|=(1<<ADSC);									// start ADC
	while(!(ADCSRA&(1<<ADIF)));       					// wait until process is finished
	ADCSRA |= (1<<ADIF);								// clear flag bit

becomes this:

	ADCSRA |= (1 << ADSC);									// start ADC
	while((ADCSRA & (1 << ADSC)));       					// wait until process is finished

ADIF is no longer involved in any of this and therefore there's no need to include code to try and reset the flag.

 

(the above works because once you have set ADSC it remains set until the conversion completes)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
...is that you are better off blocking on ADSC l...

Now, y'all are certainly free to make recommendations.  Even applauded for doing so.  But first, I'd like to see you list the reason that OP's problem arose, rather than simply proposing an alternate method of doing an operation.  Is there a flaw or typo in OP's ADIF-blocking handling?  If so, what is it?  If not, how will changing to observation of ADSC help?

 

Heck, we do not even know what "doesn't work" means.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

• Bit 4 – ADIF: ADC Interrupt Flag
This bit is set when an ADC conversion completes and the Data Registers are updated. The ADC Conversion
Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set.
 

The ADIE bit was not mentioned in the OP's code.

M

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Menahem wrote:
The ADIE bit was not mentioned in the OP's code.
Why would it need to have been? He was using ADIF synchronously not asynchronously.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Menahem wrote:

while(!(ADCSRA&(1<<ADIFADSC)));

It's been a long time since I used the ADC, but is this really the difference between working and not-working code?  Atmel example code shows checking ADIF just as OP is doing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Ok, sorry, never mind, I thought this bit worked only in interrupt context.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
you will need a prescaler for the ADC clock.

 

This is the solution, the code doesn't work without prescaler bits!

 

Why it's not mentioned in the datasheet? I didn't know they are so important for the operation of the ADC module.

 

It's working now!!

 

This is the code:

void adc_init(void)
{
	ADMUX = 0x40;
	ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1);
}

uint16_t adc_read(void)
{
	ADCSRA|=(1<<ADSC);									// start ADC
	while(!(ADCSRA&(1<<ADIF)));       					// wait until process is finished
	ADCSRA |= (1<<ADIF);								// clear flag bit
	return ADC;
}

So simple! 

Last Edited: Fri. Sep 14, 2018 - 04:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
Why it's not mentioned in the datasheet?
Oh come on - there's a whole section on ADC clocking. If you were too lazy to read the whole chapter on ADC what do you expect ??

Last Edited: Fri. Sep 14, 2018 - 04:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:

theusch wrote:
you will need a prescaler for the ADC clock.

 

This is the solution, the code doesn't work without prescaler bits!

 

Why it's not mentioned in the datasheet? I didn't know they are so important for the operation of the ADC module.

Well, if you see a clock divider configuration setting that varies from 2 to 128, that should be a hint to look for further information on what setting is appropriate for your situation.  For every configuration register for every module you use, you need to account for every bit, having a reason to either leave it in the default state or to change it.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
This is the solution, the code doesn't work without prescaler bits!

Well sure it does.  But YOU NEVER TOLD US WHAT NOT-WORKING MEANT.  Since you say "doesn't work" we can guess, now, that the result isn't what you expected.  How many times did I ask the same questions?

 

wolfrose wrote:
Why it's not mentioned in the datasheet? I didn't know they are so important for the operation of the ADC module.

In particular

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Sep 14, 2018 - 05:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
But YOU NEVER TOLD US WHAT NOT-WORKING MEANT.  Since you say "doesn't work" we can guess, now, that the result isn't what you expected.  How many times did I ask the same questions?

 

Sorry! I mean it gives me 1023 all the time, no change in the result.

 

 

kk6gm wrote:
Well, if you see a clock divider configuration setting that varies from 2 to 128, that should be a hint to look for further information on what setting is appropriate for your situation.  For every configuration register for every module you use, you need to account for every bit, having a reason to either leave it in the default state or to change it.

 

OK, how to know the original input clock to the ADC? To count the range required which is between 50-200kHz.

 

The best prescaler I got until now is:

 

ADCSRA = (1<<ADEN)|(1<<ADPS2);

Which means the clock is divided by 8.

 

clawson wrote:
Oh come on - there's a whole section on ADC clocking. If you were too lazy to read the whole chapter on ADC what do you expect ??

 

LOL I'm sorry you're right, that's embarrassing I tend to get reading the datasheet as fast as I can, and when I go back to check it again I go to the register description section, didn't check this part thoroughly.

 

I think like, "what are the bits to get this thing to work?" didn't thought that the prescaler bits are so important. I thought about the prescaler bits, but I thought like "oh it should work with no prescaler bits to get the fastest results".

 

Also said to myself "why would they need to slow it down with prescaler bits? I guess anyone like to get ADC results very fast!"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have another hardware problem.

 

 

The SW button of the joystick is flickering all the time, how to fix it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
Sorry! I mean it gives me 1023 all the time, no change in the result.

So then running the ADC too fast/out of specification surely makes sense.  Why couldn't you have told us the voltage on the pin, the "good" results, and the "bad" results?

 

wolfrose wrote:
LOL I'm sorry you're right, that's embarrassing I tend to get reading the datasheet as fast as I can, and when I go back to check it again I go to the register description section, didn't check this part thoroughly.

I'll get, say, a new device and use it without reading all the instructions, perhaps.  But when there is a puzzling situation I take care to read the manuals thoroughly, at least for the area of interest.

 

Think about all the time you spent struggling with the situation, when a bit of orderly thinking along with some research into printed materials would have eliminated that.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

The voltage on the SW is 1.5V! It's like floating.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I do not know what "SW" is.  "Software"?  But that makes no sense to me.  It is >>you<< that is measuring some signal.  What signal is it?  What does your picture have to do with ADC not working?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

I do not know what "SW" is. 

It's the push button of the joystick module. It's logic ON/OFF switch.

 

 

"Software"? 

This is the software for reading this pin:

    if (PINC&(1<<PINC2))
    {
        PORTD |= (1<<PD5);          // button with pullup resistor 
    }
    else 
    {
        PORTD &= ~(1<<PD5);
    }

 

 

It is >>you<< that is measuring some signal.  What signal is it? 

What does your picture have to do with ADC not working?

I'm sorry I didn't provide enough information about this issue.

 

As I resolved the ADC problem, which is I was testing with the joystick in the picture, this joystick has 3 pins, 2 analog for xy-axis and 1 digital pin for ON/OFF as press button.

 

This button should have either 5V or 0V but not 1.5V.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, sorry again this pin has to be pulled-up.

 

https://www.youtube.com/watch?v=MlDi0vO9Evg

 

Just noticed this man pulled the input pin high and realized the problem I have.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

So reading the SWitch is an entirely different topic than the "ADC doesn't work?

 

Haven't you gotten the hints from earlier?

theusch wrote:
Show a complete test program. Tell toolchain and build settings. Show schematic, and tell connections. Tell clock speed and Vcc level. Tell how you are testing; tell what you expect to happen; tell what >>is<< happening.

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I resolved both problems of the ADC and the button.

 

Also now I have a question regarding the same code, which is a function containing both measurements, 2 ADC and 1 switch. It's about the speed of the function, the button press misses my presses.

 

Is it OK to continue investigating this issue here?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:

OK, how to know the original input clock to the ADC? To count the range required which is between 50-200kHz.

 

The best prescaler I got until now is:

 

ADCSRA = (1<<ADEN)|(1<<ADPS2);

Which means the clock is divided by 8.

Surely you know the clock rate that your AVR is running at.  Pick a prescaler that reduces that to 50-200 kHz.  If you're running an Arduino-type board at 16 or 20 MHz, your only choice is a prescale of 128.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:

Surely you know the clock rate that your AVR is running at.  Pick a prescaler that reduces that to 50-200 kHz.  If you're running an Arduino-type board at 16 or 20 MHz, your only choice is a prescale of 128.

 

OK, my Arduino uno runs at 16MHz, does that mean the input clock to the ADC module is 16MHz?

 

If so then I don't know how things are working!

 

I did a prescaler of ADPS2, which divides the clock by 8, so the input clock is 2MHz!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
uint16_t adc_read(uint8_t chan)
{
        ADMUX = (1<<REFS0) | chan;      //select max channel and AVCC for ref
    
	ADCSRA |= (1<<ADSC);		// start ADC
	while((ADCSRA & (1<<ADSC)));    // wait until process is finished

	return ADC;
}

even simpler and more effective.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If you want 10 bit accuracy you need the ADC clock between 50kHz and 200kHz.
.
With a CPU clock of 16MHz you need to set ADPS to /128 which means the ADC will be clocked at 125kHz. As it takes 13 clocks to make a reading this means your sample rate will be 9.6kHz.

Last Edited: Sat. Sep 15, 2018 - 11:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Hello,   

    In regards to this problem, I don't believe that it is necessary to set the ADIF flag by software after the completion of the conversion.  Having the ADC clock run between 50 and 200 KHz is important, and by doing so, the ADC starts working.

 

   I must question WHY you are doing this ADC test using the writes to internal AVR registers.   The entire purpose of having the Arduino keyword extensions is to avoid working with internal registers in the main program.   Arduino is designed to make these specific microcontroller operations invisible.  Arduino code is portable between microprocessors so that all the details of getting an ADC conversion are internal to the analogRead() library code.  You don't have to bother with the microprocessor is doing it.   I don't recommend using references to a microprocessor's internal registers unless there is at least a 5X gain in speed by changing the registers directly instead of using the Arduino keywords.  

 

   Some guys say that they like to learn how the CPU works on a register level and that this makes them better programmers.  I respectfully disagree.  There is a sequence of difficulty in programming:  machine code (1 and 0 entered directly _not done since 1975),  assembler,  C or other high-level languages, and C++ (Arduino).   Using direct register access to replace Arduino keywords is like buying a new car and then taking it apart in your driveway in order to see how it works.   Every hour spend studying the internal register operations of a microprocessor is an hour that is not being spent developing the operation and user-interface of the program.  Which is where the money and the action is.   Plus, when you change to a more powerful processor, you have to learn the internal register structure for the new CPU.  Which is crazy if you already have a language that already handles internal common embedded system functions for that CPU, like Arduino.   Who cares HOW analogRead(pin) works as long as you get a correct ADC result from using it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Simonetta wrote:
In regards to this problem, I don't believe that it is necessary to set the ADIF flag by software after the completion of the conversion.

That line does not >>set<< the ADIF flag.  It clears it.  Please clarify your claim for future readers.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

There are two ways to read the ADC. Either you set ADSC then wait for it to automatically clear that signals the end of conversion. Or you wait for ADIF to become set that signals completion. If you do the latter you have to manually clear it, which is what he's trying to do in the initial code here.
.
As for the argument against trying to learn things and improve ones education. That is just madness. Why do any of us bother programming AVRs at all in that case?

Last Edited: Sat. Sep 15, 2018 - 01:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

yeah right, and having all this layers of abstractions killing the CPU throughput. I worked with Arduino ecosystem for a while, until the point where it just reached saturation. That said, it is inconsistent environment to learn something in-depth rather copy-pasting back-packs, and booster-pack and whatever plug-ins from other people and porting the code, Hence it can't be considered as learning platform rather outsourcing platform. But these days everything is being ab(used) even our language so programs become sketched, then outsourcing becomes easy2use, and so on. ^_^))))

 

program is working......

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

even simpler and more effective.

Absolutely!

 

clawson wrote:
If you want 10 bit accuracy you need the ADC clock between 50kHz and 200kHz. . With a CPU clock of 16MHz you need to set ADPS to /128 which means the ADC will be clocked at 125kHz. As it takes 13 clocks to make a reading this means your sample rate will be 9.6kHz.

 

According to this table:

Image result for ADPS prescaler

 

 

I got these results only by setting ADPS2 which is 16MHz/16 = 1MHz. I'm here pulling the y-axis to the max and min ranges for testing.

 

 

The results are OK, but it works with 1MHz input clock? Is there another clock divider before the ADC module?

 

 

 

Simonetta wrote:

 

   I must question WHY you are doing this ADC test using the writes to internal AVR registers.   The entire purpose of having the Arduino keyword extensions is to avoid working with internal registers in the main program.   Arduino is designed to make these specific microcontroller operations invisible.  Arduino code is portable between microprocessors so that all the details of getting an ADC conversion are internal to the analogRead() library code.  You don't have to bother with the microprocessor is doing it.   I don't recommend using references to a microprocessor's internal registers unless there is at least a 5X gain in speed by changing the registers directly instead of using the Arduino keywords.  

 

   Some guys say that they like to learn how the CPU works on a register level and that this makes them better programmers.  I respectfully disagree.  There is a sequence of difficulty in programming:  machine code (1 and 0 entered directly _not done since 1975),  assembler,  C or other high-level languages, and C++ (Arduino).   Using direct register access to replace Arduino keywords is like buying a new car and then taking it apart in your driveway in order to see how it works.   Every hour spend studying the internal register operations of a microprocessor is an hour that is not being spent developing the operation and user-interface of the program.  Which is where the money and the action is.   Plus, when you change to a more powerful processor, you have to learn the internal register structure for the new CPU.  Which is crazy if you already have a language that already handles internal common embedded system functions for that CPU, like Arduino.   Who cares HOW analogRead(pin) works as long as you get a correct ADC result from using it?

 

I agree with your point of view with the speed of programming development. And how things are more simpler and I have to move to the ready libraries to save the time in developing more complicated projects.

But before I had issues with pre-written Arduino libraries and at that time I decided to write my own code, to reach a decent level in the basics of programming.

 

 

 

clawson wrote:
There are two ways to read the ADC. Either you set ADSC then wait for it to automatically clear that signals the end of conversion. Or you wait for ADIF to become set that signals completion. If you do the latter you have to manually clear it, which is what he's trying to do in the initial code here. . As for the argument against trying to learn things and improve ones education. That is just madness. Why do any of us bother programming AVRs at all in that case?

 

I agree with the two ways, also just modified my code to Kartman's recommendation yes

 

And for your 2nd point I agree with you, the basics are important. I didn't reach the level to evaluate which is better to go with pre-written libraries or do your own libraries.

 

But I've worked with Arduino libraries, but I had issues so that I have to modify the code for my goals, so I knew that I better learn how to write libraries.

Maybe after some time, when I feel that I got a good grasp of the basics and do intermediate to advanced projects in C and then transfer that to C++ which is a good powerful learning curve, then I might go with the libraries, as I have enough experience to deal with them confidently.

Last Edited: Sat. Sep 15, 2018 - 02:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You can run faster than 50..200kHz but then you can't guarantee the full size 10 bit accuracy. But if you only rely on 8 bit (say) then 1MHz will probably be OK.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
You can run faster than 50..200kHz but then you can't guarantee the full size 10 bit accuracy. But if you only rely on 8 bit (say) then 1MHz will probably be OK.

 

Didn't understand what you mean exactly!

 

You mean that I can go faster than 50-200kHz, but I can't get the full size 10 bit accuracy? But in the pictures I posted those values are full 10-bit with the speed of 1MHz.

 

I know you said I can't guarantee, which means that there's a chance to get the full 10-bit accuracy.

 

Also the 2nd part of the reply isn't so clear.

 

 

 

============================================================================================================

 

Beside the clock investigation I have another problem of the button press of the joystick.

 

I can't get it precisely, I miss the press, because I use _delay_ms(500);

 

But how to improve my function to by pass this problem?

 

This is the functions:

void joystick_init(void)
{	
	DDRC=0xF8;								// x,y,button PC0,PC1,PC2 inputs rest output
	DDRB=0x20;								// PB5 output
	PORTC=0x04;								// Pull 3rd pin HIGH for button press
}

void joystick_read(void)
{
    uint16_t joystick_AN_xy[2];

    joystick_AN_xy[0] = adc_read(0);		 			// read AN0
    joystick_AN_xy[1] = adc_read(1);       				// read AN1

    if (!(PINC&(1<<PINC2)))
	{
		PORTB |= (1<<PB5);     			// button with pullup resistor 
	}
    else
	{
		PORTB &= ~(1<<PB5);
	}
	
    sprintf(val_arr,"%d",joystick_AN_xy[0]);
    LCD_string("x value:");
    move_cursor(1,10); 
    LCD_string(val_arr);
    sprintf(val_arr,"%d",joystick_AN_xy[1]);
    move_cursor(2,1);
    LCD_string("y value:");    
    move_cursor(2,10);      
    LCD_string(val_arr);
    _delay_ms(500);
    clr_dis();
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

wolfrose wrote:
Didn't understand what you mean exactly!

I was determined to stay out, but...  I'm the tuna on the lower right.  The quote above is the "baited hook or lure".

Image result for trolling

 

How many times has someone mentioned "what does the datasheet say"?    Often.  How are most questions answered here on this forum?  By looking at the datasheet, and parroting the applicable excerpt.  Besides that, Cliff >>posted<< the part of the datasheeet that addresses your query in #17.  Did you read that?  Is there a great language barrier?  How many times where you asked what speed your AVR is running at?  What was my response to your "it doesn't work without a prescaler"?  I said  something like "Sure it does, but you probably are not getting the results that you expect".  How many times were you asked what voltage is on the pin?  How many times were you asked "what results do you expect"?   How may times were you asked "what results are you getting"?  How many times were you asked to provide a complete test program that demonstrates the symptoms?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Sat. Sep 15, 2018 - 03:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You will always READ 10 bits.  But how many of those bits are valid, and how many are essentially random?  If you get a result of b1011010011, is the LSB valid?  The 3rd LSB?  The only way you can have any assurance that all 10 bits are valid is to run the ADC clock within the specified range.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

How many times has someone mentioned "what does the datasheet say"?    Often.  How are most questions answered here on this forum?  By looking at the datasheet, and parroting the applicable excerpt.  Besides that, Cliff >>posted<< the part of the datasheeet that addresses your query in #17.  Did you read that?  Is there a great language barrier?  How many times where you asked what speed your AVR is running at?  What was my response to your "it doesn't work without a prescaler"?  I said  something like "Sure it does, but you probably are not getting the results that you expect".  How many times were you asked what voltage is on the pin?  How many times were you asked "what results do you expect"?   How may times were you asked "what results are you getting"?  How many times were you asked to provide a complete test program that demonstrates the symptoms?

 

Yes, but I also said that I'm not sure what's the input clock to the ADC module? Is it the same like the system clock which is 16MHz? If so then I understand clawson points.

.....

 

OK, a quick revision of the datasheet, there's no clarification that the clock input to the ADC is modified. So, the input clock is 16MHz to the prescaler.

 

So, my calculations are right. At 1MHz I can get 10-bit resolution.

 

 

But I still have another issue regarding the button of the joystick, can I ask about it here, or open a new thread?

Last Edited: Sat. Sep 15, 2018 - 04:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:

You will always READ 10 bits.  But how many of those bits are valid, and how many are essentially random?  If you get a result of b1011010011, is the LSB valid?  The 3rd LSB?  The only way you can have any assurance that all 10 bits are valid is to run the ADC clock within the specified range.

 

Yes, I'm getting the readings OK with this speed. I tried lower speeds but I can say they are similar in the aspect of accuracy, didn't encounter glitches or wrong readings.

So I can say I'm pretty much comfortable with this speed as it's quick.

 

If I go into a more complicated project, I don't know with more code that could be errors with this speed. But for now it works OK.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

wolfrose wrote:

Yes, I'm getting the readings OK with this speed. I tried lower speeds but I can say they are similar in the aspect of accuracy, didn't encounter glitches or wrong readings.

So I can say I'm pretty much comfortable with this speed as it's quick.

 

If I go into a more complicated project, I don't know with more code that could be errors with this speed. But for now it works OK.

Well, it's your funeral, as the saying goes.  If you really need 75k samples/sec @ 10 bits, pick a different chip family.  If this is a one-off hobby project that's one thing, knock yourself out.  Anything more demanding of correct and repeatable results, just don't do this (run the ADC at 1 MHz).  If AVR actually thought their ADCs would work at 1 MHz, I promise you that they would tell us!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kk6gm wrote:

If you really need 75k samples/sec @ 10 bits, pick a different chip family. 

I actually don't need 75k samples/sec @ 10 bits.

 

 

If this is a one-off hobby project that's one thing, knock yourself out.  

This code would be a base for my project I''m working on right now, which is a set of sensors functions. Which would be of course a source of functions for my next advanced projects.

 

Anything more demanding of correct and repeatable results, just don't do this (run the ADC at 1 MHz).  If AVR actually thought their ADCs would work at 1 MHz, I promise you that they would tell us!

LOL you're right! Of course I know the limits that AVR proved for the stable well working range of speed for ADC, I'm just surprised that it works at 1MHz.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why are you ‘surprised’ it ‘works’? Again, the datasheet tells you what to expect. Have you considered that your observations are not correct? What you consider is ‘working’ is really just hiding a defect? There is a difference between working by design and working by divine intervention.

Regarding your problem with detecting the button press - we’ve been through this many times before as I’ve suggested you read a tutorial I wrote regarding multitasking. If you follow my simple example that i describe in the tutorial, then this should allow you to solve your problem and many others you might encounter. I’ve given the links before or you might Google avrfreaks kartman multitasking

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

As others have said, after a conversion you will always read some 10 bit value but how many of those bits can you trust? If you want to rely on all 10 bits then you have to run the ADC at 200kHz or less and that will give you a max 15.3kHz sample rate. This is a design limitation of the Tiny/mega AVR. If you need faster samples/better accuracy switch to another chip. The first obvious choice may be to the Xmega range which can offer 1Msps. Be warned though that while they call them AVR you will not recognize the ADC (or the timers or anything else) so it's very like switching to a whole new chip range so you might want to consider widening your horizon and looking at something different. Perhaps Cortex in some form?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

As others have said, after a conversion you will always read some 10 bit value but how many of those bits can you trust? If you want to rely on all 10 bits then you have to run the ADC at 200kHz or less and that will give you a max 15.3kHz sample rate. This is a design limitation of the Tiny/mega AVR. If you need faster samples/better accuracy switch to another chip. The first obvious choice may be to the Xmega range which can offer 1Msps. Be warned though that while they call them AVR you will not recognize the ADC (or the timers or anything else) so it's very like switching to a whole new chip range so you might want to consider widening your horizon and looking at something different. Perhaps Cortex in some form?

Absolutely! I really like your recommendation, and I like one day to work with another chip, but for now I'm still working with the 8-bit architecture. I'm not interested learning different architecture. Working with AVR and PIC is enough for now.

 

Also I'm not interested in higher speeds, I'm just testing things out, it just that it worked at 1MHz was interesting to me, but running in the recommended range would be the best setting for my projects.

 

 

Kartman wrote:
Why are you ‘surprised’ it ‘works’? Again, the datasheet tells you what to expect. Have you considered that your observations are not correct?
 

 

What you consider is ‘working’ is really just hiding a defect?

Of course I know I'm not writing very optimized code, just getting it to work is good. But for example, I got the joystick to work, but the button press misses all the time because I'm using a delay in the function. So I have to search for the solution maybe using pin or timer interrupts and so on.

 

 

There is a difference between working by design and working by divine intervention.

LOL I guess I'm working with the second type :)

 

Regarding your problem with detecting the button press - we’ve been through this many times before as I’ve suggested you read a tutorial I wrote regarding multitasking. If you follow my simple example that i describe in the tutorial, then this should allow you to solve your problem and many others you might encounter. I’ve given the links before or you might Google avrfreaks kartman multitasking

OK, thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:

Also I'm not interested in higher speeds, I'm just testing things out, it just that it worked at 1MHz was interesting to me, but running in the recommended range would be the best setting for my projects.

"Worked" for a particular chip, at a particular temperature and voltage, with a particular (and non-varying or slow varying, I'm guessing) input voltage.  Once you narrow down the operating range that way then yes, the hardware might surprise you at how much it can exceed the published specs.  And you can get away with that for individual hobby projects.  But "real" devices may be required to operate over large temperature and voltage ranges, using any chip that's not DOA (no hand-selecting), with much more rapidly varying input signals.  That's when you stick to the published specs, live longer and keep more of your hair.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 it just that it worked at 1MHz was interesting to me,

 

You are missing the point.

 

It didn't "work".

 

The fact that you have a number in a register doesn't mean that it is accurate, or that it will appear to give a close answer on another board with another chip.

 

The data sheet tells you the maximum sampling speed to be "in spec", and one uses the pre-scaler to slow the micro's clock down so that the ADC's clock is running in spec.

 

Learn good habits now, which is running your hardware in spec!

 

You need to really, really, understand the hardware in great detail before you begin intentionally designing out of spec.

 

JC

  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Why are you ‘surprised’ it ‘works’? Again, the datasheet tells you what to expect. Have you considered that your observations are not correct? What you consider is ‘working’ is really just hiding a defect? There is a difference between working by design and working by divine intervention. Regarding your problem with detecting the button press - we’ve been through this many times before as I’ve suggested you read a tutorial I wrote regarding multitasking. If you follow my simple example that i describe in the tutorial, then this should allow you to solve your problem and many others you might encounter. I’ve given the links before or you might Google avrfreaks kartman multitasking

 

ISR(PCINT1_vect)
{
	if (!(PINC&(1<<PINC2)))
	{
		PORTB |= (1<<PB5);     			// button with pullup resistor
	}
    else
	{
		PORTB &= ~(1<<PB5);
	}
}

 

I used this as a start, I've looked into your tutorial, yes that looks pretty much a sketch for a big embedded system program; like, car system, more complicated device or other devices which use a lot of functions and need multitasking.

 

I followed this video:

https://www.youtube.com/watch?v=...

 

Very helpful,

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

DocJC wrote:
It didn't "work".

I took the other tack -- of course it "worked".  It "worked" with no prescaler as well.  But...

theusch wrote:
wolfrose wrote: This is the solution, the code doesn't work without prescaler bits! Well sure it does. But YOU NEVER TOLD US WHAT NOT-WORKING MEANT. Since you say "doesn't work" we can guess, now, that the result isn't what you expected.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Errr no - it is intended for a small embedded system. Once again, you’ve come to the wtong conclusion based on faulty evidence.

Why did you choose to use an external interrupt? That would have to be the worst choice. I wrote a tutorial ‘the traps when using interrupts’ where I explain this. There’s also numerous threads on this site where we repeat the same advice.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Errr no - it is intended for a small embedded system. Once again, you’ve come to the wtong conclusion based on faulty evidence.

 

I'm sorry didn't read your whole tutorial because it was long, but I like the idea of "task" strategy. But there are things that I think it's the next higher level of programming I should learn, there're things that I don't understand; like:

1. "pgm_read_byte" in:

// enable a task for execution
void set_task(char tsk)
{
  task_bits |= pgm_read_byte(&bit_mask[tsk]);       /* sets a task bit */
}

 

2. This part also:

void task7(void)
{
    PORTB ^= (1<<5);
    task_timers[7] = 50;		//every 500ms  
    reset_task(7);
}

I think this function means: Toggle the pin output >> set the 8th element to 50 >> reset the task. But why would I reset it at this point?

 

This part I understand as it's functions inits and an ISR, not so difficult:

void init_devices(void)
{
    //stop errant interrupts until set up
    cli(); //disable all interrupts
    
    
    DDRB = 0x30;	//port 4 & 5 as outputs
    
    timer0_init();
    
    MCUCR = 0x00;
    EICRA = 0x00; //extended ext ints
    EIMSK = 0x00;
    
    TIMSK0 = 0x02; //timer 0 interrupt sources
    
    PRR = 0x00; //power controller
    sei(); //re-enable interrupts
 //all peripherals are now initialized
}
//TIMER0 initialize - prescale:1024
// WGM: CTC
// desired value: 10mSec
// actual value: 10.048mSec (-0.5%)
void timer0_init(void)
{
    TCCR0B = 0x00; //stop
    TCNT0 = 0x00; //set count
    TCCR0A = 0x02;	//CTC mode
    OCR0A = 0x9C; 
    TCCR0B = 0x05; //start timer
}

ISR(TIMER0_COMPA_vect)
{
 //TIMER0 has overflowed
 	tick_flag = 1;
}

 

3. This is the part if which has the things I don't get right now, I just need time to figure them, it's the function which manages the tasks of the program.

 

So if I have a project which deals with a lot of things, I would need a strategy or program management like this one:

void task_dispatch(void)
{
  /* scan the task bits for an active task and execute it */
  char task;
  
/* take care of the task timers. if the value ==0 skip it
	else decrement it. If it decrements to zero, activate the task associated with it */

  task=0;
  while (task < NUM_TASKS )
    {
    if (task_timers[task])
	   {
  	   task_timers[task]--;            /* dec the timer */
	   if (task_timers[task] == 0 )
	   		{
	    	set_task(task); /* if ==0 activate the task bit */
			}
	   }
    task++;
    }

  task = 0; /* start at the most significant task */
  while (task <= NUM_TASKS )
    {
	  if ((task_bits & pgm_read_byte(&bit_mask[task])))
	  		{
	  		break; /* if activate task found..*/
			}
      task++;         /* else try the next one */
    }

 

But I would say this is based on your skills, that's the way you operate your system. If I'm going to deal with a project which has a lot of operations; I would need a similar management strategy but what's interesting now is that I'm thinking if I'm going to do such a project, then how would I do it? Of course maybe not optimized as your but ... hmmm I don't know I'm not at that level right now.

 

 

Why did you choose to use an external interrupt? That would have to be the worst choice. I wrote a tutorial ‘the traps when using interrupts’ where I explain this. There’s also numerous threads on this site where we repeat the same advice.

Really! I was so happy that I solved this problem. Is it "the worst choice" oh wow I'm sorry but I think you're aware from my threads and issues that I'm a beginner << How many years I was saying the same excuse :)

 

Yes tell me about these threads.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have a question about types of sensors I'm looking for detection of people entrance, car speed radar .. etc for diploma projects, is it ok to ask here or open a new thread?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
open a new thread?
Time for that I think.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK thanks for replying and supporting.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

1. pgm_read_byte - You should've Googled that before posting! Put simply it reads the byte from the flash rather than from ram. Why? because I have a table of values that don't change (ie constants), so it makes sense to put them into flash rather than use precious ram. I suggest you Google pgm_read_byte now and find out the whole story.

 

2. Didn't I explain this in the tutorial?? task_timers are an array of down counters - one per task. I want to run this task again in 50 ticks time - 500ms. I then reset the task_bit, otherwise the task is still set to run. Why downcounters? Because the cpu can test for zero or non-zero easily. It's a throwback to programming in assembly code. Consider if I chose upcounters - I'd need a variable to store the count and another variable for the compare value - that's twice the memory. So a downcounter makes sense to me - just like an egg timer - it rings when it gets to zero.

 

3. Again, didn't I go to length describing this in the tutorial? Basically each task has an associated bit in task_bits. If it is 1, then the task is to be run. Each task has a task_timer. When it counts down to 0, the associated task bit is set. So dispatch() counts down all the task timers then scans task_bit for the highest priority task - bit 0 is the highest. When it finds the first task with it's bit set, then it calls it. That task then has 10ms to do what it needs to do then return. Then dispatch() does the same thing over again and again and again... for each tick.

 

I think you're failing to see the inherent simplicity in it. I would suggest you try it with a simple project that flashes two leds at different speeds, reads the adc and prints on the lcd a count that increments and displays the adc values. That's four tasks. Add another to read your input. In many of my commercial projects I had the following tasks:

 

lowest priority was the lcd display. It updated the lcd with the current values etc every 100ms. If the user held the menu pushbutton for 10 seconds, then the menu task was activated.

next was the menu. This had three pushbuttons and allowed the user to scroll through menus and change settings. This only ran when the menu was entered and stopped the display. If there were no button presses for 10 seconds, the display task would resume.

next was the adc task that read the adcs, did scale and offset calcs, tested alarms values etc. This ran every 100ms.

next was the i/o task that took care of updating relays etc. This ran every 100ms

highest priority was the communications task - this only ran if there was a request from the serial isr.

 

As far as the user was concerned, everything ran in parallel but was actually being run in a sequence. 

 

How would you propose to manage those tasks in an orderly fashion? Using delays quickly becomes a problem as you've found, so you need to use a timer - be it a 'soft' timer or directly using timer hardware. If you need many timers, you quickly run of of hardware ones, so you use 'soft' timers like I've done. Then you need to manage what tasks are run and you'll run in the need for priority. In my example, the lcd task can be deferred for a tick or two and the user won't notice the difference. I give the communications the highest priority as I want my serial comms with the host computer to be fast as I might have a number of devices that all need to be polled within a required time.

 

The same principles can apply to human resource management - how can one person cook dinner, talk to the children and answer the doorbell? Not all at once!

 

You're not at the level? Well, you want to do two things at once, so you better start learning as you're going to want to do three things, then four things at a time.

 

Seems you also need to learn how to use Google - in the 'olden' days I'd have to refer to text books or go to the library. Nowadays, if I have a question, I just Google it. With the likes of Stack Exchange etc, someone has asked the very same question before. So, my motto is: Google first, post question on forums later.

 

 

 

 

Last Edited: Mon. Sep 17, 2018 - 09:40 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

1. pgm_read_byte - You should've Googled that before posting! Put simply it reads the byte from the flash rather than from ram. Why? because I have a table of values that don't change (ie constants), so it makes sense to put them into flash rather than use precious ram. I suggest you Google pgm_read_byte now and find out the whole story.

Is it the same as "const" specifier? Also does this specifier put values in flash memory or only pgm functions do that?

 

Yes I got to this first link, wow it shows a lot of functions to read from flash but most of the read functions are related to each type, so the read function is the same.

https://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html

 

2. Didn't I explain this in the tutorial?? task_timers are an array of down counters - one per task. I want to run this task again in 50 ticks time - 500ms. I then reset the task_bit, otherwise the task is still set to run. Why downcounters? Because the cpu can test for zero or non-zero easily. It's a throwback to programming in assembly code. Consider if I chose upcounters - I'd need a variable to store the count and another variable for the compare value - that's twice the memory. So a downcounter makes sense to me - just like an egg timer - it rings when it gets to zero.

You of course explained that but I didn't go through all the topic, and thanks for the extra explanation.

Yes CPU can test zero easily, I think more easily than carry bit. Because carry bit could require another register to test .. I also got into the topic of the difference between carry and overflow! wow it's confusing. So checking the zero is the best yes

 

OK, got the part of timing each task, but didn't get the goal of timing each task. Why you time the tasks? You could arrange the tasks << as I consider the tasks as functions, but maybe it's different. Maybe it's not like functions that when the function finishes then it breaks or just operate and go back to the main.

 

 

3. Again, didn't I go to length describing this in the tutorial? Basically each task has an associated bit in task_bits. If it is 1, then the task is to be run. Each task has a task_timer. When it counts down to 0, the associated task bit is set. So dispatch() counts down all the task timers then scans task_bit for the highest priority task - bit 0 is the highest. When it finds the first task with it's bit set, then it calls it. That task then has 10ms to do what it needs to do then return. Then dispatch() does the same thing over again and again and again... for each tick.

Is it; for example, in case of 8-bit register I can run upto 8 tasks, like in SFR registers?

 

So I check each bit for each task, and when the task go into the dispatch function, it runs the task and also downcount the timing for the task until the timing finishes, and then the dispatch runs another task, and so on until it runs through all the tasks, and scan again.

 

But calculating the "highest priority", is the priority constant as you define the priority in the beginning of the program, or it's changing during the operation of the program? If it's changing then I guess that's really complicated for me now to work with and apply in my projects.

 

 

I think you're failing to see the inherent simplicity in it.

Yes of course, it should an easy program structure to run multiple tasks, but I'm new to this level of programming that's why I'm not understanding it completely.

 

I would suggest you try it with a simple project that flashes two leds at different speeds, reads the adc and prints on the lcd a count that increments and displays the adc values. That's four tasks. Add another to read your input. In many of my commercial projects I had the following tasks:

 

lowest priority was the lcd display. It updated the lcd with the current values etc every 100ms. If the user held the menu pushbutton for 10 seconds, then the menu task was activated.

next was the menu. This had three pushbuttons and allowed the user to scroll through menus and change settings. This only ran when the menu was entered and stopped the display. If there were no button presses for 10 seconds, the display task would resume.

next was the adc task that read the adcs, did scale and offset calcs, tested alarms values etc. This ran every 100ms.

next was the i/o task that took care of updating relays etc. This ran every 100ms

highest priority was the communications task - this only ran if there was a request from the serial isr.

 

As far as the user was concerned, everything ran in parallel but was actually being run in a sequence. 

Why you have to time everything? Of course I know you have a profound reason for that. I understand this as a method in multitasking implementations, of course the way of tasks management and scanning things, it looks smart to me right now. But I'm not imagining the functionality of this system, as how the timing works << I think it's all about the timing here, because otherwise you would normally run the tasks in a sequential pattern.

 

The procedure of executing the functions, I would think it's how things are actually is working in embedded applications where there're a lot of tasks to perform. I'm not that level yet but I'm starting to, as I have to do diploma graduation projects this semester, so I have to learn something about how to run multiple functions simultaneously and of course efficiently.

 

 

How would you propose to manage those tasks in an orderly fashion? Using delays quickly becomes a problem as you've found, so you need to use a timer - be it a 'soft' timer or directly using timer hardware. If you need many timers, you quickly run of of hardware ones, so you use 'soft' timers like I've done. Then you need to manage what tasks are run and you'll run in the need for priority. 

I would run out of hardware interrupts of course. 

 

"be it a 'soft' timer" that sounds very interesting, but also I don't know how to implement both methods, either the soft timer or the hardware timer. I'm really interested to learn that, because that would improve my projects.

 

In my example, the lcd task can be deferred for a tick or two and the user won't notice the difference. I give the communications the highest priority as I want my serial comms with the host computer to be fast as I might have a number of devices that all need to be polled within a required time.

I just need time to learn this strategy.

 

The same principles can apply to human resource management - how can one person cook dinner, talk to the children and answer the doorbell? Not all at once!

You're not at the level? Well, you want to do two things at once, so you better start learning as you're going to want to do three things, then four things at a time.

Yes, good analogy. And yes I'm doing for now this task of programming the joystick and upload the data to the LCD so that includes:

1. Reading 2 analog signals and store each one in array.

2. Put the data to the LCD.

3. Read the button press with affecting the program << I've used a hardware interrupt for that, but also looking to implement your strategy.

 

Seems you also need to learn how to use Google - in the 'olden' days I'd have to refer to text books or go to the library. Nowadays, if I have a question, I just Google it. With the likes of Stack Exchange etc, someone has asked the very same question before. So, my motto is: Google first, post question on forums later.

Of course, I can say I've a pretty good experience with Google searches, in my bachelor study I've used Google a lot and for many things, games, movies, songs, theoretical topics in life related to physics or electronics.

I downloaded a lot of text books but I don't read them. Since I got employed before 5 years, I use Google for same purposes but not as before, I think I changed a little bit not too much.

 

Now I don't use Google a lot, I want to find the results as fast as I can, in programming search I don't look now for various topics in programming because I don't want to spend much time, I try as much as I can to do the programming myself, but like usually I get stuck in simple issues :) I've done some libraries and I want to come back and improve them; for example, I want to improve my I2C and UART libraries to use ISRs so implementing them in projects would be efficient.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
Is it the same as "const" specifier?
Nope, not quite. It is true (since compiler 4.6) that anything in PROGMEM or __flash must also be tagged "const" but it does not follow that "const" alone means "in flash". You can (wastefully) also have const data in RAM too.

 

BTW as this has nothing to do with ADC I thought the plan was to take this discussion to a new, separate thread?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

wolfrose wrote:
Is it the same as "const" specifier?
Nope, not quite. It is true (since compiler 4.6) that anything in PROGMEM or __flash must also be tagged "const" but it does not follow that "const" alone means "in flash". You can (wastefully) also have const data in RAM too.

But what's the standard in most microcontroller compilers and maybe in computer programming compilers generally? That const should be in Flash or RAM? But as you recommend to take this question to the next thread.

 

BTW as this has nothing to do with ADC I thought the plan was to take this discussion to a new, separate thread?

Yes, I'm working on it right now yes 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

'const' it self means only "report as error anything that attempts to write to this". It says nothing about the location bit GCC compilers will probably put it in the ".rodata" section and the linker scripts on Von Neumann chips like ARM will likely position .rodata to be in flash. Things will be different in Harvard architectures as C doesn't really have the concept of multiple address spaces.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
'const' it self means only "report as error anything that attempts to write to this". It says nothing about the location bit GCC compilers will probably put it in the ".rodata" section and the linker scripts on Von Neumann chips like ARM will likely position .rodata to be in flash. Things will be different in Harvard architectures as C doesn't really have the concept of multiple address spaces.

 

wow I may think that I learned before that ARM runs on a different architecture, but it is interesting because how each architecture deals with the memory, and I think one is more efficient that the other.

Image result for von neumann vs harvard architecture

 

It's the Harvard that is more efficient because each memory had its own bus, which surprises me that ARM which known to be one of the most efficient, fast chips, but apparently I have no profound idea about how these chips work.

 

My new thread is on this link:

https://www.avrfreaks.net/forum/looking-appropriate-sensors-medium-diploma-graduation-projects

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If one architecture were perfect all CPUs would use it - so it's swings and roundabouts.

 

Some describe ARM (more specifically Cortex) as "modified Harvard" in fact but at the end of the day an ARM chip generally has a 4GB linear address space. For example see the first educated answer here: https://community.arm.com/proces...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

If one architecture were perfect all CPUs would use it - so it's swings and roundabouts.

 

Some describe ARM (more specifically Cortex) as "modified Harvard" in fact but at the end of the day an ARM chip generally has a 4GB linear address space. For example see the first educated answer here: https://community.arm.com/proces...

 

Yes, it's clear in the provided answer that they also have chips with either Harvard or Von Neumann architectures.

 

Just reading that answer got me the feeling that getting into one of those or any of ARM chips as working with the chip and writing libraries or complete projects that deal with the internal registers would be a lot of work and a complete different story than PIC or AVR chips. I bought a dev board which has an ARM chip but almost never worked with it, as I'm focused on AVR and PIC, I'm leaving it for the time I'm interested to get into that chip as a reward of working enough with AVR and PIC chips :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I read this in a document, really interesting that ARM doesn't have interrupt hardware.

 

An interrupt controller is a peripheral device that manages interrupts for a processor. Some embedded processor architectures (such as AVR, MSP430, and PIC) integrate a sophisticated interrupt controller, whereas others (such as ARM and x86) include only a rudimentary controller, necessitating an additional external interrupt controller, such as ARM’s VIC or x86’s PIC or APIC.

But the chip could has one integrated in it:

Note that an external interrupt controller may be located on-chip, but even so it is logically distinct from its processor.

 

 

Here:

https://www.cs.utah.edu/~regehr/papers/interrupt_chapter.pdf 

Last Edited: Mon. Sep 17, 2018 - 05:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to understand the difference between Cortex M and Cortex A too. The fact is that "A" is an application processor so won't have the facilities as an eMbedded processor.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
I read this in a document, really interesting that ARM doesn't have interrupt hardware.

LOL -- AVR (at least classic AVR8)  has as "rudimentary" interrupt controller as you might find in any microcontroller.

 

To describe the "general" ARM interrupt controller takes 200 pages:  https://www.cl.cam.ac.uk/researc...

 

I thought you just told us that

wolfrose wrote:
Now I don't use Google a lot, I want to find the results as fast as I can, ...
   So you found that scholarly paper from over a decade ago from assiduously going through your extensive library?

 

Anyway, what does that bit of information that may or may not be applicable to the single ARM-based module that you have not used have to do with ADC on an AVR8 not working in polled mode?

 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Wolrose, if reply #51 you mentioned Pin Change interrupts.

 

Are you now using that for your push button interface?

 

There are many ways to accomplish a given goal, but I think that is not a very good approach to "de-bouncing" push button switches.

 

The experts here can chime in if they wish...

 

The point is this, a push button switch's contacts can bounce, and every switch is different, and in fact every push of a given switch is different.

Bounces take fractions of a mSec to mSec's to occur, while the uC is typically running millions of instructions during that time frame.

If you use a Pin Change interrupt you will typically get many interrupts each time the switch is pressed or released, and the code to sort it out can actually be a bit messy.

 

An alternative approach is to have a Timer/Counter run in CTC mode and fire off an interrupt once every 1, or 2, or 5, or 10 mSec, (or whatever you decide is appropriate).

This is a master clock for timing operations within the system.

(This isn't a real time operating system, it is just a nice time signal)

 

Say the interrupt fires every 2 mSec.

When it fires you read the state of the push buttons, (high or low), and compare it to the prior state.

I typically have a counter for each switch, and count up if the switch is pressed, count down if it isn't pressed.

(End points, don't decrement below 0, and hold at a max count of whatever, (say 10))

 

If I count up to 10 then the switch was pressed and it has been stable in the pressed state.

(Bounces / glitches cause it to count down)

 

One can debounce as many switches as desired, very quickly, with no delay routines.

One then sets a flag when the switch is debounced as pressed, and the Main Loop can watch for the flag and act accordingly.

 

An added bonus is that one can also see how long the switch was held in the pressed state.

If you have an keypad with Up and Down counters for example, you can see if the User pressed and held the up switch, and then count up fast, by 10's instead of by 1's, for example.

This doesn't require any real change in the debounce process, one just has to see for how long the switch was pressed by the counter.

 

There are many variations on debouncing switches, as it is a common task in the embedded world.

There are many Threads on various techniques.

 

Very few of them, however, use the Pin Change interrupt.

 

Please spend some time looking at the other Threads, and playing with various approaches.

 

Having your students learn how to configure a Timer/Counter for a fixed, known, periodic interrupt, (CTC mode), is an important skill.

Using it to flash an LED is a good start.

Using it to debounce a couple of push button switches is a good learning experience, and useful for real projects.

Using it for triggering the ADC for periodic sampling is crucial for many data collecting and processing projects.

 

JC

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
You need to understand the difference between Cortex M and Cortex A too. The fact is that "A" is an application processor so won't have the facilities as an eMbedded processor.

 

It's STM32F103ZET6, but I'm not interested to work with it right now, it would take a lot of work and need me to leave AVR and PIC aside and focus more on developing functions and libraries for it, not to mention the issues I have to get like I have with PIC and AVR chip :)

 

 

theusch wrote:

 

To describe the "general" ARM interrupt controller takes 200 pages:  https://www.cl.cam.ac.uk/researc...

 

Yes that looks extensive study and specialized for the ARM, but it should be like other architectures on the market.

 

I thought you just told us that

wolfrose wrote:
Now I don't use Google a lot, I want to find the results as fast as I can, ...
   So you found that scholarly paper from over a decade ago from assiduously going through your extensive library?

I didn't mean that I don't use or like to use Google! I use it but not as excessive as before, for example in the bachelor study I used to do a lot of searching and downloading a lot of files to improve my homework and my presentations, but it's more of discovering to me than just taking the useful information and present it. So, now with the experience I learned to search less often and get the thing I want more faster without looking into more and more links that would extra search time. I know more investigating means more knowledge but I have a lot of things now to do not like in bachelor life where I'm not married and having a baby and have a work to do and files to prepare .. etc :)

 

 

Anyway, what does that bit of information that may or may not be applicable to the single ARM-based module that you have not used have to do with ADC on an AVR8 not working in polled mode?

That's the point, I should learn how to operate a simple ADC module in polling mode than talking about ARM :)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

DocJC wrote:

Wolrose, if reply #51 you mentioned Pin Change interrupts.

 

Are you now using that for your push button interface?

 

There are many ways to accomplish a given goal, but I think that is not a very good approach to "de-bouncing" push button switches.

 

The experts here can chime in if they wish...

 

The point is this, a push button switch's contacts can bounce, and every switch is different, and in fact every push of a given switch is different.

Bounces take fractions of a mSec to mSec's to occur, while the uC is typically running millions of instructions during that time frame.

If you use a Pin Change interrupt you will typically get many interrupts each time the switch is pressed or released, and the code to sort it out can actually be a bit messy.

 

An alternative approach is to have a Timer/Counter run in CTC mode and fire off an interrupt once every 1, or 2, or 5, or 10 mSec, (or whatever you decide is appropriate).

This is a master clock for timing operations within the system.

(This isn't a real time operating system, it is just a nice time signal)

 

Say the interrupt fires every 2 mSec.

When it fires you read the state of the push buttons, (high or low), and compare it to the prior state.

I typically have a counter for each switch, and count up if the switch is pressed, count down if it isn't pressed.

(End points, don't decrement below 0, and hold at a max count of whatever, (say 10))

 

If I count up to 10 then the switch was pressed and it has been stable in the pressed state.

(Bounces / glitches cause it to count down)

 

One can debounce as many switches as desired, very quickly, with no delay routines.

One then sets a flag when the switch is debounced as pressed, and the Main Loop can watch for the flag and act accordingly.

 

An added bonus is that one can also see how long the switch was held in the pressed state.

If you have an keypad with Up and Down counters for example, you can see if the User pressed and held the up switch, and then count up fast, by 10's instead of by 1's, for example.

This doesn't require any real change in the debounce process, one just has to see for how long the switch was pressed by the counter.

 

There are many variations on debouncing switches, as it is a common task in the embedded world.

There are many Threads on various techniques.

 

Very few of them, however, use the Pin Change interrupt.

 

Please spend some time looking at the other Threads, and playing with various approaches.

 

Having your students learn how to configure a Timer/Counter for a fixed, known, periodic interrupt, (CTC mode), is an important skill.

Using it to flash an LED is a good start.

Using it to debounce a couple of push button switches is a good learning experience, and useful for real projects.

Using it for triggering the ADC for periodic sampling is crucial for many data collecting and processing projects.

 

JC

 

 

 

Thank you so much for this answer, I actually now started to test an IR module with the joystick, the IR triggers PCINT while the ADC working on polling, after some time of launching the program it freezes!

 

Now that's a problem I have to look for. I think it has to do with the debouncing issue, but this is an IR module so there's not switch debounce! What could be the problem? That's what I'm trying to find now.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, I want to know how to work properly with interrupts.

 

How important that I have to use sei(), cli() and reti()?

 

For example, I looked into this I2C library which doesn't have an application code:

void i2c_init(void) {
  uint8_t sreg;

  /* Store the status register and disable interrupts. */
  sreg = SREG;
  cli();

 

So the programmer used only cli() at the beginning of the init function and didn't use the other commands, even the reti(), isn't in the ISR block as I learned this command should be before exiting the ISR, but it isn't there.

 

I understand that the programmer used cli() to prevent any interrupts to happen during the operation as the application code may have interrupt enabling instructions beside the I2C library. So the programmer doesn't have to enable the sei() command, as the programmer should enable the interrupts when it's necessary in the application code.

 

But what's not clear is that there's not reti(), I don't know is this command not necessary for ISRs?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Is this the right thing to do? Which is to clear interrupts during the initialization of functions inits, and enable interrupts before going into the while loop?

int  main(){
  cli();
  I2C_init();
  LCD_Init();
  joystick_init();
  adc_init();
  sei();
  while(1){
    joystick_read();
  }
}

I compiled this sketch and noticed more improved performance of reading 2 ADC channels and one PCINT. No hangs, fast response.

Last Edited: Mon. Sep 17, 2018 - 07:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
OK, I want to know how to work properly with interrupts. How important that I have to use sei(), cli() and reti()?

I could point you to a good Tutorial forum article.  And mention the datasheet.  And mention the documentation for your chosen toolchain.

 

But we learned recently that you wouldn't read it all the way through anyway.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

wolfrose wrote:
OK, I want to know how to work properly with interrupts. How important that I have to use sei(), cli() and reti()?

I could point you to a good Tutorial forum article.  And mention the datasheet.  And mention the documentation for your chosen toolchain.

 

But we learned recently that you wouldn't read it all the way through anyway.

 

No it's not that I don't like to read, I enjoy reading but not much, for example, there're a lot of article, topics, documentations, videos on YouTube ... etc. If I'm looking for one issue and only started to search on Google, then that's a lot of search results, even reading one article takes a lot of time reading someone's opinion about something just for interest in the topic, where I should save my time and look for what I wanted to look for so it's like diving into the web, sometimes I ended up reading wikipedia about chemistry scientists and how chemistry and physics just changed that world :)

 

So yeah tell me about your resources.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wolfrose wrote:
So yeah tell me about your resources.

I was not referring to a general e.g. Google search.  I was referring to your response to a link to a well-respected Tutorials article that directly addressed your questions:

wolfrose wrote:
I'm sorry didn't read your whole tutorial because it was long,

And then you went on to ask related questions about material that was in that very article.

 

I am a bit pussled at your questions in the first place, as they are addressed in the article you quoted about ARM not having an interrupt controller.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

wolfrose wrote:
So yeah tell me about your resources.

I was not referring to a general e.g. Google search.  I was referring to your response to a link to a well-respected Tutorials article that directly addressed your questions:

 

wolfrose wrote:
I'm sorry didn't read your whole tutorial because it was long,

And then you went on to ask related questions about material that was in that very article.

That doesn't mean that I'm skipping that article entirely I said it's long but I also said I need time to get to that level.

 

I am a bit pussled at your questions in the first place, as they are addressed in the article you quoted about ARM not having an interrupt controller.

I wrote that at the time I was reading that document but the lines after said it has an external one integrated into the chip.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You seem to be wallowing in a pool of confusion.
1. Carry and zero are equally easy to test for the average micro.
2. Timing each task - it is useless to update the LCD any faster than 10 times per second as a poor human can read it much faster than that and the average character lcd has a spec that the dots can’t change much faster than that. Similarly with relays.
3. 8 tasks was an arbitrary number. I don’t understand your reference to an sfr.
4. Soft/hard timer - the code in question demonstrates this concept! We have one hardware timer that generates a 10ms timebase then the task-timers that implement a number of timers in software.

5. Priority is fixed by the way the code works. Testing the task-bits in a specific order creates the priority. The first bit that is set wins.

You’ve clearly not taken the time to read the tutorials nor have you stepped through the code using a pencil and paper. The answers are in front of your eyes - you just need to look for them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
You seem to be wallowing in a pool of confusion.
 

LOL I laughed for this analogy, yes I'm soo wallowing :)

 

1. Carry and zero are equally easy to test for the average micro.

Yes from the SREG, but anyway I can either test the task timer to be zero or the zero bit in the SREG, right? I mean both ways are applicable.

 

2. Timing each task - it is useless to update the LCD any faster than 10 times per second as a poor human can read it much faster than that and the average character lcd has a spec that the dots can’t change much faster than that. Similarly with relays.

That's what I want to do in my sketch code, I want to get rid of _delay_ms(), and solve the interrupts problems.

 

What should I do now? Should I:

1. Develop a timer overflow interrupt and use that to update the code?

2. Develop "soft timer" but I need more explanation of how to do that. I may go back to your tutorial, because you know I can't understand it from one go, I have to revise it more than one time, it depends how it's difficult to me. Somethings I can understand just with one go, other things need to revise it over and over. 

 

 

3. 8 tasks was an arbitrary number. I don’t understand your reference to an sfr.

My reference is that you assign each task with the bit location, just like testing each bit in 8-bit register, so I have 8 locations for each bit means I have up to 8 possible tasks in 8-bit register.

 

 

4. Soft/hard timer - the code in question demonstrates this concept! We have one hardware timer that generates a 10ms timebase then the task-timers that implement a number of timers in software.

OK, just I'm trying to understand this, is what you mean:

1. Set the hardware timer to run and overflow in 10ms, and enable the interrupt for that timer.

2. Set soft timers for each task, and run the hard timer, and each overflow it decrements 1 from the soft timer, until the soft timer reaches 0. And it starts again scanning for new reinitialize of the soft timers.

 

How about my description, Am I close?

 

 

5. Priority is fixed by the way the code works. Testing the task-bits in a specific order creates the priority. The first bit that is set wins. You’ve clearly not taken the time to read the tutorials nor have you stepped through the code using a pencil and paper. The answers are in front of your eyes - you just need to look for them.

OK, I have to look more, as I said, looking at it the first time, maybe I can't get it in one go, just like other things I learned in microcontrollers, sometimes I need couple days to learn something.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

My comments outline the rules of how it works - just follow the rules. Don’t try and re-interpret them incorrectly. Think of the task timer likes ‘sands through the hourglass....”when all the sand gets to the bottom, then it’s time to run the task.

Determining which task to run is like 8 bottles in a row. Find the first one that is full. The first bottle has priority over the last bottle by nature of the order. Can you suggest a downside to this method? And how would you solve it?

Last Edited: Tue. Sep 18, 2018 - 12:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
My comments outline the rules of how it works - just follow the rules. Don’t try and re-interpret them incorrectly. Think of the task timer likes ‘sands through the hourglass....”when all the sand gets to the bottom, then it’s time to run the task. Determining which task to run is like 8 bottles in a row. Find the first one that is full. The first bottle has priority over the last bottle by nature of the order. Can you suggest a downside to this method? And how would you solve it?

 

I want to ask you why you declared this variable with these specifiers?

static const PROGMEM char bit_mask[]={1,2,4,8,16,32,64,128};            /* value -> bit mask xlate table */

I know "static" is because you want it to be focused in this scope and not called from other code.

"const" is you don't want it to be changed

What I didn't understand is "PROGMEM" I know this is used for long set of data that's mostly constant and not need to be changing all the time so they put this data in the flash memory. But this one doesn't seem to be large.

 

char task_bits = 0;  /* lsb is hi priority task */
volatile char tick_flag = 0;    /* if non-zero, a tick has elapsed */
unsigned int task_timers[NUM_TASKS]={0,0,0,0,0,0,0,0};                  /* init the timers to 0 on startup */

These are OK:

1. the "task_bits" is used in the main function so it's an ordinary variable

2. "volatile" because it get into the ISR

3. "task_timers" it's called in the functions and its value doesn't hold and not need to be const, so you initialize them all the time inside the functions

 

I don't have a question about these 3 variables, I just want to know if my points of interpretation is correct?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

‘Long data that’s mostly constant’? Who told you that?? I would suggest that the use of PROGMEM is for data that won’t change. I had a choice - either put the array in ram and the initial values in flash OR just put the array in flash. I put it just in flash and saved 8 bytes of ram. Obviously, for larger data, the savings are greater and more important as in it may not fit in the available ram.
You might argue that saving 8 bytes of ram isn’t much, but the average AVR isn’t blessed with lots of it, so why waste it unnecessarily?
Note that in later versions of avr-gcc that they have the __flash qualifier to bring it inline with the likes of the IAR and Imagecraft compilers. I’d suggest you read upon that feature.

1. The variable would be described as global. There are three basics type - global, local and static.
2. Volatile because it is shared. The isr counts as another thread of execution.
3. again, this array is global. Technically, i don’t need to initialise it to 0 as part of the C startup process the ram is cleared. Note - this only works for globals. Locals are stored on the stack and can have a random initial value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
‘Long data that’s mostly constant’? Who told you that?? I would suggest that the use of PROGMEM is for data that won’t change. I had a choice - either put the array in ram and the initial values in flash OR just put the array in flash.

Thank you so much for support.

 

I had to be sure that data put in flash is always constant. I absolutely agree with saving space in RAM.

EEPROM is also an alternative choice suitable for this case, right?

 

I also have one question about this, is the speed of reading the data from the flash or the RAM the same? I would say yes, and even the EEPROM. Is my assumption correct? I have no robust theoretical background for this assumption.

 

Note that in later versions of avr-gcc that they have the qualifier to bring it inline with the likes of the IAR and Imagecraft compilers. I’d suggest you read upon that feature.

So "__flash" would be the same as "PROGMEM"? And putting it inline means more execution speed, that it's in the program stack or overall execution overhead.

 

 

1. The variable would be described as global. There are three basics type - global, local and static.

2. Volatile because it is shared. The isr counts as another thread of execution.

OK, got it.

 

3. again, this array is global. Technically, i don’t need to initialise it to 0 as part of the C startup process the ram is cleared. Note - this only works for globals. Locals are stored on the stack and can have a random initial value.

Thanks, arrays in global are initialized to 0, locals in main or any function needs to initialized. got it thanks for this information.

 

 

==================================================================================================

 

I'm trying to implement your "co-operative task switcher" strategy in my library, but I have one problem for now.

 

The "timer_tick" doesn't accept calling it in the main.

 

I'm putting my code and ISRs in libraries, so calling a function works, but calling "timer_tick" variable isn't working apparently.

 

I get this message:

main_program:13: error: 'TMR0_flag' was not declared in this scope

     if(TMR0_flag)

        ^

exit status 1
'TMR0_flag' was not declared in this scope

Last Edited: Sun. Sep 23, 2018 - 06:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

EEPROM is also an alternative choice suitable for this case, right? Not really. Why would you want to put it in eeprom as it does not change?

 

I also have one question about this, is the speed of reading the data from the flash or the RAM the same? I would say yes, and even the EEPROM. Is my assumption correct? I have no robust theoretical background for this assumption.

There is a slight overhead accessing flash vs ram as you need an extra LPM instruction. I dare say there is only a couple of clocks difference between the various memories. The only way to be sure is to look at what the compiler generates.

 

So "__flash" would be the same as "PROGMEM"? And putting it inline means more execution speed, that it's in the program stack or overall execution overhead.

 

 

You clearly haven't done any reading have you? Nothing to do with execution speed - it is just a different way of telling the compiler if stuff is in flash. 'program stack or overall execution overhead' - now you're just spouting bullshit aren't you?

 

 

 

I'm trying to implement your "co-operative task switcher" strategy in my library, but I have one problem for now.

 

The "timer_tick" doesn't accept calling it in the main.

 

I'm putting my code and ISRs in libraries, so calling a function works, but calling "timer_tick" variable isn't working apparently.

 

I get this message:

main_program:13: error: 'TMR0_flag' was not declared in this scope

     if(TMR0_flag)

        ^

exit status 1
'TMR0_flag' was not declared in this scope

 

What do you mean by a 'library'?

 

You've given us no context in which to address your question - just a random excerpt of code. Give us the whole picture and we can make a determination from factual evidence rather than guess. Since when do you 'call' a variable? javascript yes, C no.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

 

You clearly haven't done any reading have you? Nothing to do with execution speed - it is just a different way of telling the compiler if stuff is in flash. 'program stack or overall execution overhead' - now you're just spouting bullshit aren't you?

No I didn't sorry, I was pulling words out of memory, because I actually haven't gone through deep understanding for memories part where it's stack or program overhead, also stack vs heap, things related to memory management or how memory works, it's just I don't have a clear understanding currently.

 

But anyway, for now "__flash" or "PROGMEM", aren't so important to me, because I haven't got the need to save RAM space, but I should start applying this rule, to have more optimized code in the future.

 

What do you mean by a 'library'?

I put my functions and ISRs into libraries, and call the functions in the main.c file.

 

 

You've given us no context in which to address your question - just a random excerpt of code. Give us the whole picture and we can make a determination from factual evidence rather than guess. Since when do you 'call' a variable? javascript yes, C no.

Sorry I thought you're following my issues from the posts.

 

Here's my problem:

In the main.c:

#include "interrupts.h"

int  main(){
  cli();
  TMR0_COMP_init();
  INTERRUPTS_init();
  sei();
  while(1){
  if(TMR0_flag)
    {
        TMR0_flag = 0;
        task_dispatch();
    }
  }
}

In interrupts.c:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <stdio.h>
#include "interrupts.h"

uint8_t task_timer[2];
volatile uint8_t TMR0_flag;
uint8_t bits_mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uint8_t task_bits;		// LSB high priority

void task_dispatch(void)
{
	uint8_t task;
	// first of all we scan all the tasks and set the priorities
	task=0;
	while(task<task_num)
	{
		if (task_timer[task])
		{
			task_timer[task]--;
			if (task_timer[task]==0)
			{
				task_set(task);
			}
		}
		task++;
	}

	task=0;
	while(task<=task_num)
	{
		if(task_bits&(bits_mask[task]))
		{
			break;			// task found goto switch functions
		}
		task++;
	}
	switch(task)
	{
		case 0:
			blink_PB4();
			break;
		case 1:
			blink_PB5();
			break;
		default:
			break;
	}
}

void task_set(uint8_t task)
{
	task_bits|=bits_mask[task];
}
void task_reset(uint8_t task)
{
	task_bits&=(~bits_mask[task]);
}

void blink_PB4(void)
{
  PORTB ^= (1<<PB4);
  task_timer[0]=20;
  task_reset(0);
}
void blink_PB5(void)
{
  PORTB ^= (1<<PB5);
  task_timer[1]=40;
  task_reset(1);
}

void TMR0_COMP_init(void){
	DDRB|=0x20;							// PB5 output
	OCR0A=77;							// for 10ms along with the 1024 prescaler & 16MHz
	TCCR0A|=(1<<WGM01);					// Normal port operation, OC0A disconnected // WGM01: CTC MODE
	TCCR0B|=0x05;						// 1024 prescaler
	TIMSK0|=(1<<OCIE0A);				// TMR0 O/P COMPA Int Enable
}

void INTERRUPTS_init(void)
{
	DDRC=0xE0;								// PC3,PC4 inputs
	DDRB=0x38;								// PB3,PB4 outputs
	PCICR|=(1<<PCIE1);PCMSK1=(1<<PCINT11)|(1<<PCINT12);
}

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

ISR(TIMER0_COMPA_vect)
{
 	TMR0_flag = 1;
}

 

 

===========================================================================================

 

The problem in the main.c, "TMR0_flag" isn't declared there.

Last Edited: Sun. Sep 23, 2018 - 08:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I can't see any 'libraries'. Where's interrupts.h? Show us the FULL picture. 

 

You've not shown us where task_num is defined.

 

Why would you put 

 if(TMR0_flag)
    {
        TMR0_flag = 0;
        task_dispatch();    
    }

 

in main()?

 

why not a function in interrupts.c that you call from main()?

or in interrupts.h put:

 

extern volatile uint8_t TMR0_flag;

 

so that other files can see it.

 

Using extern is probably the worst choice, so use that. You've also done a number of seemingly random things - we wouldn't want to break habits?

 

Why is using extern the worst choice? I'll leave that as an exercise for the reader to investigate. Hint - something to do with variable scope and visibility.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

I can't see any 'libraries'. Where's interrupts.h? Show us the FULL picture. 

 

You've not shown us where task_num is defined.

#ifndef INTERRUPTS_H_
#define INTERRUPTS_H_
#include <inttypes.h>

#define task_num 2

void task_dispatch(void);
void task_set(uint8_t task);
void task_reset(uint8_t task);
void blink_PB4(void);
void blink_PB5(void);
void TMR0_COMP_init(void);
void INTERRUPTS_init(void);

#endif

 

 

Why would you put 

 if(TMR0_flag)
    {
        TMR0_flag = 0;
        task_dispatch();    
    }

 

in main()?

I was copying your code, as you put everything in one page as a demonstration, but I thought to take the part in main and thought it's like I copy it to my main :)

 

So that's different I guess.

 

 

why not a function in interrupts.c that you call from main()?

Absolutely, that what I needed to do.

 

 

or in interrupts.h put:

 

extern volatile uint8_t TMR0_flag;

Tried that but got this error:

Arduino: 1.8.0 (Windows 10), Board: "Arduino/Genuino Uno"

C:\Users\R1S8k\AppData\Local\Temp\cc99fxsE.ltrans0.ltrans.o: In function `__vector_14':

F:\Program Files\Arduino\libraries\sensors_modules/interrupts.cpp:150: undefined reference to `TMR0_flag'

C:\Users\R1S8k\AppData\Local\Temp\cc99fxsE.ltrans0.ltrans.o: In function `main':

F:\microcontroller dev\AVR\AVR C\projects\main_program/main_program.ino:17: undefined reference to `TMR0_flag'

F:\microcontroller dev\AVR\AVR C\projects\main_program/main_program.ino:19: undefined reference to `TMR0_flag'

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino/Genuino Uno.

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

 

 

You've also done a number of seemingly random things - we wouldn't want to break habits?

Like what :) I'm happy because you considered things I do in programming as habits and not totally wrong. This is like you respected my habits :) Thanks,

 

 

===============================================================================================================================

 

 

But anyway, I did it! The code works now I'm so happy this is the beginning to RTOS, I'm really happy I moved a step into project programming experience. This is my code:

// .h file

#ifndef INTERRUPTS_H_
#define INTERRUPTS_H_
#include <inttypes.h>

#define task_num 2

void task_dispatch(void);
void task_scan(void);
void task_set(uint8_t task);
void task_reset(uint8_t task);
void blink_PB4(void);
void blink_PB5(void);
void TMR0_COMP_init(void);

#endif

// .c file

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <inttypes.h>
#include <stdio.h>
#include "interrupts.h"

uint8_t task_timer[2];
volatile uint8_t TMR0_flag;
uint8_t bits_mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uint8_t task_bits;		// LSB high priority

void TMR0_COMP_init(void){
	DDRB|=0x38;							// PB5,PB4 outputs
	OCR0A=77;							// for 10ms along with the 1024 prescaler & 16MHz
	TCCR0A|=(1<<WGM01);					// Normal port operation, OC0A disconnected // WGM01: CTC MODE
	TCCR0B|=0x05;						// 1024 prescaler	
	TIMSK0|=(1<<OCIE0A);				// TMR0 O/P COMPA Int Enable
}

void task_scan(void)
{
	if(TMR0_flag)
    {
        TMR0_flag = 0;
        task_dispatch();    
    }	
}
void task_dispatch(void)
{
	uint8_t task;
	task=0;
	while(task<task_num)
	{
		if (task_timer[task])
		{
			task_timer[task]--;
			if (task_timer[task]==0)
			{
				task_set(task);
			}
		}
		task++;
	}

	task=0;
	while(task<task_num)
	{
		if(task_bits&(bits_mask[task]))
		{
			break;			// task found goto switch functions
		}
		task++;
	}
	
	switch(task)
	{
		case 0:
			blink_PB4();
			break;
		case 1:
			blink_PB5();
			break;
		default:
			break;	
	}
}
void task_set(uint8_t task)
{
	task_bits|=bits_mask[task];
}
void task_reset(uint8_t task)
{
	task_bits&=(~bits_mask[task]);
}

void blink_PB4(void)
{
  PORTB ^= (1<<PB4);
  task_timer[0]=40;
  task_reset(0);
}
void blink_PB5(void)
{
  PORTB ^= (1<<PB5);
  task_timer[1]=40;
  task_reset(1);
}


/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

ISR(TIMER0_COMPA_vect)
{
 	TMR0_flag = 1;
}


// program code

#include "interrupts.h"

int  main(){
  cli();
  TMR0_COMP_init();
  sei();
  task_set(0);
  task_set(1);
  while(1){
    task_scan();  
  }
}

 

Thanks again Mr Kartman, you just helped me a lot in this and in other issues.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I want to get more tasks than what 8-bit register offers, how to do that?

 

I thought of declare an array, and if the number of tasks exceed 8 tasks, then I call the 2nd array element and store the new tasks, how about that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

wolfrose wrote:
If I want to get more tasks than what 8-bit register offers, how to do that?

uint8_t bits_mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uint8_t task_bits;		// LSB high priority

Well the next step up from uint8_t is uint16_t, after that some compilers have uint24_t but it's a fair bet they all have uint32_t and even beyond that there is uint64_t for some compilers. If you find yourself needing more than 64 tasks you have probably out-grown an AVR!

 

As a start

uint16_t bits_mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000};
uint16_t task_bits;		// LSB high priority

then everything that was previously using the 8 bit bitfield would need to update:

void task_set(uint16_t task)
{
	task_bits|=bits_mask[task];
}
void task_reset(uint16_t task)
{
	task_bits&=(~bits_mask[task]);
}

I suppose that you could do this with an array instead but while it's just 8/16/32/64 bits I think that keeping all the task states in a single variable is probably the easier option.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Well the next step up from uint8_t is uint16_t, after that some compilers have uint24_t but it's a fair bet they all have uint32_t and even beyond that there is uint64_t for some compilers.

Yes, I thought about that, yes some compilers support 64-bit.

If you find yourself needing more than 64 tasks you have probably out-grown an AVR!

No I'm not :)

 

As a start

uint16_t bits_mask[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x100,0x200,0x400,0x800,0x1000,0x2000,0x4000,0x8000};
uint16_t task_bits;		// LSB high priority

then everything that was previously using the 8 bit bitfield would need to update:

void task_set(uint16_t task)
{
	task_bits|=bits_mask[task];
}
void task_reset(uint16_t task)
{
	task_bits&=(~bits_mask[task]);
}

OK, thanks, that how it should be done, it's a nice strategy, I'm now testing the code to run 5 tasks. It just stopped working so I have to debug the code as usual :)

 

I suppose that you could do this with an array instead but while it's just 8/16/32/64 bits I think that keeping all the task states in a single variable is probably the easier option.

Yeah absolutely. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm running 5 tasks :)

 

That's really nice, it's a good level for projects development.

 

Guys you saved me a lot of issues and confusions in the future for my projects course this semester.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You really don’t want to go beyond 8 tasks with this. It’s a bit like having 10 people to do the work of two - you’ve increased your management overhead and people are bumping into each other to get the work done.
You’ve got 10ms to do some work, so make sure the time is used well as having a zillion tasks each just flashing an led is rather wasteful.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Kartman wrote:
You really don’t want to go beyond 8 tasks with this. It’s a bit like having 10 people to do the work of two - you’ve increased your management overhead and people are bumping into each other to get the work done.

The Mythical Task Month

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I wasn't thinking of that when I wrote the allegory - my intention was  to illustrate that having a number of tasks do trivial work is not the best solution vs the concept of double the workforce and get twice the work done - which rarely rings true!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, what if I have modules which need delays or input polling for its operation, what's the best strategy for that?

 Like:

 

1. Input polling:

	PORTB &= ~(1<<PB0);									// pull PB0 LOW	for 18ms
	_delay_ms(18);
	PORTB |= (1<<PB0);									// pull it HIGH again
	DDRB = 0xFE;

	while((PINB & (1<<PINB0)));							// leading HIGH pulse waiting for response
	while(!(PINB & (1<<PINB0)));						// 1st response 80us LOW
	while((PINB & (1<<PINB0)));							// 2nd response 80us HIGH

The timer scale is 10ms, so to deal with input polling for 80us?

 

Is it the time to use hard interrupts?

 

 

2. Required delay:

A/ DH11 initialization, starting with 2s delay after VCC powering up:

void DH11_init(void)
{
  _delay_ms(2000);										// 2s delay for DH11 to response
  TCCR0B = 0x02;										// enable timer0 clkT2S/8 and divide reading /2
}

B/ After each reading I have to issue another 2s delay because DH11 needs 2s every time to get ready to respond:

In this example it's at the end of the function, the line before the last one.

void DH11_read(void)
{
	uint8_t DH11_arr[5],parity_check;

	DDRB = 0xFF;										// Pin0 output
	PORTB &= ~(1<<PB0);									// pull PB0 LOW	for 18ms
	_delay_ms(18);
	PORTB |= (1<<PB0);									// pull it HIGH again
	DDRB = 0xFE;

	while((PINB & (1<<PINB0)));							// leading HIGH pulse waiting for response
	while(!(PINB & (1<<PINB0)));						// 1st response 80us LOW
	while((PINB & (1<<PINB0)));							// 2nd response 80us HIGH

	DH11_read_40bits(DH11_arr);
    parity_check = DH11_arr[0]+DH11_arr[1]+DH11_arr[2]+DH11_arr[3];
    if (parity_check != DH11_arr[4])
    {
      LCD_string("parity error");
      _delay_ms(500);
      clr_dis();
      return 1;
    }
    LCD_string("Humi: ");
    move_cursor(1,6);
    sprintf(val_arr,"%.2d",DH11_arr[0]);
    LCD_string(val_arr);
    move_cursor(1,8);
    sprintf(val_arr,".%.2d",DH11_arr[1]);
    LCD_string(val_arr);
    LCD_string("%");
    move_cursor(2,1);
    LCD_string("Temp: ");
    move_cursor(2,6);
    sprintf(val_arr,"%.2d",DH11_arr[2]);
    LCD_string(val_arr);
    move_cursor(2,8);
    sprintf(val_arr,".%.2d",DH11_arr[3]);
    LCD_string(val_arr);
    move_cursor(2,11);sendData(0xDF);
    move_cursor(2,12);sendData(0x43);
    move_cursor(2,13);
    _delay_ms(2000);
    clr_dis();
}

 

 

I'm reading now about periodic polling, but I think I may do another timers for delays and polling, if the delay expires then it should activate something.

The polling also is similar but different, each time I decrement, I check the pin.

Last Edited: Tue. Sep 25, 2018 - 08:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Didn’t i cover this very situation in my tutorial?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let me check again, I didn't read the whole tutorial, just the first part.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

wolfrose wrote:
I didn't read the whole tutorial, just the first part
I was just sitting here when I thought I heard a collective "sigh" from internet connected colleagues around the globe!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

 I was just sitting here when I thought I heard a collective "sigh" from i