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:
#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;
}
}
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
(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?
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
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
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 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.