Pages: [1]   Go Down
Author Topic: Changing pin output with port registers  (Read 3664 times)
0 Members and 1 Guest are viewing this topic.
The Netherlands
Offline Offline
Newbie
*
Karma: 0
Posts: 43
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all,

I think this is a simple question, I just can't figure it out myself. I'm using a bit of code that Stimmer ported to Arduino before (see here http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1265488118). As it is, it simply outputs the chiptune music on digital pin 3. I would like to change the output to pin 5. I'm not sure how to change the code, although I presume it is somewhere in the "main"-function, where the port registers are declared (DDRD = oxff, etc.). I looked at the Arduino page about port registers, but I can't figure out where in the code it says that the output is on port 3 (and so I can't change it to port 5).
I post the end of the code here, because the whole code is too long to upload. However, the whole code can be found here: https://docs.google.com/leaf?id=0B5O7TidPmOdzYWRiODI1ZGYtNjNkYS00NmM4LWE5MjQtMWI2YzhkN2Q0M2Zl&hl=en
Could someone help? Thanks!

Code:
/* BEGINNING OF CODE OMITTED*/

int main() {
asm("cli");
watchdogoff();
CLKPR = 0x80;
CLKPR = 0x80;

DDRC = 0x12;
DDRD = 0xff;

PORTC = 0;

timetoplay = 0;
trackwait = 0;
trackpos = 0;
playsong = 1;
songpos = 0;

osc[0].volume = 0;
channel[0].inum = 0;
osc[1].volume = 0;
channel[1].inum = 0;
osc[2].volume = 0;
channel[2].inum = 0;
osc[3].volume = 0;
channel[3].inum = 0;

initresources();

TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz

        TCCR2A=0b10100011;
        TCCR2B=0b00000001;

TIMSK0 = 0x02;

asm("sei");
for(;;) {
  while(!timetoplay);

  timetoplay--;
  playroutine();
}
}


ISR(TIMER0_COMPA_vect)  // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;

OCR2B = lastsample;

newbit = 0;
if(noiseseed & 0x80000000L) newbit ^= 1;
if(noiseseed & 0x01000000L) newbit ^= 1;
if(noiseseed & 0x00000040L) newbit ^= 1;
if(noiseseed & 0x00000200L) newbit ^= 1;
noiseseed = (noiseseed << 1) | newbit;

if(callbackwait) {
  callbackwait--;
} else {
  timetoplay++;
  callbackwait = 180 - 1;
}

acc = 0;
for(i = 0; i < 4; i++) {
  s8 value; // [-32,31]

  switch(osc[i].waveform) {
   case WF_TRI:
    if(osc[i].phase < 0x8000) {
     value = -32 + (osc[i].phase >> 9);
    } else {
     value = 31 - ((osc[i].phase - 0x8000) >> 9);
    }
    break;
   case WF_SAW:
    value = -32 + (osc[i].phase >> 10);
    break;
   case WF_PUL:
    value = (osc[i].phase > osc[i].duty)? -32 : 31;
    break;
   case WF_NOI:
    value = (noiseseed & 63) - 32;
    break;
   default:
    value = 0;
    break;
  }
  osc[i].phase += osc[i].freq;

  acc += value * osc[i].volume; // rhs = [-8160,7905]
}
// acc [-32640,31620]
lastsample = 128 + (acc >> 8); // [1,251]
}
/* Time for some Cake! */
}
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 30
Posts: 2600
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's not in the main(). Maybe in playroutine()?
Logged

The Netherlands
Offline Offline
Newbie
*
Karma: 0
Posts: 43
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the whole code, minus the songdata:

Code:
/*
Chiptune Music!
Open Source Code

Connect Speaker or Amp to Digital Pin3 and GND
Sound is made with PWM
Enjoy!

Happy Electric Circuits!
http://happyelectriccircuits.com/
*/

#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>

#define TRACKLEN 32
#define MAXTRACK 0x92
#define SONGLEN  0x37

#include <avr/pgmspace.h>

extern "C" {
 
typedef unsigned char u8;
typedef unsigned short u16;
typedef char s8;
typedef short s16;
typedef unsigned long u32;

byte songdata[] PROGMEM = {
 /* SONGDATA*/
};

volatile u8 callbackwait;
volatile u8 lastsample;

volatile u8 timetoplay;

volatile u8 test;
volatile u8 testwait;

u8 trackwait;
u8 trackpos;
u8 playsong;
u8 songpos;

u32 noiseseed = 1;

u8 light[2];

/* Freq Table */
const u16 freqtable[] = {
/* FREQUENCIES*/
};

/* Simply replace the code above to code below to change tone,

const u16 freqtable[] = {
/* FREQUENCIES*/
};


*/


const s8 sinetable[] = {
/* SINES*/
};

const u8 validcmds[] = "0dfijlmtvw~+=";

enum {
WF_TRI,
WF_SAW,
WF_PUL,
WF_NOI
};

volatile struct oscillator {
u16 freq;
u16 phase;
u16 duty;
u8 waveform;
u8 volume; // 0-255
} osc[4];

struct trackline {
u8 note;
u8 instr;
u8 cmd[2];
u8 param[2];
};

struct track {
struct trackline line[TRACKLEN];
};

struct unpacker {
u16 nextbyte;
u8 buffer;
u8 bits;
};

struct channel {
struct unpacker  trackup;
u8   tnum;
s8   transp;
u8   tnote;
u8   lastinstr;
u8   inum;
u16   iptr;
u8   iwait;
u8   inote;
s8   bendd;
s16   bend;
s8   volumed;
s16   dutyd;
u8   vdepth;
u8   vrate;
u8   vpos;
s16   inertia;
u16   slur;
} channel[4];

u16 resources[16 + MAXTRACK];

struct unpacker songup;

byte readsongbyte(u16 offset)
{
 
   return pgm_read_byte_near(&songdata[0] + offset);
}

void watchdogoff()
{
 
}

void initup(struct unpacker *up, u16 offset) {
up->nextbyte = offset;
up->bits = 0;
}

u8 readbit(struct unpacker *up) {
u8 val;

if(!up->bits) {
  up->buffer = readsongbyte(up->nextbyte++);
  up->bits = 8;
}

up->bits--;
val = up->buffer & 1;
up->buffer >>= 1;

return val;
}

u16 readchunk(struct unpacker *up, u8 n) {
u16 val = 0;
u8 i;

for(i = 0; i < n; i++) {
  if(readbit(up)) {
   val |= (1 << i);
  }
}

return val;
}

void readinstr(byte num, byte pos, byte *dest) {
dest[0] = readsongbyte(resources[num] + 2 * pos + 0);
dest[1] = readsongbyte(resources[num] + 2 * pos + 1);
}

void runcmd(u8 ch, u8 cmd, u8 param) {
switch(validcmds[cmd]) {
  case '0':
   channel[ch].inum = 0;
   break;
  case 'd':
   osc[ch].duty = param << 8;
   break;
  case 'f':
   channel[ch].volumed = param;
   break;
  case 'i':
   channel[ch].inertia = param << 1;
   break;
  case 'j':
   channel[ch].iptr = param;
   break;
  case 'l':
   channel[ch].bendd = param;
   break;
  case 'm':
   channel[ch].dutyd = param << 6;
   break;
  case 't':
   channel[ch].iwait = param;
   break;
  case 'v':
   osc[ch].volume = param;
   break;
  case 'w':
   osc[ch].waveform = param;
   break;
  case '+':
   channel[ch].inote = param + channel[ch].tnote - 12 * 4;
   break;
  case '=':
   channel[ch].inote = param;
   break;
  case '~':
   if(channel[ch].vdepth != (param >> 4)) {
    channel[ch].vpos = 0;
   }
   channel[ch].vdepth = param >> 4;
   channel[ch].vrate = param & 15;
   break;
}
}

void playroutine() {   // called at 50 Hz
u8 ch;
u8 lights;

if(playsong) {
  if(trackwait) {
   trackwait--;
  } else {
   trackwait = 4;

   if(!trackpos) {
    if(playsong) {
     if(songpos >= SONGLEN) {
      playsong = 0;
     } else {
      for(ch = 0; ch < 4; ch++) {
       u8 gottransp;
       u8 transp;

       gottransp = readchunk(&songup, 1);
       channel[ch].tnum = readchunk(&songup, 6);
       if(gottransp) {
        transp = readchunk(&songup, 4);
        if(transp & 0x8) transp |= 0xf0;
       } else {
        transp = 0;
       }
       channel[ch].transp = (s8) transp;
       if(channel[ch].tnum) {
        initup(&channel[ch].trackup, resources[16 + channel[ch].tnum - 1]);
       }
      }
      songpos++;
     }
    }
   }

   if(playsong) {
    for(ch = 0; ch < 4; ch++) {
     if(channel[ch].tnum) {
      u8 note, instr, cmd, param;
      u8 fields;

      fields = readchunk(&channel[ch].trackup, 3);
      note = 0;
      instr = 0;
      cmd = 0;
      param = 0;
      if(fields & 1) note = readchunk(&channel[ch].trackup, 7);
      if(fields & 2) instr = readchunk(&channel[ch].trackup, 4);
      if(fields & 4) {
       cmd = readchunk(&channel[ch].trackup, 4);
       param = readchunk(&channel[ch].trackup, 8);
      }
      if(note) {
       channel[ch].tnote = note + channel[ch].transp;
       if(!instr) instr = channel[ch].lastinstr;
      }
      if(instr) {
       if(instr == 2) light[1] = 5;
       if(instr == 1) {
        light[0] = 5;
        if(channel[ch].tnum == 4) {
         light[0] = light[1] = 3;
        }
       }
       if(instr == 7) {
        light[0] = light[1] = 30;
       }
       channel[ch].lastinstr = instr;
       channel[ch].inum = instr;
       channel[ch].iptr = 0;
       channel[ch].iwait = 0;
       channel[ch].bend = 0;
       channel[ch].bendd = 0;
       channel[ch].volumed = 0;
       channel[ch].dutyd = 0;
       channel[ch].vdepth = 0;
      }
      if(cmd) runcmd(ch, cmd, param);
     }
    }

    trackpos++;
    trackpos &= 31;
   }
  }
}

for(ch = 0; ch < 4; ch++) {
  s16 vol;
  u16 duty;
  u16 slur;

  while(channel[ch].inum && !channel[ch].iwait) {
   u8 il[2];

   readinstr(channel[ch].inum, channel[ch].iptr, il);
   channel[ch].iptr++;

   runcmd(ch, il[0], il[1]);
  }
  if(channel[ch].iwait) channel[ch].iwait--;

  if(channel[ch].inertia) {
   s16 diff;

   slur = channel[ch].slur;
   diff = freqtable[channel[ch].inote] - slur;
   //diff >>= channel[ch].inertia;
   if(diff > 0) {
    if(diff > channel[ch].inertia) diff = channel[ch].inertia;
   } else if(diff < 0) {
    if(diff < -channel[ch].inertia) diff = -channel[ch].inertia;
   }
   slur += diff;
   channel[ch].slur = slur;
  } else {
   slur = freqtable[channel[ch].inote];
  }
  osc[ch].freq =
   slur +
   channel[ch].bend +
   ((channel[ch].vdepth * sinetable[channel[ch].vpos & 63]) >> 2);
  channel[ch].bend += channel[ch].bendd;
  vol = osc[ch].volume + channel[ch].volumed;
  if(vol < 0) vol = 0;
  if(vol > 255) vol = 255;
  osc[ch].volume = vol;

  duty = osc[ch].duty + channel[ch].dutyd;
  if(duty > 0xe000) duty = 0x2000;
  if(duty < 0x2000) duty = 0xe000;
  osc[ch].duty = duty;

  channel[ch].vpos += channel[ch].vrate;
}

lights = 0;
if(light[0]) {
  light[0]--;
  lights |= 0x02;
}
if(light[1]) {
  light[1]--;
  lights |= 0x10;
}
PORTC = lights;
}

void initresources() {
u8 i;
struct unpacker up;

initup(&up, 0);
for(i = 0; i < 16 + MAXTRACK; i++) {
  resources[i] = readchunk(&up, 13);
}

initup(&songup, resources[0]);
}

int main() {
asm("cli");
watchdogoff();
CLKPR = 0x80;
CLKPR = 0x80;

DDRC = 0x12;
DDRD = 0xff;

PORTC = 0;

timetoplay = 0;
trackwait = 0;
trackpos = 0;
playsong = 1;
songpos = 0;

osc[0].volume = 0;
channel[0].inum = 0;
osc[1].volume = 0;
channel[1].inum = 0;
osc[2].volume = 0;
channel[2].inum = 0;
osc[3].volume = 0;
channel[3].inum = 0;

initresources();

TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz

        TCCR2A=0b10100011;
        TCCR2B=0b00000001;

TIMSK0 = 0x02;

asm("sei");
for(;;) {
  while(!timetoplay);

  timetoplay--;
  playroutine();
}
}


ISR(TIMER0_COMPA_vect)  // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;

OCR2B = lastsample;

newbit = 0;
if(noiseseed & 0x80000000L) newbit ^= 1;
if(noiseseed & 0x01000000L) newbit ^= 1;
if(noiseseed & 0x00000040L) newbit ^= 1;
if(noiseseed & 0x00000200L) newbit ^= 1;
noiseseed = (noiseseed << 1) | newbit;

if(callbackwait) {
  callbackwait--;
} else {
  timetoplay++;
  callbackwait = 180 - 1;
}

acc = 0;
for(i = 0; i < 4; i++) {
  s8 value; // [-32,31]

  switch(osc[i].waveform) {
   case WF_TRI:
    if(osc[i].phase < 0x8000) {
     value = -32 + (osc[i].phase >> 9);
    } else {
     value = 31 - ((osc[i].phase - 0x8000) >> 9);
    }
    break;
   case WF_SAW:
    value = -32 + (osc[i].phase >> 10);
    break;
   case WF_PUL:
    value = (osc[i].phase > osc[i].duty)? -32 : 31;
    break;
   case WF_NOI:
    value = (noiseseed & 63) - 32;
    break;
   default:
    value = 0;
    break;
  }
  osc[i].phase += osc[i].freq;

  acc += value * osc[i].volume; // rhs = [-8160,7905]
}
// acc [-32640,31620]
lastsample = 128 + (acc >> 8); // [1,251]
}
/* Time for some Cake! */
}
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 30
Posts: 2600
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Oh, I 've been looking for direct port manipulation, and couldn't find it first time, simple because he's using direct PWM port manipulation. Pin3 is internally associated to PWM-B of TIMER2, here is relevant part of the code:
Code:
TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz

        TCCR2A=0b10100011;
        TCCR2B=0b00000001;

TIMSK0 = 0x02;

asm("sei");
for(;;) {
while(!timetoplay);

timetoplay--;
playroutine();
}
}


ISR(TIMER0_COMPA_vect) // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;

OCR2B = lastsample;

  Pin - 5 and 6 are belongs to TIMER0, which is busy doing time clocking via OCM  - output compare match. You can't use them. You could, probably transfer it to pin 11 or pin 9 - 10 if TIMER1 isn't in use in your code
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 30
Posts: 2600
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Other way around, would be swap TIMER2 - to do a clock job, and TIMER0 - for PWM
Logged

The Netherlands
Offline Offline
Newbie
*
Karma: 0
Posts: 43
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Would it be a lot of work to change that in the code? Here is a link to the code file. Sorry, this all goes a bit over my head.
If it's too much work I will try it some other way. Thanks!

https://www.dropbox.com/s/c4my8owoa94y4m8/Chiptune.ino
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 30
Posts: 2600
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Would it be a lot of work to change that in the code?
Which way? To play on pin11? Tryy this:
Code:
// OCR2B = lastsample;    <<<---   replace with
OCR2A = lastsample;

Logged

The Netherlands
Offline Offline
Newbie
*
Karma: 0
Posts: 43
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks! And what would have to be changed to play it on pin 5?
Logged

Montreal
Offline Offline
Faraday Member
**
Karma: 30
Posts: 2600
Per aspera ad astra.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Download a data sheet for AtMega328 (atmel.com), full version.  Look into section TIMER0. Using control register description,  de-crypt this settings:
"TCCR0A = 0x02;
TCCR0B = 0x02; // clkIO/8, so 1/8 MHz
OCR0A = 125;//125; // 8 KHz"  In binary TCCR0A = 0b0000 0010 , as you can see bit 2 is set. Find out what it means. Than transfer settings to TIMER2, again using data sheet . It would be something like TCCR2A = 0b0000 0010 - just an example, I'm not sure if they similar in bit settings.
And TCCR2B = xxxxxxxx, and OCR2A = 125. After that TIMER2 would be responsible for 8 kHz clock, and pin 5 get freedom.
What left, is change:
        TCCR0A=0b10100011;  <<<- verify !
        TCCR0B=0b00000001; <<<- verify !

TIMSK2 = 0x02;

 -//-//--//  skip, the same

ISR(TIMER2_COMPA_vect)  // called at 8 KHz
{
u8 i;
s16 acc;
u8 newbit;

OCR0B = lastsample;"
Logged

Pages: [1]   Go Up
Jump to: