Standalone 328P: programmed successfully but doesn't work

Hello, I've got a project for a servo controller board utilizing the 328P. I've successfully programmed the chip, but it doesn't seem to do what it's supposed to: notably, send some PWM signals to gates on an H-bridge. The basic question is: Is it possible that the chip was somehow incorrectly connected but still programs successfully? That way I'd know to look for a connection problem rather than a nonworking chip, for example (it's soldered in so not trivial to replace and check). Any ideas would be appreciated. Thank you.

Have you got a scope to look at the output pins? See if they are doing anything. One thing I like to do is use test points that I can probe - an LED makes a good test point too if you have one under control of the processor. Since it (according to your information) is a standalone, you don't have debug info from the programmer etc. available so it is handy to put things in your code the pulse a test point 3 times for example each time through that part of the code which you can see with the scope and get an idea where it is running. Once you get it running, you can either re-compile without those debug statements or just leave them in as long as they don't take up any critical processor time. Debugging hardware without a scope or equiv. and no debug from a standalone board can be a pain since you don't know where (or if) your code is running.

Hi, and thanks for your response. My scope is what told me there's not PWM. In fact the pins are doing nothing as far as it's concerned. I'm going to try the test LED approach and upload an example blink, the only issue is having to solder the LED in (it's a circuit board with SMD components so that's a bit annoying) and update this post if anything happens, but I doubt it since the PWM generation is about as straight-forward as the LED in the first place. Edit: blink doesn't work, i.e. the MCU output pins don't respond to the program's commands. It consistently programs successfully though. I'm thinking possibly faulty chip, but it'd be nice to know for sure if that's possible (i.e. it programs fine) before desoldering and resoldering 32 tiny pins. Also eliminated oscillator as possible issue (scraped it off and soldered with wires, clock registers properly on oscilloscope). At this point I'm thinking chip replacement, perhaps some module got damaged so that it responds to programming but can't work the output pins?

You could have overheated the 328p when soldering. I've done it before..

Do you have another board with the same chip on it?

Test the same sketch on that board, and see if the pins work there. If they don't, the problem is your sketch.

Also check all pins to make sure they're not shorted to gnd, vcc, or eachother. if you're hand-soldering the part, which it sounds like you are, this is a very common issue.

Thanks for the suggestions everyone.

I tried redoing the whole circuit, with a fresh chip, was very careful not to overheat it, and got the same exact result: it programs, but the pins don’t do anything they’re supposed to.

I tried the sketch on a working UNO, and it works just fine there. Plus I have a basic blink type sketch for a test, which is impossible to get wrong, and that doesn’t work either.

No pins seem to be shorted as far as I can tell.

Is this a board you have designed/created? One of the first things I would check is to make sure you didn't do something clever like forget the power or ground on the chip (ask me how I know about that one - you can get some really strange logic circuits if you forget power on something like a quad 2 input nand gate. If any input is high, the other 3 gates work correctly - makes for fun debugging!!). Also, especially on a board where the cpu is soldered on, I would put the ISP header on the board so I could program it without removing it from the board.

Yes, it's my design, I can share it if someone finds it helpful.

I do believe I didn't forget any essentials. At the very least there is clean, 5V power to the chip, and the oscillator works properly. The necessary resistors/capacitors and inductor are there, and I have access to SPI through a header on the board. Said SPI successfully programs the 328P chip (as evidenced by avrdude), but the chip fails to react to the (verified to work correctly) sketch.

Perhaps problem with trying to use pin numbers as the software mapped names? Ex, D13 is not physical pin 13.

Could you elaborate please? That is, if I want to blink an LED connected to physical pin 25 with what would be PC2 (physical pin 25), I can't use PC2 but must use digitalWrite(25, HIGH) for example? I actually tried that to no avail, so maybe still doing something wrong.

Physical pin 25 is PC2 is A2 = D16, not D25.

Not sure what you mean by "D16", that doesn't compile... I tried using 25, PC2, and A2. Compiles successfully, but never works. Uploads fine though, no errors from avrdude, so it seems to see the chip. That's 2 identical boards with the same behavior now so I doubt it's a bad chip.

Post your code. 16 is the software mapping to physical pin 25.

byte testPin = 16;
void setup(){
pinMode (testPin, OUTPUT);
}
void loop(){
digitalWrite (testPin, HIGH);
delay (500);
digitalWrite (testPin, LOW);
delay (500);
}

That should make the pin flash.

Essentially, I was trying this:

byte testPin = PC2;
void setup(){
pinMode (testPin, OUTPUT);
}
void loop(){
digitalWrite (testPin, HIGH);
delay (500);
digitalWrite (testPin, LOW);
delay (500);
}

You’re right, using 16 works, whereas PC2 apparently does not. Where can I find the software mapping for the other pins? I need to use the timers and was using something like (long code segment):

  #include "Arduino.h"
#include <avr/io.h>
#include <Wire.h>
#include <SPI.h>

#define SET_POS_REG 0x02
#define SET_SPEED_REG 0x04
#define POS_REG 0x06
#define SPEED_REG 0x08
#define VOLTAGE_REG 0x10
#define TEMP_REG 0x11
#define CONST_SPEED_REG 0x12

#define GET_TEMP(voltage) ((voltage - 288) / 1.024)    // This formula should be changed for every chip for accuracy
#define GET_BVOLTAGE(voltage) (voltage * 2.436)         // Scaled voltage value from voltage divider 
#define SET_PWM(error) (0.647 * error + 90)            // Sets the pwm based on error reading, needs adjustment for different motor types/settings

#define DIR1 0
#define DIR2 1
#define STOP 3

byte read_reg;

//*(byte *)SET_POS_REG = 512;

int set_speed;
int set_pos;

int error;

byte spinDir = STOP;


bool talk = false;

void setup() {
  Serial.begin(115200);
  
  TWAR = 2;    // THIS MUST BE A DIFFERENT ADDRESS FOR EACH INDIVIDUAL SERVO
  TWBR = 72;    // Defines I2C communication freq to be ~100kHz for 16MHz clock
 
  Wire.begin(TWAR);
  Wire.onReceive(i2c_write);
  Wire.onRequest(i2c_read);
  
  // SET UP ADC
  ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)); //Prescaler at 128 so we have an 125Khz clock source
  ADMUX |= (1 << REFS0);              //Voltage reference from Avcc (5v)
  ADCSRA |= (1 << ADEN);              //Turn on ADC
  ADCSRA |= (1 << ADSC);              //Do an initial conversion because this one is the slowest and to ensure that everything is up and running

  // SET UP TIMERS FOR PWM
  TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);    // Phase correct pwm, clear on comp. match upcounting, set downcounting
  TCCR2B = _BV(CS20);    // no prescale, thus frequency = 16MHz / 510 ~30kHz
  
  TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);    // Phase correct pwm, clear on comp. match upcounting, set downcounting
  TCCR0B = _BV(CS00);    // no prescale, thus frequency = 16MHz / 510 ~30kHz
  
  // 328p datasheet calls for setting pins to output after setting up timers
  DDRD |= (1 << PD3) | (1 << PD5);    // digital pin 3  = OC2B, digital pins 5 = OC0B
  DDRD |= (1 << PD2) | (1 << PD4) | (1 << PD6) | (1 << PD7);  // non-pwm gate signals and bemf signals

  // Enable temperature sensor, +/- 10C, can find out way to do calibration in datasheet
  ADMUX |= (1 << MUX2);
  
  //while (!Wire.available()); // wait until control signal to start moving, DISABLE WHEN TESTING
  Serial.println("ready");
}

void i2c_write(int bytes) {
  byte write_reg = Wire.read();
  byte value;
  
  switch (write_reg) {
    case SET_POS_REG  : set_pos = Wire.read();
                        *((byte *)SET_POS_REG) = (set_pos >> 8);     // most significant byte of set_pos
                        *((byte *)SET_POS_REG + 1) = set_pos;        // least significant byte of set_pos
                        set_speed = Wire.read();
                        *((byte *)SET_SPEED_REG) = (set_speed >> 8); // most significant byte of set_speed
                        *((byte *)SET_SPEED_REG + 1) = set_speed;    // least significant byte of set_pos
                        *((byte *)CONST_SPEED_REG) = Wire.read();
                        break;
    case POS_REG     :  
    case SPEED_REG   :
    case VOLTAGE_REG : 
    case TEMP_REG    : read_reg = write_reg;
                       break;                  
  }
}

void i2c_read() {
  if (read_reg == POS_REG || SPEED_REG) {    // 2-byte values
    Wire.write(*((byte *)read_reg));
    Wire.write(*((byte *)read_reg + 1));
  }
  else
    Wire.write(*((byte *)read_reg));
}

void loop() {  
  talk = false;
  
  *(byte *)SET_POS_REG = 128;
  
  if (Serial.available()) { *(byte *)SET_POS_REG = Serial.parseInt(); talk = true;  }
  
  ADMUX |= PC3;              // ADC will read channel of A3 pin
  ADCSRA |= (1 << ADSC);      // new conversion starts
  while (ADCSRA & (1 << ADSC)); // wait for conversion to finish                
  *((byte *)POS_REG) = ADCW / 4;    // read pot value
  if (talk == true) Serial.println(ADCW);
  ADMUX &= 0xF0;              // clear the channel

  error = (*((byte *)SET_POS_REG) << 8) | *((byte *)SET_POS_REG + 1) - (*((byte *)POS_REG) << 8) | *((byte *)POS_REG + 1);    // 16-bit position value

  if (error > 3 / 4.0) {
    if (spinDir != DIR1) {
      spinDir = DIR1;
      PORTD = 0;
      TCCR0A = 0;
      TCCR2A = _BV(COM2B1) | _BV(WGM20);    // Prepares pwm for dir1
      PORTD = (1 << PD4);                   // open bottom gate for dir1
    }
    OCR2B = SET_PWM(error);               // Forward direction H-bridge pwm signal proportional to error
  }
  else if (error < -3 / 4.0) {
    if (spinDir != DIR2) {
    spinDir = DIR2;
    PORTD = 0;                 // Will close dir1 top gate after pwm stops
    TCCR2A = 0;
    TCCR0A = _BV(COM0B1) | _BV(WGM00);
    PORTD = (1 << PD2);
    }
    OCR0B = SET_PWM(-error);
  }
  else if (spinDir != STOP) {                       // Voltage and temp should only be read if PWM is off for accuracy, no point in rereading multiple times if motor is stopped
    spinDir = STOP;
    TCCR2A = 0;                // Stop pwm
    TCCR0A = 0;
    PORTD = 0;                // close all bottom gates
    PORTD = (1 << PD3 ) | (1 << PD5);  // Open both top gates for regenerative braking
  
    ADMUX |= PC2;              // ADC will read channel of A2 pin
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC)); 
    *((byte *)VOLTAGE_REG) = GET_BVOLTAGE(ADCW);             // read battery voltage
    ADMUX &= 0xF0;              // clear the channel
    
    ///  POSSIBLE THAT IT TAKES TIME FOR VOLTAGE FROM AREF CAPACITOR TO SETTLE TO 1.1V AFTER REF CHANGE, MAY AFFECT RESULTS IF DELAY NOT IMPLEMENTED
    ADMUX |= (1 << REFS1);      // 1.1V internal voltage ref must be enabled for temp sensor
    ADMUX |= (1 << MUX3);               // temperature channel ADC8
    *((byte *)TEMP_REG) = GET_TEMP(ADCW);    // temp conversion formula needs to be adjusted for every chip
    ADMUX &= 0xF0;
    ADMUX &= ~(1 << REFS1);      // reset voltage ref AVcc
  }
}

I was using the standard abbreviations that work for an UNO’s 328P. Will I need to revise for the standalone? Thank you.

|500x353

haha... gotcha !!!

of course w/o the queer naming and other oddball quirks there would be no arduino and atmel probably still a niche market to this day. can you say "market differentiation" ten times fast?

So my trouble comes from using things like "D5" instead of "5" (the IDE mapping of PD5)? I'll try replacing those instances in the code. Everything else should work fine I assume, since I'm using registers directly from the manufacturer's datasheet to control the timers.

you can use "5" or in the case of m328 "PD5". to make matters worse for other chips like tiny for example "5" is "PB5". some others the numbers dont line up at all and appear to be chosen based on some random lottery. and it gets even stranger when they start talking about analog pins like ic pin 25 (PC5) being either "A5" or "19" or "D19" or "pin 19" or "analog pin 5".

"D5" wont compile and i dont know why people ever use that terminology. i prefer the standard c method: for example "DDRD=0xFF" to make all pins outputs in one swell foop. correlates with the datasheet that way too. the "arduino way" it takes 10x more code to do the same thing , its at least 20x slower, and of course nothing makes sense from a datasheet point of view.

ps. sometimes you do need a sense of humor to get through this.

Update: good news, I’ve confirmed the second chip to work: blink and PWM both. I think the first might have indeed been overheated or something. Insidious that it would still program fine but just didn’t work.

Thank you to everyone who helped.