You're asking a very broad question; there are a wide variety of PICs, and I would probably not use "formal" memory management for any of them. There is no need.
A/D conversion results, filtering, UART buffers, etc. would normally be stored in plain old arrays in memory. You may augment them with head and tail pointers for ring buffers or index variables or other means, but your typical data manipulation techniques are no different for a PIC than you would have on a PC once you've got the address for the buffer. With small embedded microcontrollers you simply declare the array and use it.
Alternatively, you can work with the linker to artificially lower the amount of available memory that the system will see and then use the table read/write instructions and/or standard pointer accesses to hit this "hidden" memory. You reduce the memory the compiler can use so that the stack (which typically grows from the top of memory down) does not collide with your storage area, or BSS and the heap (which are usually located at the start of data memory) don't interfere with your memory area. The exact means to achieve this linker magic depends on the compiler suite of course.
If you could provide some more details, perhaps more specific application as well I could provide more specific answers. I think you are perhaps over-analyzing the issue. C is C; you can declare arbitrary memory and use it on a PC as well, so long as you don't run afoul of the MMU.
edit to add example ADC reading and averaging code:
I tend to write my code simply and clearly; I use small single-purpose functions and tend to write code as state machines. I also tend to leave optimization to the compiler unless I have a specific need to hand-optimize. This has come from years (almost two decades) of embedded design and learning things the hard way.
The code sets up a 1ms timer interrupt, two ADC channels and then samples them continuously; the readings are filtered using a simple sliding average filter. In this example, the data samples from each ADC channel are not stored individually. Each channel has its own running sum; the last 8 values (the FILTER_POINTS constant) are averaged and this filtered count value is stored. Care must be taken to make sure that the sum variable is big enough to store the number that can be created if 8 ADC samples are full-scale.
Finally, a temperature in 1/10 of a degree F, using a conversion function. The conversion function of course is specific to the sensors that I'm sampling. Your own conversion function(s) would have to be written to suit your sensors.
This is written for use with the CCS compiler; I do NOT like this compiler for a variety of reasons, but it's what most of my clients use because it's price is the lowest among its competitors.
#device PIC24F32KA304 ADC=12
#device ANSI
#include <24f32ka304.h>
#fuses OSCIO, NOPROTECT, NOWDT
#use delay(OSC=8MHz, CLOCK=32MHz)
#use standard_io(all)
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
/* I hate mixed case types */
typedef BOOLEAN bool;
/* pin definitions */
#define PIN_FOO (PIN_C8)
#define PIN_BAR (PIN_A4)
#define PIN_BAZ (PIN_A9)
#define VREFP (0)
#define VREFN (1)
#define CHAN1 (4)
#define CHAN2 (5)
#define FILTER_POINTS (8)
/*
* 10mV per degree F.
* 2.048V reference, 4096 counts, for 0.5mV per count
* therefore 10mV/0.5mV or 20 counts per degree F
*/
#define COUNTS_PER_DEGREE (20)
#define COUNTS_PER_DEG_TENTHS (COUNTS_PER_DEGREE / 10)
/* globals */
volatile bool one_ms_flag;
volatile bool onehundred_ms_flag;
volatile bool one_second_flag;
unsigned int32 chan1_sum, chan2_sum; /* summing values for the averaging filter */
unsigned int16 chan1_counts, chan2_counts; /* raw ADC values for temp sensors */
unsigned int16 chan1_temp, chan2_temp; /* temperatures, in 0.1 degrees F steps */
/*
* Timer ISR
*/
#INT_TIMER1 level=7
void system_isr(void)
{
static int ticks = 0;
one_ms_flag = TRUE;
if ((ticks % 100) == 0) {
onehundred_ms_flag = TRUE;
}
if (++ticks > 999) {
one_second_flag = TRUE;
ticks = 0;
}
}
/*
* stupid simple sliding average (FILTER_POINTS data points)
* runsum is the running average (which is basically the sum of the last FILTER_POINTS values)
* function returns the new average
*/
static unsigned int16 filter_analog(unsigned int32 *runsum, unsigned int16 new)
{
*runsum -= *runsum / FILTER_POINTS;
*runsum += new;
return *runsum / FILTER_POINTS;
}
/*
* counts are 0.5mV/count, and sensor is 10mV/oF
* returns in 1/10 degrees (60F = 600)
* rounds to half a degree
*/
int to_degrees_f(int counts)
{
int deg_f;
int tenths;
deg_f = counts / COUNTS_PER_DEG_TENTHS;
tenths = deg_f % 10;
deg_f /= 10;
deg_f *= 10;
if (tenths >= 5) {
deg_f += 5;
}
return deg_f;
}
/*
* ADC loop
* meant to be called every 100ms or so.
* if reset is true, will reset the ADC state machine and reconfigure the ADC.
*/
void adc_loop(bool reset)
{
static enum { ADC_INIT=0, SMPL_CHAN1, SMPL_CHAN2 } adc_state = ADC_INIT;
unsigned int16 adc_value;
if (reset) {
adc_state = ADC_INIT;
}
switch(adc_state) {
case ADC_INIT:
setup_adc(ADC_CLOCK_DIV_128);
setup_adc_ports(sAN0|sAN1, VREF_VREF);
set_adc_channel(CHAN1);
read_adc(ADC_START_ONLY);
chan1_sum = chan2_sum = FILTER_SEED;
chan1_counts = chan2_counts = 0;
chan1_temp = chan2_temp = 0;
adc_state = SMPL_CHAN1;
break;
case SMPL_CHAN1:
adc_value = read_adc(ADC_READ_ONLY);
chan1_counts = filter_analog(&chan1_sum, adc_value);
chan1_temp = to_degrees_f(chan1_counts);
set_adc_channel(CHAN2);
read_adc(ADC_START_ONLY);
adc_state = SMPL_CHAN2;
break;
case SMPL_CHAN2:
adc_value = read_adc(ADC_READ_ONLY);
chan2_counts = filter_analog(&chan2_sum, adc_value);
chan2_temp = to_degrees_f(chan2_counts);
set_adc_channel(CHAN1);
read_adc(ADC_START_ONLY);
adc_state = SMPL_CHAN1;
break;
default:
adc_state = ADC_INIT;
};
}
void main(void)
{
/* Set up timer1 for 1ms interrupts. Tcy = Fosc/2 = 16MHz, /64 is 4us. 250 4us ticks is 1ms. */
setup_timer1(TMR_INTERNAL | TMR_DIV_BY_64, 250);
/* set up I/O */
output_low(PIN_FOO);
output_low(PIN_BAR);
output_float(PIN_BAZ);
/* reset the ADC subsystem */
adc_loop(TRUE);
/* variable setup */
one_second_flag = onehundred_ms_flag = one_ms_flag = FALSE;
enable_interrupts(INT_TIMER1);
/* main loop */
while (1) {
if (one_ms_flag) {
/* do stuff every 1ms */
one_ms_flag = FALSE;
}
if (onehundred_ms_flag) {
adc_loop(FALSE);
onehundred_ms_flag = FALSE;
}
if (one_second_flag) {
/* do stuff every 1s */
one_second_flag = FALSE;
}
}
}
Best Answer
Maybe i'm missing something here (paragraphs would help) but with the C18 compiler local variables within a function are generally allocated on the software stack so i have no idea what the following means:
By moving all your variables to globals within a module you are requiring there is space for all of them at the same time.
What compiler are you using?