atmega328P: stack problem ?

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

First of all, sorry for my english… :(

I have a problem for 6 months that I can not solve.

The project concerns domotics and is a controller of 2-channel for led lights in pwm, rs485 for communication and the possibility to connect a button for direct on/off/dimmer.

The circuit works correctly, the RS485 communication also, the button also.

The problem is that after 1 or 2 days (the time is never the same) atmega328p lose values in the variables and/or in the constants and therefore no longer works anything.

Program don't crash, beacause there is a led blinking every seconds, and it blink forever.
I think the problem is a stack corruption but I can not find where I made some mistakes that damage it.

I've been looking for error in last 6 months but now I have to get to a conclusion.

I add in attachment complete atmel studio solution as it is in multiple files.
I know it will be hard work to analyze my code so if I do not have help it does nothing.

Atmega is ATMEGA328P, running 20Mhz.

If someone needs I can also attach schematic in pdf.

Thank you so much for those who want to help me.

 

Attachment(s): 

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

Hi,

 

I think you should check the BOD, Brown-out-detector, not setting the BOD may cause corruption. try to investigate this problem.

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

Thanks for posting your project.  I have not looked at it yet, but before I do, I would like to ask how you are powering the system?  A poor power supply can cause the issues you are having.  Using the BOD as Moe123 noted can help the system possibly recover depending on how the AVR handles the BOD.  But the first thing I look for when this happens is how my poser rails behave, and how they are built(wire gage, length etc.).  Power dips and spikes can be random and would explain the issues you are having.

 

A schematic of your field wiring, and power supply specs would help.

 

JIm

 

 

 

EDIT:

I started looking around your project.  Quite the busy AVR!  I have done a brief skim over and it looks pretty good.....there are much better sleuths here than I that may find otherwise though.

 

 

Unrelated to the issue you are having, but I saw this in your ADC.C file:

uint16_t readAdc16(int chan) {
	ADMUX = (1<<REFS0)  | (0<<ADLAR) | (chan & 0b00001111);  //select input and ref
	ADCSRA |= (1<<ADSC);                 //start the conversion
	while (ADCSRA & (1<<ADSC));          //wait for end of conversion
	return ADCL | (ADCH<<8);

I believe you can simply change the last line to:

return   ADCW;

Like I said, it has nothing to do with your issue.

 

 

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

Last Edited: Wed. Feb 27, 2019 - 03:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, try removing or bypassing different sections of code to see if the problem goes away---then it may help to diagnose.

Also, if you are using EEPROM, are you disabling interrupts when writing?---you need to

If you get junk characters/garbage or too many characters all at once---how will the software react?

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

Moe123 wrote:

Hi,

 

I think you should check the BOD, Brown-out-detector, not setting the BOD may cause corruption. try to investigate this problem.

 

Thank you for your reply.

BOD is set to 4.3V.

 

jgmdesign wrote:

Thanks for posting your project.  I have not looked at it yet, but before I do, I would like to ask how you are powering the system?  A poor power supply can cause the issues you are having.  Using the BOD as Moe123 noted can help the system possibly recover depending on how the AVR handles the BOD.  But the first thing I look for when this happens is how my poser rails behave, and how they are built(wire gage, length etc.).  Power dips and spikes can be random and would explain the issues you are having.

 

A schematic of your field wiring, and power supply specs would help.

 

JIm

 

Thank for your reply. 

As I wrote above, BOD is set to 4.3V

In attachments schematic in pdf.

 

Attachment(s): 

Last Edited: Wed. Feb 27, 2019 - 03:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

autsel wrote:
In attachments schematic in pdf.

 

Saw that.

 

jgmdesign wrote:
A schematic of your field wiring, and power supply specs would help.

 

I was looking for how you have everything hooked up in the field, and the specs of your power supply for the devices.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

What's the actual power supply like?

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

the_real_seebs wrote:

What's the actual power supply like?

 

power supply is from gel battery, 24v 160Ah

(with solar mantainer)

and linear regulator to limit 24V max because battery full is above 27v

Last Edited: Wed. Feb 27, 2019 - 04:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

autsel wrote:

In attachments schematic in pdf.

 

 

Saw that.

 

 

jgmdesign wrote:

A schematic of your field wiring, and power supply specs would help.

 

 

I was looking for how you have everything hooked up in the field, and the specs of your power supply for the devices.

 

JIm

 

this is the cs

The problem also happens leaving the bus485 disconnected and leaving only the button and vice versa,

LEDs turned off and without touching anything

 

power supply is from gel battery, 24v 160Ah

(with solar mantainer)

and linear regulator to limit 24V max because battery full is above 27v

 

 

 

 

Attachment(s): 

Last Edited: Wed. Feb 27, 2019 - 04:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your running out of ram for the stack, you can move your gamma tables to flash, no need to copy these to ram upon startup.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

autsel wrote:
LEDs turned off and without touching anything

If the 485 is disconnected, and there are no termination resistors THis can happen as the Driver may be picking up interference and sending garbage to the AVR.  In your case you have the termination based on your schematic, but I would make a few changes

 

1) get rid of the 1k resistors in series with your USART data lines.  Replace tehm with 0 ohm jumpers

2) In your PORT configuration, turn on the Pull up resistor for the USART RX pin so it's held in a known condition when the 485 is idle.  The RXO of the driver floats when not active which can cause garbage in your data buffers.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

ki0bk wrote:

If your running out of ram for the stack, you can move your gamma tables to flash, no need to copy these to ram upon startup.

 

Jim

 

RAM is not full

 

Attachment(s): 

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

autsel wrote:

ki0bk wrote:

If your running out of ram for the stack, you can move your gamma tables to flash, no need to copy these to ram upon startup.

 

Jim

 

RAM is not full

 

 

Anyhow, i've tried to use progmem for gamma table, but problem persist

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

jgmdesign wrote:
If the 485 is disconnected, and there are no termination resistors THis can happen as the Driver may be picking up interference and sending garbage to the AVR. In your case you have the termination based on your schematic, but I would make a few changes 1) get rid of the 1k resistors in series with your USART data lines. Replace tehm with 0 ohm jumpers 2) In your PORT configuration, turn on the Pull up resistor for the USART RX pin so it's held in a known condition when the 485 is idle. The RXO of the driver floats when not active which can cause garbage in your data buffers.

 

oh, 1K resistor in series with usart are not 1K, i'v change with 22R (I forgot to update)

USART have termination, and pull-up on atmega is active

I've already solved uart interrupt problem 

https://www.avrfreaks.net/forum/...

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

autsel wrote:
RAM is not full

That only shows the static use of ram, not the dynamic use, i.e. stack space needed.

Again, no need to use ram for static (const) table, just add flash to table, and recompile, see large drop in static ram usage. 

Same as string constants....

 

Nice coding, only a few trivial changes needed, nothing I see that would cause your issue.

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

ki0bk wrote:
That only shows the static use of ram, not the dynamic use, i.e. stack space needed. Again, no need to use ram for static (const) table, just add flash to table, and recompile, see large drop in static ram usage. Same as string constants....

 

 

I guess I did not understand, I never did this thing.
Where should I add exactly flash?

 

Thank you for nice coding :-)

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

change:

const uint16_t gamma_12bit[]  = {

and

const uint8_t gamma_8bit[]  = {

 

to:

const __flash uint16_t gamma_12bit[]  = {

and

const __flash uint8_t gamma_8bit[]  = {

 

 

thats two underscores followed by keyword flash

 

Maybe that will solve your problem, or not, but worth a try.

 

Jim

 

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

ki0bk wrote:

change:

const uint16_t gamma_12bit[]  = {

and

const uint8_t gamma_8bit[]  = {

 

to:

const __flash uint16_t gamma_12bit[]  = {

and

const __flash uint8_t gamma_8bit[]  = {

 

 

thats two underscores followed by keyword flash

 

Maybe that will solve your problem, or not, but worth a try.

 

Jim

 

 

 

This really did not know.

I try and we will have the answer in a few days

Will update

Thank you very much.

 

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

Done.

Now wait.

 

Attachment(s): 

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

ki0bk wrote:
Nice coding,

Agreed, I think professor Johan would have approved.

 

Other Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

jgmdesign wrote:

ki0bk wrote:

Nice coding,

 

Agreed, I think professor Johan would have approved.

 

Other Jim

thanks

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

jgmdesign wrote:
EDIT: I started looking around your project. Quite the busy AVR! I have done a brief skim over and it looks pretty good.....there are much better sleuths here than I that may find otherwise though. Unrelated to the issue you are having, but I saw this in your ADC.C file: uint16_t readAdc16(int chan) { AD

 

I had not seen this EDIT.
I note, but the ADC is not used in this project.
They are files that I have made and I keep for all projects
(in fact it is not included adc.h)

 

Thanks

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

autsel wrote:
I note, but the ADC is not used in this project.

Still, as this is your standard ADC routine, the way you have written it you are at the mercy of the toolchain used as to whether you are reading ADCL and ADCH in the correct order.

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

autsel wrote:
I think the problem is a stack corruption ...
Does a mega328P have data breakpoints?

(couldn't find that in the datasheet at DBG)

If yes then Atmel Studio 7 - Stack Overflow Detection Using Data Breakpoint

autsel wrote:
... but I can not find where I made some mistakes that damage it.
There's a reasonable chance the in-progress source code review will locate the problem's cause though may take awhile.

There's somewhat a chance one or more linters may raise an error, warning, or caution that would lead to the problem's cause.

Greater probability of problem detection by a static analyzer once it's configured completely, precisely, and correctly and one has waded through the false positives and false negatives.; likely quicker to locate the cause possibly via a debugger or logic analyzer.

 

P.S.

autsel wrote:
Program don't crash, beacause there is a led blinking every seconds, and it blink forever.
... without handshaking with main.

Hardware watchdogs handshake; likewise with effective software watchdogs.

 


Atmel Studio 7 - General Information on Data Breakpoint

What is "Static Code Analysis"? | AVR Freaks

edit :

Jack Ganssle's "Reason #4" on why embedded software projects run into trouble | AVR Freaks

edit2 : semicolon

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Wed. Feb 27, 2019 - 08:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> First of all, sorry for my english…

 

me too.

 

I would get rid off the delay inside your UART-RX ISR. During this 5ms "dead" period, your uart-receiver will probably overrun. 

 

nbyte=buffer[1];
...
for(i=0;i<=(uint8_t)(nbyte-1);i++) { byte[i]=buffer[6+i]; error_compute^=byte[i]; }
if(error_compute>250) error_compute-=10;
...
channel=byte[0]; action=byte[1]; percent=byte[2];

And if you receive garbage, your copy from buffer to bytes will shredder your ram. Why copy at all, if you use only buffer[6], buffer[7] and buffer[8]. error_compute is logic stuff, why do arithmetic operations on it?

 

 

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

NetOger wrote:

Quote:

I would get rid off the delay inside your UART-RX ISR. During this 5ms "dead" period, your uart-receiver will probably overrun.

In usart-rx isr there are not delay

 

Quote:

Why copy at all, if you use only buffer[6], buffer[7] and buffer[8].

I don't copy all and I don't use only 6,7,8.

nbyte is the amount of bytes that I must consider in the package and can take several values.

 

Quote:

error_compute is logic stuff, why do arithmetic operations on it?

a value of more than 250 must never appear in the packet as they are reserved for special operations

 

gchapman wrote:

Quote:

Does a mega328P have data breakpoints?

I don't have breakpoints.

About the watchdog I could activate that software and make the appropriate changes where needed

 

Quote:
logic analyzer

there is a lot to read in yout link... I start ….

 

and….

 

ki0bk wrote:

nothing changes ... this morning again issue

 

 

 

 

 

 

Last Edited: Thu. Feb 28, 2019 - 07:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi sorrry for late reply, What I suspect is that you have a high oscillation that cause power interrupt and and destabilization at for the MCU suppluy. I was checking your schematic. Try to do the following:

 

- Change the BOD setting to lower than the value 4.3V....(Any value lower), and Add a cermaic capacitor right after output the LDO (0.1uF) and before C3, place it as close as possible to the LDO.

- add another capacitor but 0805 before C13. Now if you have an oscillator try to check Drop out values, does it stabilize more ?

 

Report what you happened, so we can go further ?

 

Regards,

Moe

 

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

Moe123 wrote:

Hi sorrry for late reply, What I suspect is that you have a high oscillation that cause power interrupt and and destabilization at for the MCU suppluy. I was checking your schematic. Try to do the following:

 

- Change the BOD setting to lower than the value 4.3V....(Any value lower), and Add a cermaic capacitor right after output the LDO (0.1uF) and before C3, place it as close as possible to the LDO.

- add another capacitor but 0805 before C13. Now if you have an oscillator try to check Drop out values, does it stabilize more ?

 

Report what you happened, so we can go further ?

 

Regards,

Moe

 

 

Thank you for your reply.

I remind you that the 24v power supply comes from a gel battery,
and the 5v regulator is a linear regulator.

I can try making your changes, but how can I get a dropout?

 

 

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

 

 

I was not so clear so I quoted a part of the datasheet so it can describe for you what I meant. Now the "Linear" regulator LDO is not really linear as there is always some kind of +/- fulctualation depending on the load...input/output, temperature...etc. if you look at the end of the datsheet there should be some graphs to help you analyse further what is the behaviour of your LDO.

 

The MCU need a stable input supply, when this supply is fluctuating then this would/may cause the MCU to behave differently.

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

Moe123 wrote:

 

 

I was not so clear so I quoted a part of the datasheet so it can describe for you what I meant. Now the "Linear" regulator LDO is not really linear as there is always some kind of +/- fulctualation depending on the load...input/output, temperature...etc. if you look at the end of the datsheet there should be some graphs to help you analyse further what is the behaviour of your LDO.

 

The MCU need a stable input supply, when this supply is fluctuating then this would/may cause the MCU to behave differently.

 

OK, I understand that ... but ... Cin and Cout, 0.1uF or more.
I have Cin = 0.1uF and Cout = 10uF
I would like to understand what's wrong or I have not considered

 

 

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

Do you have osci ?

 

EDIT: you are also using electrolytic capacitors with 10uF at the output...now this aside, the datasheet suggests to use 0.1uF at the output of the regulator.

Last Edited: Thu. Feb 28, 2019 - 09:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

of course yes

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

try to investigate how your load is affecting the supply to ur MCU. check input/output...etc. and according to that set your BOD

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

Moe123 wrote:

try to investigate how your load is affecting the supply to ur MCU. check input/output...etc. and according to that set your BOD

This is my 5V

The issue occurs leaving the circuit in standby, without turning on light and doing nothing

Attachment(s): 

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

Okay, from your last attachment its obvious that this is not the issue.

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

Just to check if it's a RAM problem I would change the speed of the LED if the stackpointer is getting to low (something like RAMSTART+staticRAMuse+"a tad").

 

Perhaps some static variables get's overwritten (not by stack), try to declare statics in an other order, this don't solve anything but is the behavior different we know it's a SW problem.

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

sparrow2 wrote:

Just to check if it's a RAM problem I would change the speed of the LED if the stackpointer is getting to low (something like RAMSTART+staticRAMuse+"a tad").

 

Perhaps some static variables get's overwritten (not by stack), try to declare statics in an other order, this don't solve anything but is the behavior different we know it's a SW problem.

 

I can try this too.

The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom.
Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem.

So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted.

Then I reset the software, I deleted the subroutine for reading the usart buffer and I implemented the interrupt, not with a few modifications.

In the end nothing has changed, if the variables are not corrupted the constants are corrupted and vice versa.

I've done hundreds of projects going from attiny4/10, atmega88, atmega328, atmega32, sam4 and I've never had a similar problem.

Honestly, I have invested a lot of time and money on this project that includes 10 other devices type to put on the bus, but I have reached a point where I no longer know what to try or try.

 

I'm really embittered

 

 

Last Edited: Thu. Feb 28, 2019 - 10:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

autsel wrote:
I don't have breakpoints.
My interpretation of that is you don't have a debugger (not an issue; intention for my question is to seek knowledge about megaAVR DBG or OCD)

autsel wrote:

logic analyzer

 

there is a lot to read in yout link... I start ….

More :

Troubleshooting real-time software issues using a logic analyzer | Embedded

Paulvdh operates that method.

That method is not zero effect (timing, sizing) as a debugger usually is zero effect.

Though for PIC18 :

Instrumented Trace Resource Usage Examples - Developer Help

 

P.S.

Dynamic analysis in addition to static analysis :

Can evaluate stack usage if willing to port the application to macOS, Linux, Android, a BSD, or Solaris.

Static Analysis and Metrics Tools - Hardware and software tools for embedded developers

...

 

Matthew MacClary uses these checkers:

[enable warnings from the compiler]

[a linter]

  • valgrind is excellent see here, it is actually a dynamic checker though. I implement the core of my functionality for embedded systems as regular Linux programs, and then just pass a define variable to the compiler when I want to to pull in the microcontroller specific functionality. This systems lets me use every tool that is available for Linux development.

[Matthew elaborated]

 


Valgrind: Tool Suite - Experimental Tools

...

 

SGCheck

SGCheck is a tool for finding overruns of stack and global arrays. It works by using a heuristic approach derived from an observation about the likely forms of stack and global array accesses.

 

...

Valgrind: Supported Platforms

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Thu. Feb 28, 2019 - 01:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

autsel wrote:
The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom. Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem. So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted. Then I reset the software, I deleted the subroutine for reading the usart buffer and I implemented the interrupt, not with a few modifications. In the end nothing has changed, if the variables are not corrupted the constants are corrupted and vice versa. I've done hundreds of projects going from attiny4/10, atmega88, atmega328, atmega32, sam4 and I've never had a similar problem. Honestly, I have invested a lot of time and money on this project that includes 10 other devices type to put on the bus, but I have reached a point where I no longer know what to try or try.

 

Too many unexplained problems in the above statement.

Can you post a clear picture of your project?  Something seems fundamentally wrong here for a battery powered project.

A picture may help us help you.

 

 

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

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

+100

Paulvdh have a very great method, you can follow his method in this thread:

https://www.avrfreaks.net/commen...

or

simply check his signature, you will find the link there

Comment #32

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

ki0bk wrote:

autsel wrote:

The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom. Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem. So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted. Then I reset the software, I deleted the subroutine for reading the usart buffer and I implemented the interrupt, not with a few modifications. In the end nothing has changed, if the variables are not corrupted the constants are corrupted and vice versa. I've done hundreds of projects going from attiny4/10, atmega88, atmega328, atmega32, sam4 and I've never had a similar problem. Honestly, I have invested a lot of time and money on this project that includes 10 other devices type to put on the bus, but I have reached a point where I no longer know what to try or try.

 

 

Too many unexplained problems in the above statement.

Can you post a clear picture of your project?  Something seems fundamentally wrong here for a battery powered project.

A picture may help us help you.

 

 

Jim

 

 

but OP has posted code, schematics, pictures with osci.

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

There have been threads in the past about EEPROM corruption in the 328 family. I had such an issue even with BOD. Usually the first couple of bytes of EEPROM take the hit. The solution was to start storing your information after the first 5 bytes in EEPROM. I started at 0x0A and the unit has been running for years without issue.

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

autsel wrote:
I'm really embittered
That will block that which is inherent in you and will be the most effective aid (your intuition)

As one acquires data, transforms the data into information (data with meaning), then transforms information into knowledge (relationships amongst information) then one may, or likely will, have a "hunch".

May you create joy!

 

hunch - Wiktionary (noun, 3rd definition)

 

"Dare to be naïve." - Buckminster Fuller

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

jgmdesign wrote:

There have been threads in the past about EEPROM corruption in the 328 family. I had such an issue even with BOD. Usually the first couple of bytes of EEPROM take the hit. The solution was to start storing your information after the first 5 bytes in EEPROM. I started at 0x0A and the unit has been running for years without issue.

Jim

 

I take note.
In fact, I always lost the first bytes of the eeprom

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

jgmdesign wrote:

There have been threads in the past about EEPROM corruption in the 328 family. I had such an issue even with BOD. Usually the first couple of bytes of EEPROM take the hit. The solution was to start storing your information after the first 5 bytes in EEPROM. I started at 0x0A and the unit has been running for years without issue.

Jim

 

Quite interesting actually...

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

eheh

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

gchapman wrote:

autsel wrote:
I don't have breakpoints.
My interpretation of that is you don't have a debugger (not an issue; intention for my question is to seek knowledge about megaAVR DBG or OCD)

autsel wrote:

logic analyzer

 

there is a lot to read in yout link... I start ….

More :

Troubleshooting real-time software issues using a logic analyzer | Embedded

Paulvdh operates that method.

That method is not zero effect (timing, sizing) as a debugger usually is zero effect.

Though for PIC18 :

Instrumented Trace Resource Usage Examples - Developer Help

 

P.S.

Dynamic analysis in addition to static analysis :

Can evaluate stack usage if willing to port the application to macOS, Linux, Android, a BSD, or Solaris.

Static Analysis and Metrics Tools - Hardware and software tools for embedded developers

...

 

Matthew MacClary uses these checkers:

[enable warnings from the compiler]

[a linter]

  • valgrind is excellent see here, it is actually a dynamic checker though. I implement the core of my functionality for embedded systems as regular Linux programs, and then just pass a define variable to the compiler when I want to to pull in the microcontroller specific functionality. This systems lets me use every tool that is available for Linux development.

[Matthew elaborated]

 


Valgrind: Tool Suite - Experimental Tools

...

 

SGCheck

SGCheck is a tool for finding overruns of stack and global arrays. It works by using a heuristic approach derived from an observation about the likely forms of stack and global array accesses.

 

...

Valgrind: Supported Platforms

 

 

Your intuition is correct. 
I have a debugger only for sam family.

I have to investigate the logic analyzer and try "Something"

 

 

 

 

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

ki0bk wrote:

autsel wrote:
The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom. Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem. So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted. Then I reset the software, I deleted the subroutine for reading the usart buffer and I implemented the interrupt, not with a few modifications. In the end nothing has changed, if the variables are not corrupted the constants are corrupted and vice versa. I've done hundreds of projects going from attiny4/10, atmega88, atmega328, atmega32, sam4 and I've never had a similar problem. Honestly, I have invested a lot of time and money on this project that includes 10 other devices type to put on the bus, but I have reached a point where I no longer know what to try or try.

 

Too many unexplained problems in the above statement.

Can you post a clear picture of your project?  Something seems fundamentally wrong here for a battery powered project.

A picture may help us help you.

 

 

Jim

 

 

I have posted schematic, complete solution and pcb.
I Have posted dropout ~30mV from osci

Tell me exactly what you want see, and I post that.

 

 

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

autsel wrote:
I have a debugger only for sam family.
fyi

VisualGDB - Version History

...

14 Jan 2019 v5.4

...

Added support for tracing ARM targets with Segger J-Trace

...

Added support for static stack analysis for ARM targets

...

Analyzing the Stack Usage of your Firmware with VisualGDB – VisualGDB Tutorials

 

PIC32 :

Instruction Trace / Profiling - Developer Help

PIC32 has a MPU and PIC32MZ has a MMU; therefore, a stack overflow can be thrown then caught.

 

SEGGER J-Trace is for arm Cortex-A, Cortex-R, and/or Cortex-M; SEGGER J-Link :

Overview of supported CPUs and devices | SEGGER

[PIC32MX, PIC32MZ, PIC32WK]

 

Inadvertent infinite loops become obvious by tracing approximate to a signal (a watchdog event is typical), can be flagged by a static analyzer, and are sometimes due to an overflow.

A defective pacemaker was diagnosed by one at the USA FDA (pacemaker's source code, static analyzer, inadvertent infinite loop) (oversight is good though can be uneasy for one and others)

Defect -> Fault -> Failure

Failure is inadequate function, Fault is an exception or assertion, Defect is typically in source code (overflow, buffer overrun; IIRC the two most common defects in C) (an 'off by 1' can skip the end of loop therefore inadvertent infinite loop)

The output of some linters will give one pause on overflow and buffer overruns due to type mis-match warnings.

 

edit: italic

 

"Dare to be naïve." - Buckminster Fuller

Last Edited: Thu. Feb 28, 2019 - 03:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
There have been threads in the past about EEPROM corruption in the 328 family. I had such an issue even with BOD. Usually the first couple of bytes of EEPROM take the hit. The solution was to start storing your information after the first 5 bytes in EEPROM. I started at 0x0A and the unit has been running for years without issue. Jim

autsel wrote:
I take note. In fact, I always lost the first bytes of the eeprom

I have experienced an incident of eeprom damage at address 0 with ATmega328P

I avoid eeprom address 0x00 to 0x0f any more. 

Apparently it is related to bad reset during an eeprom write. 

İt is mentioned here:

https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_eeprom_corruption.html

[ADDED] :

Microchip FAQ wrote:
 The failure mechanism for an overwritten byte is generally one of "stuck" bits, i. e. a bit will stay at a one or zero state regardless of the byte written. Also a write followed by a read may return the correct data, but the data will change with the passage of time, due the EEPROM's inability to hold a charge from the excessive write wear.

Similar to your project problem symptoms :

autsel wrote:
The problem is that after 1 or 2 days (the time is never the same) atmega328p lose values in the variables and/or in the constants and therefore no longer works anything.
 

Majid

Last Edited: Fri. Mar 1, 2019 - 02:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

autsel wrote:
The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom.
Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem.

So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted.


I tried to figure out how often your code writes the eeprom, I did not succeed to trace 13 status for each channel.
Only God and you know what your code does! ;)
.
I assume you do aware of limited write/erase cycles of eeproms which is 100,000 cycles for ATmega328P and 1,000,000 for AT24C64
.
Maybe you would also debug to make sure not crossing eeprom write cycle limits.

Majid

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

Tnx for mentioning me :)

I did not read through all 50 posts, just glanced briefly through them.

 

When you look through the code, then any pointers used, are suspicious, especially if shared with an ISR.

Right now I'm not thinking too clear, time for a nap I guess.

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

m.majid wrote:

autsel wrote:

The point is this, initially the project did not have an external eeprom and I used the internal eeprom of the ATMEGA and I was corrupted the data in the eeprom.
Also, about usart I did not use the interrupt in reception but I used a sobroutine with a timeout to simply read the buffer and I had the same problem.

 

So I put my hands on the circuit and put an external eeprom. Obviously the data in the external eeprom remain intact but the variables and constants are corrupted.

I tried to figure out how often your code writes the eeprom, I did not succeed to trace 13 status for each channel.
Only God and you know what your code does! ;)
.
I assume you do aware of limited write/erase cycles of eeproms which is 100,000 cycles for ATmega328P and 1,000,000 for AT24C64
.
Maybe you would also debug to make sure not crossing eeprom write cycle limits.

 

In number of writes on the eeprom is really limited, depends on how many times a user programs the device.
Getting to 1000 scriptures is really very challenging !!!

 

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

Paulvdh wrote:

Tnx for mentioning me :)

I did not read through all 50 posts, just glanced briefly through them.

 

When you look through the code, then any pointers used, are suspicious, especially if shared with an ISR.

Right now I'm not thinking too clear, time for a nap I guess.

 

 

I eagerly await your opinion.

 

 

In the meantime I ordered atmel-ICE which should be a debugger for AVR too via debugWire.

Then, I do not think it will be easy to debugger in assembler a program written in c.

 

I looked and read also about the logic analyzer...
I understood how it worked, but I did not understand how to do it. I tried but without success
I'll read more.

 

 

 

 

 

 

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

Sorry I over looked #37:

autsel wrote:
the data in the external eeprom remain intact

.
I deviated because of #44 :
autsel wrote:
I take note.
In fact, I always lost the first bytes of the eeprom

.
Ok, you had eeprom issue at first with internal eeprom of ATmega328P,
but not any more issue with external eeprom AT24C64
You beleive it is variable corruption and you suspect stack.
.
#1
autsel wrote:
atmega328p lose values in the variables and/or in the constants

#37
autsel wrote:
but the variables and constants are corrupted.

Could you explain more details about how you established variables corrupted?
.
autsel wrote:
Program don't crash, beacause there is a led blinking every seconds, and it blink forever.

Maybe program stucks in main, then LED is blinking by timer.

Majid

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

m.majid wrote:

 

autsel wrote:
but the variables and constants are corrupted.

Could you explain more details about how you established variables corrupted? .

I try to send via 485 some variables every 2hours for test.

At a certain moment variables change value into value which makes no sense.

 

m.majid wrote:

autsel wrote:
Program don't crash, beacause there is a led blinking every seconds, and it blink forever.

Maybe program stucks in main, then LED is blinking by timer.

 

No stuck in main.
The button sometimes does not work anymore, sometimes work, but rs485 forever work correctly (sending  errata data values)
sometimes the pwm works abnormally because of the corrupt gamma table constant (before write in flash)

 

 

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

I had a very short look at the code.

 

Are you 100% sure that none of the indexes are out of range?

 

I would put a check before all writes where indexes are used.

 

Also your RX routine return 0 if there is a timeout, if you receive a 0 it also return a 0! (that is why normal get routines return 16 bit so it can return -1 for an error) 

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

I missed this during the last week (perhaps the thread title wasn't interesting enough).

 

But I get the feeling this an XY problem unfolding here:

 

https://en.wikipedia.org/wiki/XY_problem

 

Even after reading all the posts so  far I still could not tell whether this is a problem of SRAM corruption of EEPROM corruption. Or even the conditions under which the corruption occurs.

 

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

sparrow2 wrote:
I had a very short look at the code. Are you 100% sure that none of the indexes are out of range? I would put a check before all writes where indexes are used. Also your RX routine return 0 if there is a timeout, if you receive a 0 it also return a 0! (that is why normal get routines return 16 bit so it can return -1 for an error)

 

I'm sure 100% that are not out of range indexes. There are only 2, buffer[] and ch[][].

Which routines are you referring to with RX?

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

N.Winterbottom wrote:

I missed this during the last week (perhaps the thread title wasn't interesting enough).

 

But I get the feeling this an XY problem unfolding here:

 

https://en.wikipedia.org/wiki/XY_problem

 

Even after reading all the posts so  far I still could not tell whether this is a problem of SRAM corruption of EEPROM corruption. Or even the conditions under which the corruption occurs.

 

 

It could be.
I solved the eeprom problem by placing an external eeprom.
The real problem (I think) is that although the program works correctly in all its phases, I notice that after a time X some variables are altered making the program no longer working correctly.
I thought of the stack because I do not know what else to think but it could be that my intuition on the stack is completely wrong.

I don't konw.

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

Some notes about the schematic (Although these are probably not your error)

 

There is a red craoss on U1, the S-1142, pin 3. Could that be a missed connection which makes the voltage regulator turn itself off?

RED LED DL3 is connected to +24Vcc, and before the reverse voltage protection diode D1 / GS1M.

R15, R16, R17 is a really weird combo for RS485, just as the 100 Ohm Series resistors R18 & R19.

Are you familiar with "10 ways to bulletproof RS485"?

RS485 is not a 2 wire connection, but a 3 wire connection. GND is not optional, but mandatory for RELIABLE communication.

https://duckduckgo.com/?q="10+ways+to+bulletproof+RS485"&t=opera&ia=web

I see no ferrite cores / inductors / Chokes or other components for EMI filtering.

Your circuit may get upset by external EMI events, especially with long cables connected.

Sometimes upsets by EMI events can be easily triggered by switching a 12Vhalogen lamp (with real transformer) or Fluorescent ligts (with Inductor as ballast) nearby.

 

After that I had a few looks at your code.

How do you want to maintain such code?

I'm not surprised you get lost and cant find bugs.

I don't like:

1). 300+ lines in a while loop.

2). 7 levels of indentation.

3). 188 character long lines with complicated, but very similar if conditions.

4). No decent use of braces.

5). Magic numbers everywhere.

6). Multiple statements on a single line. I even saw something like:  if (a) { b = c; for( d;e;f) { ....;  ...; ...; }}   // ???

7). In a previous post you said you do not use ADC. Why is there ADC code in your program then?

8). How long does this for loop take to execute?

ISR (USART_RX_vect) {
	if(flag485==0) { buf=0; for(int i=0; i<20;i++) buffer[i]=0; }
    ...

9). I do not see structure in the code. It all looks cobbled together.

  For example: calling a bunch of uart_send() functions in a row, checksum stuff in arbitrary places.

  Instead: Write a communications library with defined RS485 data and buffers.

10). twi.c. Full of magic numbers.

 

I2C is not too difficult to get it almost right, but also notoriously difficult to get perfect.

There are a few infinite loops in your twi / I2C code, such as:

	while ((TWCR & (1<<TWINT)) == 0);

If "something" happens and the condition of the loop is never met, your code hangs.

 

 

11). Header files included from header files.

12). Functions in all captials (usually reserved for #defined macro's): TX485_ONN()

13). Non uniform naming. Also in rs485.c: reset_buffer() init_uart(), USART_putstring(). 

14). Delays in ISR: 

 ISR (USART_TX_vect) {
     mdelay(TX485_OFF_DELAY);

15). 2 different files with void main(void){} ???

16). ...

 

 

Motto:

It is easy to write code that has no obvious errors, but it is very hard to write code that obviously has no errors.

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

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

Paulvdh wrote:

Some notes about the schematic (Although these are probably not your error)

 

There is a red craoss on U1, the S-1142, pin 3. Could that be a missed connection which makes the voltage regulator turn itself off?

RED LED DL3 is connected to +24Vcc, and before the reverse voltage protection diode D1 / GS1M.

R15, R16, R17 is a really weird combo for RS485, just as the 100 Ohm Series resistors R18 & R19.

Are you familiar with "10 ways to bulletproof RS485"?

RS485 is not a 2 wire connection, but a 3 wire connection. GND is not optional, but mandatory for RELIABLE communication.

https://duckduckgo.com/?q="10+ways+to+bulletproof+RS485"&t=opera&ia=web

I see no ferrite cores / inductors / Chokes or other components for EMI filtering.

Your circuit may get upset by external EMI events, especially with long cables connected.

Sometimes upsets by EMI events can be easily triggered by switching a 12Vhalogen lamp (with real transformer) or Fluorescent ligts (with Inductor as ballast) nearby.

 

After that I had a few looks at your code.

How do you want to maintain such code?

I'm not surprised you get lost and cant find bugs.

I don't like:

1). 300+ lines in a while loop.

2). 7 levels of indentation.

3). 188 character long lines with complicated, but very similar if conditions.

4). No decent use of braces.

5). Magic numbers everywhere.

6). Multiple statements on a single line. I even saw something like:  if (a) { b = c; for( d;e;f) { ....;  ...; ...; }}   // ???

7). In a previous post you said you do not use ADC. Why is there ADC code in your program then?

8). How long does this for loop take to execute?

ISR (USART_RX_vect) {
	if(flag485==0) { buf=0; for(int i=0; i<20;i++) buffer[i]=0; }
    ...

9). I do not see structure in the code. It all looks cobbled together.

  For example: calling a bunch of uart_send() functions in a row, checksum stuff in arbitrary places.

  Instead: Write a communications library with defined RS485 data and buffers.

10). twi.c. Full of magic numbers.

 

I2C is not too difficult to get it almost right, but also notoriously difficult to get perfect.

There are a few infinite loops in your twi / I2C code, such as:

	while ((TWCR & (1<<TWINT)) == 0);

If "something" happens and the condition of the loop is never met, your code hangs.

 

 

11). Header files included from header files.

12). Functions in all captials (usually reserved for #defined macro's): TX485_ONN()

13). Non uniform naming. Also in rs485.c: reset_buffer() init_uart(), USART_putstring(). 

14). Delays in ISR: 

 ISR (USART_TX_vect) {
     mdelay(TX485_OFF_DELAY);

15). 2 different files with void main(void){} ???

16). ...

 

 

Motto:

It is easy to write code that has no obvious errors, but it is very hard to write code that obviously has no errors.

 

 

 

OMG
Maybe it's better to rewrite it all over again

 

 

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

it's this :

unsigned char USART_receive(void){
    uint16_t kk=1000;
    //while(!(UCSR0A & (1<<RXC0)));
    //return UDR0;
    do{
        if (UCSR0A & (1<<RXC0)) return UDR0;
    } while(--kk);
    return 0;

}

But with the new information I will say it's probably some ISR's that block for each other.

I will normally just have one relatively fast timer ISR and then pool the rest!

 

  

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

sparrow2 wrote:

it's this :

unsigned char USART_receive(void){
    uint16_t kk=1000;
    //while(!(UCSR0A & (1<<RXC0)));
    //return UDR0;
    do{
        if (UCSR0A & (1<<RXC0)) return UDR0;
    } while(--kk);
    return 0;

}

But with the new information I will say it's probably some ISR's that block for each other.

I will normally just have one relatively fast timer ISR and then pool the rest!

 

  

The zip file include (but not used in the solution) the file "main_prima_version_bacco.c" The first version of my program and will be deleted.

Here there is second void main(void) referred by Paulvdh.

The subroutine that references is in that file before I wrote the ISR code.

 

In any case, after Paulvdh's beating, I think it's better to cancel everything and start again.

:(

 

I'm sorry to have lost so much time on so many people

 

 

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

autsel wrote:
I'm sorry to have lost so much time on so many people
If it was a learning experience, then the time was not lost.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

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

Then the risk of no reduction in severe issue (solve one issue, create another issue)

When the software is functional except for one issue then simply, though maybe not easily, solve the issue.

Some or a lot of effort to solve the issue, or, one may be fortunate (by lint)

Compilers and linters are like peas and carrots.

Some good linters are zero price and low cost; excellent linters are low price, low cost, and an excellent value.

 

"Dare to be naïve." - Buckminster Fuller