Go Down

Topic: Software PWM not working (Read 5475 times) previous topic - next topic

ematson5897

I am trying to make a software pwm sketch for a MCP23017 chip. I have changed the wire library settings so that the i2c speed is now 1.7mhz. I have tried to run the sketch below on my teensy++, but nothing happens and my laptop tells me that the usb device has malfunctioned. I'm pretty sure this means the processor is overloaded but I don't know why. I can set the interrupt as slow as possible and it still does this. Any help is great.


Here's the code:
Code: [Select]
#include <TimerThree.h>

#include <TimerOne.h>

/*
Example 41.1 - Microchip MCP23017 with Arduino
http://tronixstuff.wordpress.com/tutorials > chapter 41
John Boxall | CC by-sa-nc
*/

// pins 15~17 to GND, I2C bus address is 0x20

#include "Wire.h"
int times;
int PWMValue;
void setup()
{
 

 
 
  Wire.begin(); // wake up I2C bus

  Wire.beginTransmission(B00100001);
  Wire.send(0x12);
  Wire.send(0x20);
  Wire.endTransmission();



  Wire.beginTransmission(B00100001);
  Wire.send(0x00); // IODIRA register
  Wire.send(0x00); // set all of bank A to outputs
  Wire.send(0x00); // set all of bank B to outputs
  Wire.endTransmission();
 
 
  cli();//stop interrupts
  //set timer1 interrupt
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set timer count
  OCR1A = 105;// = (16*10^6) / (1000*8) - 1
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS11 bit for 8 prescaler
  TCCR1B |= (1 << CS11); 
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei();//allow interrupts

}


void loop()
{
    for(int i=1; i<=255; i++)
    {
      PWMValue = i;
      delay(50);
    }
}

ISR(TIMER1_COMPA_vect)
{
  if(times == 0 && PWMValue != 0)
  {
    Wire.beginTransmission(B00100001);
    Wire.send(0x13); // GPIOA
    Wire.send(B11111111);    // bank A
    Wire.endTransmission();
  }
 
  if(times>=PWMValue)
  {
    Wire.beginTransmission(B00100001);
    Wire.send(0x13); // GPIOA
    Wire.send(B00000000);    // bank A
    Wire.endTransmission();
  }
 
  times++;
  if(times>255)
  {
    times = 0;
  }
}

AWOL

Quote
i2c speed is now 1.7mhz.

You must be very patient.

ematson5897


CrossRoads

mhz is millihertz.
MHz would be MegaHertz.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

AWOL

1.7mHz is one cycle every nearly ten minutes.

CrossRoads

#5
Jul 20, 2012, 09:12 pm Last Edit: Jul 20, 2012, 09:15 pm by CrossRoads Reason: 1
From the '328 data sheet:

22. 2-wire Serial Interface
22.1 Features
• Simple Yet Powerful and Flexible Communication Interface, only two Bus Lines Needed
• Both Master and Slave Operation Supported
• Device can Operate as Transmitter or Receiver
• 7-bit Address Space Allows up to 128 Different Slave Addresses
• Multi-master Arbitration Support
• Up to 400kHz Data Transfer Speed
• Slew-rate Limited Output Drivers
• Noise Suppression Circuitry Rejects Spikes on Bus Lines
• Fully Programmable Slave Address with General Call Support
• Address Recognition Causes Wake-up When AVR is in Sleep Mode
• Compatible with Philips' I2C protocol


fSCL SCL Clock Frequency fCK (4) > max(16fSCL, 250kHz)(5)  Min:0  MAX:400 kHz

(4). fCK = CPU clock frequency
(5). This requirement applies to all ATmega48A/PA/88A/PA/168A/PA/328/P 2-wire Serial Interface operation. Other devices connected
to the 2-wire Serial Bus need only obey the general fSCL requirement.

How'd you get past the 400 KHz Data Transfer Speed?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

ematson5897

I knew it was something like that. Yea I'm doing pwm with 10 minute cycles.  Haha well I (obviously) meant MHz. And I set it in the library. It works so I assumed it was at 1.7MHz. I guess not

ematson5897

I also have the SPI version of this chip so I might use that

ematson5897

#8
Jul 20, 2012, 11:20 pm Last Edit: Jul 20, 2012, 11:24 pm by ematson5897 Reason: 1
I made a useable version for the SPI version, but I2C just isn't fast enough.

EDIT:
Ok there is no way that I2C is this slow. I can  push the inturrupt interval back to 10 seconds and it still freezes the processor. I have tried both timer 1 and 3

ematson5897

Heres the code:

Quote

#include <Wire.h>

#include <TimerThree.h>


#include <SPI.h>
int i;
boolean bits[8];
int times;
byte byteToTransfer;
int PWMValue[8];
void setup()
{



 Wire.begin(); // wake up I2C bus

  Wire.beginTransmission(B00100001);
  Wire.send(0x12);
  Wire.send(0x20);
  Wire.endTransmission();



  Wire.beginTransmission(B00100001);
  Wire.send(0x00); // IODIRA register
  Wire.send(0x00); // set all of bank A to outputs
  Wire.send(0x00); // set all of bank B to outputs
  Wire.endTransmission();
  
Timer3.initialize(10000000); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer3.attachInterrupt( timerIsr ); // attach the service routine here
 
}


void loop()
{
  for(int j = 0; j < 8; j++)
  {
    for(int g=1; g<=255; g++)
    {
      PWMValue[j] = g;
      delay(10);
    }
    for(int g=255; g>=0; g--)
    {
      PWMValue[j] = g;
      delay(10);
    }
  }
}

void timerIsr()
{
  if(times == 0)
  {
    for (i = 0; i < 8; i++)
    {
      if (PWMValue > 0)  // don't set if checkval == 0
      {
        bits = 1;
      }
    }
    SendSPI();
  }

  
   for (i = 0; i < 8; i++)
  {
   if (PWMValue <= times)  // if we have hit the width
      {
        bits = 0;
      }
    }
    SendSPI();
    
  times++;
  if(times>255)
  {
    times = 0;
  }
}

void SendSPI()
{
  
  
    byteToTransfer = 0;
    for (i = 0; i < 8; i++) {
    // trigger the clock pin and wait.
    if (bits)
      byteToTransfer |= (1 << i);
    }

  Wire.beginTransmission(B00100001);
  Wire.send(0x13); // GPIOA
  Wire.send(byteToTransfer);    // bank A
  Wire.endTransmission();
}


Chagrin

Try this for your ISR:

Globals:
Code: [Select]

#define NUMPINS 8
byte PWMValue[NUMPINS] = {0, 0, 0, 0, 0, 0, 0, 0};
short int PWMValueD[NUMPINS] = {0, 0, 0, 0, 0, 0, 0, 0};


ISR:

Code: [Select]
void timerIsr() {
  static byte times;  # setting your 0-255 counter as a byte allows it to overflow to zero

  byte bankA = 0;  # the bit mask to send to bank A, initialized to eight zeroes. 

  for (byte pin = 0; i < NUMPINS; i++)     {
    if (PWMValueD[pin] < 0) {     
      bankA |= 1<<pin;
      PWMValueD[pin] = PWMValueD[pin] + 511 - 2 * PWMValue[pin];       
    } else {
      PWMValueD[pin] = PWMValueD[pin] - 2 * PWMValue[pin];
    }
  }

  sendSPI(bankA);
}


PWMValue[pin number] is an array of the PWM values for your pins in bank A. bankA is the bit mask you want to send to bank A.

This should speed things up for a couple reasons. First, it allows you to set all eight outputs in bank A with a single transmission. Second, it chops up the "pulse" on the pins into smaller pieces; for example if your pin was set with a value of 10, using your method, it would be on for 10 "times" and off for 246 "times". Using my method it will set the pin on 10 times spread evenly across the full 256 "times" and result in less flicker (hopefully). In that respect it's not really a "pulse width" but ... whatever. The for{} loop is a modified Bresenham algorithm which would normally be used to draw a properly sloped line across a grid -- think of it as blinking the pin whenever the line would go up a Y value.

Hopefully no bugs!

ematson5897

Thanks alot! I will try it tomorrow. I will also put this in my SPI version and post it

ematson5897

It is freezing the teensy. Heres all my code:

Code: [Select]
#include <TimerThree.h>
#include <SPI.h>
#define NUMPINS 16
int i;
boolean outputStates[NUMPINS];
int times;
byte byteToTransfer[NUMPINS/8];
int PWMValue[NUMPINS];
boolean PWMEnabled[NUMPINS];
boolean Pullup[NUMPINS];
boolean IO[NUMPINS];
boolean inputStates[NUMPINS];
short int PWMValueD[NUMPINS];
byte bank[NUMPINS/8];




void setup()
{
  Serial.begin(9600);
  pinMode(20,OUTPUT);
  digitalWrite(20,HIGH);
 
 
 
  SPI.begin(); // wake up I2C bus
  SPI.setClockDivider(SPI_CLOCK_DIV2);



  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x12);
  SPI.transfer(0x20); // use table 1.4 addressing
  digitalWrite(20,HIGH);

 
 
Timer3.initialize(10); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer3.attachInterrupt( timerIsr ); // attach the service routine here

for(int q=0; q<NUMPINS; q++)
  {
    pinMode2(q,OUTPUT);
  }

}


void loop()
{
  digitalWrite2(9,1);
  analogWrite2(8,10);
}







boolean digitalRead2(int pin)
{
  readInputs();
  return inputStates[pin];
}

void pinMode2(int pin, byte state)
{
  if(state < 2)
  {
    IO[pin] = state;
  }
  else if(state == 2)
  {
    IO[pin] = 0;
    Pullup[pin] = 1;
  }
  setPinModes();
}

void digitalWrite2(int pin, boolean state)
{
  PWMEnabled[pin] = 0;
  outputStates[pin] = state;
  SetOutputs();
}

void analogWrite2(int pin, byte val)
{
  PWMEnabled[pin] = 1;
  PWMValue[pin] = val;
}

void timerIsr() {
  static byte times;  // setting your 0-255 counter as a byte allows it to overflow to zero
  for(byte s=0; s<NUMPINS/8; s++)
  {
    for (byte pin = 0; pin < 8; i++)     {
      if (PWMValueD[pin] < 0) {     
        bank[s] |= 1<<pin;
        PWMValueD[pin] = PWMValueD[pin] + 511 - 2 * PWMValue[pin];       
      } else {
        PWMValueD[pin] = PWMValueD[pin] - 2 * PWMValue[pin];
      }
    }
  }
  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x14); // GPIOA
  SPI.transfer(bank[0]);    // bank A
  SPI.transfer(bank[1]);    // bank A
  digitalWrite(20,HIGH);
}

void SetOutputs()
{

  for(int j=0; j<NUMPINS/8; j++)
  {
    byteToTransfer[j] = 0;
    for (i = 0; i < 8; i++) {
    if (outputStates[i+(j*8)])
      byteToTransfer[j] |= (1 << i);
    }
  }



  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x14); // GPIOA
  SPI.transfer(byteToTransfer[0]);    // bank A
  SPI.transfer(byteToTransfer[1]);    // bank A
  digitalWrite(20,HIGH);
}

void setPinModes()
{
  for(int j=0; j<NUMPINS/8; j++)
  {
    byteToTransfer[j] = 0;
    for (i = 0; i < 8; i++) {
    if (IO[i+(j*8)])
      byteToTransfer[j] |= (1 << i);
    }
  }
  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x12); // GPIO
  for(i=0; i<NUMPINS/8; i++)
  {
    SPI.transfer(byteToTransfer[i]);
  }
  digitalWrite(20,HIGH);
 
  for(int j=0; j<NUMPINS/8; j++)
  {
   byteToTransfer[j] = 0;
    for (i = 0; i < 8; i++) {
    if (Pullup[i+(j*8)])
      byteToTransfer[j] |= (1 << i);
    }
  }
  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x0C); // GPPU
  for(i=0; i<NUMPINS/8; i++)
  {
    SPI.transfer(byteToTransfer[i]);
  }
  digitalWrite(20,HIGH);
}

void readInputs()
{
  byte byte1;
  byte byte2;
  digitalWrite(20,LOW);
  SPI.transfer(B01000011);
  SPI.transfer(0x12);
  byte1 = SPI.transfer(0);
  byte2 = SPI.transfer(0);
  digitalWrite(20,HIGH);
  for(i=0; i<NUMPINS/2; i++)
  {
    inputStates[8-i] = byte1 & 1;
    inputStates[16-i] = byte2 & 1;
    byte1 = byte1 >> 1;
    byte2 = byte2 >> 1;
  }
}
 
 
 
 
 
 

ematson5897

Now everything is working but PWM

Code: [Select]
#include <SPI.h>

#include <TimerThree.h>

#define NUMPINS 16
byte PWMValue[NUMPINS] = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned short int PWMValueD[NUMPINS] = {0, 0, 0, 0, 0, 0, 0, 0};
byte pinValue[NUMPINS] = {0, 0, 0, 0, 0, 0, 0, 0};
boolean outputStates[NUMPINS];
byte byteToTransfer[NUMPINS/8];
unsigned int i;
boolean PWMEnabled[NUMPINS];
boolean Pullup[NUMPINS];
boolean IO[NUMPINS];



void setup()
{
  pinMode(20,OUTPUT);
  digitalWrite(20,HIGH);
 
 
 
  SPI.begin(); // wake up I2C bus
  SPI.setClockDivider(SPI_CLOCK_DIV2);



  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x12);
  SPI.transfer(0x20); // use table 1.4 addressing
  digitalWrite(20,HIGH);
 
   for(int q=0; q<16; q++)
  {
    pinMode2(q,OUTPUT);
  }

  Timer3.initialize(100); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer3.attachInterrupt( timerIsr ); // attach the service routine here


}

void loop()
{
  for(int q=0; q<16; q++)
  {
    analogWrite2(q,255);
  }
}






void pinMode2(unsigned int pin,  byte state)
{
 
  if(state < 2)
  {
    IO[pin] = state;
  }
  else if(state == 2)
  {
    IO[pin] = 0;
    Pullup[pin] = 1;
  }
  setPinModes();
}


void setPinModes()
{
  byte bTT[NUMPINS/8];
  for( int j=0; j<NUMPINS/8; j++)
  {
    bTT[j] = 0;
    for (i = 0; i < 8; i++) {
    if ((IO[i+(j*8)]))
      bTT[j] |= (1 << i);
    }
  }
  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x00); // GPIO
  for(i=0; i<NUMPINS/8; i++)
  {
    SPI.transfer(byte(~bTT[i]));
  }
  digitalWrite(20,HIGH);
 
  for( int j=0; j<NUMPINS/8; j++)
  {
   bTT[j] = 0;
    for (i = 0; i < 8; i++) {
    if (Pullup[i+(j*8)])
      bTT[j] |= (1 << i);
    }
  }
  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x0C); // GPPU
  for(i=0; i<NUMPINS/8; i++)
  {
    SPI.transfer(bTT[i]);
  }
  digitalWrite(20,HIGH);
}


void digitalWrite2(unsigned int pin, boolean state)
{
  PWMEnabled[pin] = 0;
  outputStates[pin] = state;
  SetOutputs();
}
void analogWrite2(unsigned int pin,  byte val)
{
  PWMEnabled[pin] = 1;
  PWMValue[pin] = val;
}


void timerIsr() {
  for (byte pin = 0; pin < NUMPINS; pin++)     
  {
    if(PWMEnabled[pin])
    {
      if (PWMValueD[pin] < 0)
      {     
        outputStates[pin] = 1;
        PWMValueD[pin] = PWMValueD[pin] + 511 - 2 * PWMValue[pin];       
      }
      else
      {
        outputStates[pin] = 0;
        PWMValueD[pin] = PWMValueD[pin] - 2 * PWMValue[pin];
      }
    }
  }
  SetOutputs();
}


void SetOutputs()
{
  for( int j=0; j<NUMPINS/8; j++)
  {
    byteToTransfer[j] = 0;
    for (i = 0; i < 8; i++) {
    if (outputStates[i+(j*8)])
      byteToTransfer[j] |= (1 << i);
    }
  }

  digitalWrite(20,LOW);
  SPI.transfer(B01000010);
  SPI.transfer(0x14); // GPIOA
  for(int j=0; j<NUMPINS/8; j++)
  {
    SPI.transfer(byteToTransfer[j]);
  }
  digitalWrite(20,HIGH);
}


























Go Up