[Solved] PROGMEM behaviour?

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

I have been working with some code that uses PROGMEM to save port state, uWebServer by Simon Kueppers, and noticed behaviour that I do not understand.

If I change and re-compile the source and then run it, I notice that the state of the uP ports seem to be retained from the previous source version. So, I tried loading in a completely different program and then re-loading the uWebServer code, lo and behold port state did not revert to the uninitialised state.

Is PROGMEM code placed somewhere where is can only be over-written and not erased, or what? Couldn't find anything in the PROGMEM tutorial or Goggle.

Thanks,
davef

Last Edited: Tue. Apr 14, 2009 - 07:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"state" information would usually be stored in EEPROM not PROGMEM - are you sure it's not the EEPROM that's being used to maintain state?

PROGMEM is just another name for "code flash". While it's possible for an AVR to modify it's own code flash while running it can only do this using bootloader flash writing routines and there are various limitations such as data has to be handled in complete pages (32/64/128 bytes) and anyone page can only be erased 10,000 times during the entire life of the AVR chip.

The usual use of PROGMEM is to build constant data into a program and access it with pgm_read_???() routines to avoid the data being needlessly copied out to SRAM but it's pretty unusual for a program to actually use SPM to rewrite the data at run time.

If you want to learn about PROGMEM for holding const data then this tutorial explains it:

https://www.avrfreaks.net/index.p...

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

Searched the whole program for EEMEM and not a sign. As far as I can work out PROGMEM, in this program, is used to hold GPIO state that can change. Does your last comment still apply? IE, there is something in the tutorial I have missed.

struct tGpio PROGMEM g_Gpio[] =
{
	{	&PINC, &PORTC, &DDRC, PC0},
	{	&PINC, &PORTC, &DDRC, PC1},
	{	&PINC, &PORTC, &DDRC, PC2},
	{	&PINC, &PORTC, &DDRC, PC3},
	{	&PINC, &PORTC, &DDRC, PC4},
	{	&PINC, &PORTC, &DDRC, PC5},
	{	&PIND, &PORTD, &DDRD, PD0},
	{	&PIND, &PORTD, &DDRD, PD1},
	{	&PIND, &PORTD, &DDRD, PD2},
	{	&PIND, &PORTD, &DDRD, PD3},
	{	&PIND, &PORTD, &DDRD, PD4},
	{	&PIND, &PORTD, &DDRD, PD5},
	{	&PIND, &PORTD, &DDRD, PD6},
	{	&PIND, &PORTD, &DDRD, PD7}
};

Thanks,
davef

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

If you want to understand PROGMEM just consider what that would look like if it didn't contain the word "PROGMEM" but instead contained the word "const". The only difference would be that when entries in g_Gpio[] were being accessed they'd just be accessed directly rather than using pgm_read_byte(&g_Gpio[N].???);

As I say the only purpose of PROGMEM (usually) is to hold const data like this - presumably there's nothing in the program that WRITES to g_Gpio[] - all accesses are READ only?

For the data in this array to actually be written would be a VERY complex task.

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

That array doesn't appear to be holding the state of an IO port.

Rather, it appears to be used to provide a means of mapping various application-specific I/O functionality to the specific addresses and bit locations of various pins on the I/O port.

There's nowhere near enough information presented here to know for sure, but presumably it could be used to allow easy relocation of certain I/O functionality to an alternate I/O port, or to an alternate pin on the same port, by modifying just one row in the table (at compile time) rather than having to manually edit the entire program.

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

Boy, am I :oops:

The main problem was I didn't understand refresh button operation. I thought it would send any modified browser state to the web server. Of course you have to hit the (or submit) button to change any PORT or DDR state!

I need to do some more work on understanding how the gpio.c works.

/*
 * Code for controlling GPIOs. This is meant to be an extension
 * to the simple PORTx/PINx/DDRx Interface.
 * You simply can specify a various number of GPIOs that are
 * mapped to randomly specified pins on the avr. 
 * 
 * Author: Simon Kueppers
 * Email: simon.kueppers@web.de
 * Homepage: http://klinkerstein.m-faq.de
 * 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see .
 
 Copyright 2008 Simon Kueppers
 * */

#include "Gpio.h"
#include 
#include 

struct tGpio
{
	volatile uint8_t* pPin;
	volatile uint8_t* pPort;
	volatile uint8_t* pDdr;
	uint8_t nBit;
};

struct tGpio PROGMEM g_Gpio[] =
{
	{  &PINC, &PORTC, &DDRC, PC0},
	{  &PINC, &PORTC, &DDRC, PC1},
	{  &PINC, &PORTC, &DDRC, PC2},
	{  &PINC, &PORTC, &DDRC, PC3},
	{  &PINC, &PORTC, &DDRC, PC4},
	{	&PINC, &PORTC, &DDRC, PC5},
	{	&PIND, &PORTD, &DDRD, PD0},
	{	&PIND, &PORTD, &DDRD, PD1},
	{	&PIND, &PORTD, &DDRD, PD2},
	{	&PIND, &PORTD, &DDRD, PD3},
	{	&PIND, &PORTD, &DDRD, PD4},
	{	&PIND, &PORTD, &DDRD, PD5},
	{	&PIND, &PORTD, &DDRD, PD6},
	{	&PIND, &PORTD, &DDRD, PD7}
};

uint8_t GpioGetPin(uint8_t nGpio)
{
	volatile uint8_t* pPin =
			(volatile uint8_t*) pgm_read_word(&(g_Gpio[nGpio].pPin));
	uint8_t nBit = pgm_read_byte(&(g_Gpio[nGpio].nBit));

	return (*pPin >> nBit) & 1;
}

uint8_t GpioGetPort(uint8_t nGpio)
{
	volatile uint8_t* pPort =
			(volatile uint8_t*) pgm_read_word(&(g_Gpio[nGpio].pPort));
	uint8_t nBit = pgm_read_byte(&(g_Gpio[nGpio].nBit));

	return (*pPort >> nBit) & 1;
}

uint8_t GpioGetDdr(uint8_t nGpio)
{
	volatile uint8_t* pDdr =
			(volatile uint8_t*) pgm_read_word(&(g_Gpio[nGpio].pDdr));
	uint8_t nBit = pgm_read_byte(&(g_Gpio[nGpio].nBit));

	return (*pDdr >> nBit) & 1;
}

void GpioSetPort(	uint8_t nGpio,
					uint8_t nState)
{
	volatile uint8_t* pPort =
			(volatile uint8_t*) pgm_read_word(&(g_Gpio[nGpio].pPort));
	uint8_t nBit = pgm_read_byte(&(g_Gpio[nGpio].nBit));

	if (nState)
		*pPort |= (1<<nBit);
	else
		*pPort &= ~(1<<nBit);
}

void GpioSetDdr(uint8_t nGpio,
				uint8_t nState)
{
	volatile uint8_t* pDdr =
			(volatile uint8_t*) pgm_read_word(&(g_Gpio[nGpio].pDdr));
	uint8_t nBit = pgm_read_byte(&(g_Gpio[nGpio].nBit));

	if (nState)
		*pDdr |= (1<<nBit);
	else
		*pDdr &= ~(1<<nBit);
}

Thanks guys.
davef