Initialiseing Array Values programmaticaly into PROGMEM

Thanks for taking the time to read my post.

My Question/Problem Is:---

I am currently storing all the Cosine and Sine values for 0 to 359 degrees in Arrays.

This is done with a run once routine at boot. Looking up values is much faster than having my program calculate these values on the fly. The processing time penalty is significant and with not much extra code nearly half my Megas ram is used.

I would like to have the generated Arrays stored in PROGMEM. The Arrays are declared constant.

No way am I wanting to sit and populate my Arrays manually( CosX[359]={ , , , , ,etc})..whatever.

Hoping this makes sense. So, can this be done with PROGMEM?

Any help here would be much appreciated.

Thanks.

Have you tried using a for loop?

EDIT - oops, sorry. I understand now. That wasn't helpful at all.

curly1:
I am currently storing all the Cosine and Sine values for 0 to 359 degrees in Arrays.

First thing I think is, why? You only need 1, sine and cosine are just shifted. And you only need 1/4th of it because the rest is just mirrored.

But if you want them in PROGMEM you can't use the Arduino in runtime for that. Use you computer to make the list. I would use Python but you can also just use Excel en a CSV file. That's not to hard to transform into a list of variables.

Write a small program to print the values that need to go in the array declaration to the Serial monitor complete with commas. Run the program, copy the output to the array declaration in the actual program.

Ahh, yeah, you can indeed also misuse the Arduino to do so! :smiley:

https://github.com/Optiboot/optiboot/blob/master/optiboot/examples/test_dospm/test_dospm.ino

but you would need to change the bootloader of Mega to Optiboot 8
I have here a definition of Mega with Optiboot: GitHub - JAndrassy/my_boards: My Arduino boards custom variants. You can use it as starting point for your boards specialities.

but others are right. you can prepare the table and include it in the sketch source.

septillion:
But if you want them in PROGMEM you can't use the Arduino in runtime for that.

not true. the do_spm function of Optiboot 8 mediates the ability of bootloader to write to flash.

Ok Thanks for your answers. Yes I had thought of using a pre-printed list ...not typing it in.

I humbly admit I am mathematically challenged. I had to crash course myself in Trig just to get some idea of using said Trig. septillion....would you mind giving me a pointer on using 1 and mirroring the values for what I assume is meant by 1/4th ie: Quadrants...just changing the signs?..struggling a bit.

Dumb as I am I have still managed to get a compass with counter rotating bezel to line up with a fixed heading pointer working perfectly on a TFT.....just have more( a lot) to learn yet. There surely nearly always is an easier way!...in this case a more precisely mathematical way I imagine.

Thanks heaps folks

curly1:
would you mind giving me a pointer on using 1 and mirroring the values for what I assume is meant by 1/4th

Plot sin(x) and cos(x) on a piece of paper. Note the symmetries around the x-axis and around vertical lines drawn through the peaks and troughs. Note that cos(x) and sin(x) are shifted with respect to each other by 90 degrees on the x-axis.

Juraj:
not true. the do_spm function of Optiboot 8 mediates the ability of bootloader to write to flash.

Yeah, I was assuming stock bootloaders :smiley:

And I would get something like:

#include <avr/pgmspace.h>

const int SinTable[] PROGMEM = {0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, 5690, 6252, 6813, 7371, 7927, 8481, 9032, 9580, 10126, 10668, 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, 16384, 16876, 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, 24351, 24730, 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, 28377, 28659, 28932, 29196, 29451, 29697, 29934, 30162, 30381, 30591, 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165, 32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, 32767};

void setup() {
  Serial.begin(115200);

  Serial.println(F("===Sin==="));
  for(unsigned int i = 0; i <= 360; i++){
    Serial.println(sinLookup(i));
  }

  Serial.println(F("===Cos==="));
  for(unsigned int i = 0; i <= 360; i++){
    Serial.println(cosLookup(i));
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

int sinLookup(unsigned int n){
  if(n <= 90){
    return SinTable[n];
  }
  if(n <= 180){
    return SinTable[90 - (n - 90)];
  }
  if(n <= 270){
    return -SinTable[n - 180];
  }
  if(n <= 360){
    return -SinTable[90 - (n - 270)];
  }
  else{
    return 0;
  }
}

int cosLookup(unsigned int n){
  if(n <= 270){
    return sinLookup(n + 90);
  }
  if(n <= 360){
    return sinLookup(n - 270);
  }
  else{
    return 0;
  }
}

Compiles but untested :smiley:

And I just assumed a full scale 16-bit signed sin/cos. Note, sinLookup() and cosLookup() only work for 0 <= angle <= 360. You could make them work for higher and negative as well but was more work :stuck_out_tongue:

Ok folks...thanks for your input..it is appreciated.
Here is the entire code for my compass which works beautifully with my 240x320 TFT in Landscape mode/Mega2560/HMC5338L compass module. Its far from perfect and please ignore libraries included for other purposes but not included in code

#include <avr/pgmspace.h>
#include <Adafruit_GFX.h>
#include <gfxfont.h>
#include <TouchScreen.h>
#include <MCUFRIEND_kbv.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_HMC5883_U.h>
       
#define LCD_CS A3 
#define LCD_CD A2 
#define LCD_WR A1 
#define LCD_RD A0 
#define LCD_RESET A4 

#define YP A3  
#define XM A2 
#define YM 9 
#define XP 8 
//Touch Screen 
#define TS_MINX 100
#define TS_MAXX 920
#define TS_MINY 75
#define TS_MAXY 900
//Colors
#define BLACK       0x0000      /*   0,   0,   0 */
#define DARKGREY    0x7BEF      /* 128, 128, 128 */
#define BLUE        0x001F      /*   0,   0, 255 */
#define GREEN       0x07E0      /*   0, 255,   0 */
#define RED         0xF800      /* 255,   0,   0 */
#define YELLOW      0xFFE0      /* 255, 255,   0 */
#define WHITE       0xFFFF      /* 255, 255, 255 */
#define NAVY        0x000F      /*   0,   0, 128 */
//TFT and Touch Instance
MCUFRIEND_kbv tft;
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
TSPoint tp;

//Touch Pressure
const int MINPRESSURE=10;
const int MAXPRESSURE=1000;

//*************Compass Initialize & Variables*******************************************************************************************
Adafruit_HMC5883_Unified magsensor = Adafruit_HMC5883_Unified(12345);
const float declinationAngle =-2.016667;
const float Deg2Rad=0.0174532925;

float cosX[360];        //Arrays of cosines and sines
float sinY[360];       // "


char  buf[3];          //formatted display buffer
int   angleInt;        //Angle as Integer for stepping through arrays of cos, sin etc

float lastCosX, lastSinY; //previous line position values for erasing tft heading line(s),
float lastNorthX, lastNorthY;
float lastSouthX, lastSouthY;
float lastEastX,  lastEastY;
float lastWestX,  lastWestY;


//*************************************************************************************************************************

void setup(){
  Serial.begin(9600);
  //
  magsensor.begin();
  //pinMode(trigPin, OUTPUT); 
  //pinMode(echoPin, INPUT); 
  //
  tft.reset();
  tft.begin(0x7783);
  tft.setRotation(1);
  tft.fillScreen(BLACK);
  tft.setTextSize(1);
  tft.setTextColor(GREEN,BLACK);
 
  CoSinCalc(); //Populate Arrays
  compassScreenSetUp(); //draw initial constant screen items
  
}

void compassScreenSetUp()
{
 tft.fillScreen(NAVY); 
 tft.fillCircle(160,120,107,BLACK); 
 tft.drawCircle(160,120,108,YELLOW);
 tft.drawCircle(160,120,119,YELLOW);
 tft.setCursor(tft.width()/2 - (String("Heading Degrees").length()/2)*6,110); //*6 is the pixels count for each character
 tft.setTextSize(1);
 tft.setTextColor(YELLOW,DARKGREY);
 tft.print("Heading Degrees");
 
 tft.fillTriangle(155,50,160,32,165,50,GREEN);
  for(int i=0;i<=360;i+=5)
  {
    tft.drawLine(110*cosX[i]+160,110*sinY[i]+120,118*cosX[i]+160,118*sinY[i]+120, GREEN);
  }
 
}

void getHeading()
{
sensors_event_t event; 
magsensor.getEvent(&event);
float my=event.magnetic.y;
float mx=event.magnetic.x;
float heading=atan2(my, mx);
heading+=declinationAngle;  

//Correct for Declination Angle
if(heading < 0)
   heading += 2*PI;
if(heading > 2*PI)
   heading -= 2*PI;

if(heading>0 && heading<=180)
   heading*=(180/PI);
if(heading<0 && heading<180)
   heading*=(-180/PI);

//Convert Heading from Float to Integer   
angleInt = heading;

//Correction for Landscape TFT display
int North=270-angleInt;
int East=North+90;
int South=North-180;
int West=North+270;

//Check valus In Range
if(North<0)
    North+=359;
if(South<0)
    South+=359;
if(East>359)
    East-=359;
if(West>359)
    West-=359;
  
//Erase only if heading changes 
if(cosX[angleInt]!=lastCosX)
{
tft.setTextColor(BLACK,BLACK);

tft.setCursor(95*lastNorthX+160,95*lastNorthY+120);
  tft.print("N");
tft.setCursor(95*lastSouthX+160,95*lastSouthY+120); 
  tft.print("S"); 
tft.setCursor(95*lastEastX+160,95*lastEastY+120);
  tft.print("E");  
tft.setCursor(95*lastWestX+160,95*lastWestY+120);
  tft.print("W");  
}

//Draw items for current heading

tft.setTextColor(GREEN,BLACK);

tft.setCursor(95*cosX[North]+160,95*sinY[North]+120);
  tft.print("N");
tft.setCursor(95*cosX[South]+160,95*sinY[South]+120);
  tft.print("S");
tft.setCursor(95*cosX[East]+160,95*sinY[East]+120);
  tft.print("E");
tft.setCursor(95*cosX[West]+160,95*sinY[West]+120); 
  tft.print("W"); 

//Heading Text
sprintf(buf,"%3u",angleInt);
  tft.setTextColor(YELLOW,DARKGREY);
  tft.setTextSize(2);
  tft.setCursor(tft.width()/2 - (String(buf).length()/2)*12,120);
  tft.print(buf);
  tft.setTextSize(1);

//Erase Co-Ordinates 
lastCosX=cosX[(angleInt)]; 
lastNorthX=cosX[North]; lastNorthY=sinY[North];
lastSouthX=cosX[South]; lastSouthY=sinY[South];
lastEastX=cosX[East]  ; lastEastY=sinY[East];
lastWestX=cosX[West]  ; lastWestY=sinY[West];
delay(100);
}

void CoSinCalc()                          //Creates Arrays of Cos, Sin for 0-360
{
  for(int i=1;i<=360;i++)
  {
  float n=(i*Deg2Rad);
  cosX[i]=cos(n);
  sinY[i]=sin(n);
  }
} 


 
void loop()
{
getHeading();  

}