2

I am getting back into programming with Arduino and built a POV fidget spinner inspired by an instructable by MakersBox. I am using the SparkFun AVR Pocket Programmer and an ATTiny84 (8MHz internal clock) with ATTinyCore as the board manager.

I was able to upload the code without error a few days ago using the 'USBTinyISP (ATTinyCore) FAST, for parts running >= 2MHz' programmer selection but it stopped working. Now the below error shows:

avrdude: Version 6.3-20201216
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:\Users\Franklin Marquette\AppData\Local\Arduino15\packages\ATTinyCore\hardware\avr\1.5.2/avrdude.conf"

         Using Port                    : usb
         Using Programmer              : usbtiny
         Setting bit clk period        : 0.3
avrdude: usbdev_open(): Found USBtinyISP, bus:device: bus-0:\\.\libusb0-0001--0x1781-0x0c9f
         AVR Part                      : ATtiny84
         Chip Erase delay              : 15000 us
         PAGEL                         : P00
         BS2                           : P00
         RESET disposition             : possible i/o
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65     6     4    0 no        512    4      0  4000  4500 0xff 0xff
           flash         65     6    32    0 yes      8192   64    128  4500  4500 0xff 0xff
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00
           lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00

         Programmer Type : USBtiny
         Description     : USBtiny simple USB programmer, http://www.ladyada.net/make/usbtinyisp/
avrdude: programmer operation not supported

avrdude: Setting SCK period to 1 usec
avrdude: initialization failed, rc=-1
         Double check connections and try again, or use -F to override
         this check.


avrdude done.  Thank you.

Failed programming: uploading error: exit status 1

NOW if I upload using the "USBtinyISP (ATtinyCore) SLOW, for new or 1 MHz parts" programmer option, the code does upload. It is 8x slower though. I uploaded the Blink sketch to a separate ATTiny84 on a breadboard and the connected LED stayed on for ~7.75s when it was programmed to stay on for 1s.

I have no idea what is going on and would really appreciate any help that I can get. Thank you!

For reference, the code is below.

/*

Not all Attiny cores support the tone() function. Try this one
https://github.com/SpenceKonde/ATTinyCore

// ATMEL ATTINY84 / ARDUINO
//
//                           +-\/-+
//                     VCC  1|    |14  GND
//             (D  0)  PB0  2|    |13  AREF (D 10)
//             (D  1)  PB1  3|    |12  PA1  (D  9) 
//                     PB3  4|    |11  PA2  (D  8) 
//  PWM  INT0  (D  2)  PB2  5|    |10  PA3  (D  7) 
//  PWM        (D  3)  PA7  6|    |9   PA4  (D  6) 
//  PWM        (D  4)  PA6  7|    |8   PA5  (D  5)        PWM

*/

#include <EEPROMex.h>
#include <avr/pgmspace.h>
#include "font.h"
#include "textAndShapes.h"

const int charHeight = 8;
const int charWidth = 5;
int rows= 8;                // Total LED's in a row
int LEDS[] = {0, 1, 2, 3, 4, 5, 7, 6};
bool STATES[] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};

char charArray[6];              // holds characters to display
unsigned long lastTimeUs;                // time (us) magnet sensed
unsigned long revTimeUs;                 // time (us) of last full rotation
unsigned long dwellTimeUs;               // time (us) between LED changes (based on rotation speed)
volatile unsigned long revolutions = 0;  // track number of revolutions since last start
long totalRevolutions;          // track total number of revolutions (stored in EEPROM)
bool spinning = true;          // track reset of time & counts
unsigned long startTimeUs;               // time (us) current spinning started
  
int mode;                       // current operating mode, stored in EEPROM
int modes = 8;                  // number of modes available
  // 0 -> text "Hello World!"
  // 1 -> RPM
  // 2 -> time in seconds
  // 3 -> spin count
  // 4 -> spin count (total)
  // 5 -> "lilly pad" pattern
  // 6 -> shape 1 (heart)
  // 7 -> shape 2 (smile)
  
byte ledPin = 0;     
byte magPin = 0;                // Hall effect sensor, pulled-up, goes low when magnet passes
byte magPullUp = 10;            // Pin A0 / D10
byte touchPin = 1;              // Push button A1 / D9
byte touchPullUp = 9;

volatile boolean rotationFlag = false;  // modified by ISR


void setup(){
  // setup inputs
  pinMode(touchPin, INPUT);
  digitalWrite(touchPullUp, HIGH);
  pinMode(magPin, INPUT);
  digitalWrite(magPullUp, HIGH);
  
  // setup other LEDs
  for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
    pinMode(LEDS[LED], OUTPUT);
    digitalWrite(LEDS[LED], STATES[LED]);
  }

  // Interupt setting
  GIMSK  = bit (PCIE0);  // Pin change interrupt enable
  PCMSK0 = bit (PCINT0); // Enable interupt on PCINT0 (D10)
  sei();                 // enables interrupts

  // get saved mode from eeprom
  mode = EEPROM.read(0);
  if (mode >= modes) {
    mode = 0; // may be very large first time
  }
  
  // get saved revolution total from eeprom
  totalRevolutions = EEPROMReadlong(1);
  if (totalRevolutions < 0 || totalRevolutions > 999999UL){ // will be -1 first time.
    totalRevolutions = 0;
    EEPROMWritelong(1, 0);
  }

  // show we are alive and what mode is active
  blipLEDs();

  lastTimeUs = micros();
}


void loop(){
  int sigFigs = 6;               // number of significant figures to deplay
  unsigned long curTimeUs;
  int seconds;
  unsigned int rpm;
  
  checkButton();
  if (((micros() - lastTimeUs) > 1000000UL)){ // less than 1 rev / sec
    if (spinning){
      spinning = false;
      totalRevolutions = totalRevolutions + revolutions;
      EEPROMWritelong(1, totalRevolutions);
      revolutions = 0;
      clearArray();
      blipLEDs();
    }
    else {
      //digitalWrite(LEDS[mode], LOW); 
    }
  }

  if (mode == 5){  // lilly pad pattern, show regardles of magnet.
    for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
       digitalWrite(LEDS[LED], HIGH); 
       delay(1); 
       digitalWrite(LEDS[LED], LOW); 
    } 
    for(int LED=(sizeof(LEDS)/sizeof(int))-1; LED >= 0; LED--){
       digitalWrite(LEDS[LED], HIGH); 
       delay(1); 
       digitalWrite(LEDS[LED], LOW); 
    }     
  }

  else if (rotationFlag){ // we are spinning!
    rotationFlag = false;
    if (!spinning){
      spinning = true;
      startTimeUs = micros(); 
    }
    curTimeUs = micros();
    revTimeUs = curTimeUs - lastTimeUs;
    dwellTimeUs = revTimeUs * 3UL / 360UL;   // 3 degrees
    seconds = (curTimeUs - startTimeUs) / 1000000UL;
    rpm = 60000 * 1000 / revTimeUs;
    lastTimeUs = curTimeUs;
    clearArray();    
    if (mode == 0){
      strcpy (charArray, text);
      //sprintf(charArray, "%lu", dwellTimeUs);
    }
    else if (mode == 1){
      sprintf(charArray, "%d", rpm);
      sigFigs = 2;
    }
    else if (mode == 2){
      sprintf(charArray, "%d", seconds);
    }   
    else if (mode == 3){   
      sprintf(charArray, "%lu", revolutions);
    }
    else if (mode == 4){   
      sprintf(charArray, "%lu", totalRevolutions + revolutions);
    }
    else if (mode == 6){ // shape 1 (heart)
      for(int k=0; k< sizeof(shape_1);k++){
        if (rotationFlag){
          break;
        }
        char b = pgm_read_byte_near(&(shape_1[k]));
        for (int j=0; j<charHeight; j++) {
          digitalWrite(LEDS[j], bitRead(b, 7-j));
        }
        dwellTimeUs = revTimeUs * 4UL / 360UL;   // 5 degrees
        delayMicroseconds(dwellTimeUs);
      }
    }    
    else if (mode == 7){ // shape 2 (smile)
      for(int k=0; k< sizeof(shape_2);k++){
        if (rotationFlag){
          break;
        }
        char b = pgm_read_byte_near(&(shape_2[k]));
        for (int j=0; j<charHeight; j++) {
          digitalWrite(LEDS[j], bitRead(b, 7-j));
        }
        dwellTimeUs = revTimeUs * 4UL / 360UL;   // 5 degrees
        delayMicroseconds(dwellTimeUs);
      }
    }

    // Text in top half
    if (mode < 5) { 
      int digits = 0;    
      for(int k=0; k< sizeof(charArray);k++){
        char c = charArray[k];
        if (rotationFlag){
          break;
        }
        if(c){
          if (digits < sigFigs){
            printLetter(c);
            //digits += 1;
          }
          else{
            printLetter('0');
          }
          digits += 1;
        }
      }
      
      // Handle display in lower section
      clearArray();
      if(1 && (revTimeUs < 200000)){
        char * ptr = (char *) pgm_read_word (&string_table[mode]);
        //char buffer [6]; // must be large enough!
        strcpy_P (charArray, ptr);
  
        // wait for it . . .
        while((micros() < (lastTimeUs + revTimeUs / 2)) && !rotationFlag){};
        
        // show it
        for (int k=sizeof(charArray)-1; k>=0; k--){
          if (rotationFlag){
            break;
          }
          printLetterLower(charArray[k]);;
        }
      }
    }
  }  
}


ISR(PCINT0_vect){  // Magnet sensed
  if (!digitalRead(magPullUp)){
    rotationFlag = true;             // Increment volatile variables
    revolutions += 1;
  }
}


void dwellDelay(){ // avoid glitch on first rotation having erronious value 
  if (dwellTimeUs > 2000){
    dwellTimeUs = 2000;
  }
  if (dwellTimeUs < 100){
    dwellTimeUs = 100;
  }  
  delayMicroseconds(dwellTimeUs);
}


void printLetter(char ch){
// https://github.com/reger-men/Arduion-POV-clock/blob/master/clock.ino
  // make sure the character is within the alphabet bounds (defined by the font.h file)
  // if it's not, make it a blank character
  if (ch < 32 || ch > 126){
    ch = 32;
    }
  // subtract the space character (converts the ASCII number to the font index number)
  ch -= 32;
  // step through each byte of the character array
  for (int i=0; i<charWidth; i++) {
    char b = pgm_read_byte_near(&(font[ch][i]));
    
    for (int j=0; j<charHeight; j++) {
      digitalWrite(LEDS[j], bitRead(b, 7-j));
    }
    dwellDelay();
  }
  
  //clear the LEDs
  for (int i = 0; i < rows; i++)
    digitalWrite(LEDS[i] , LOW);
  dwellDelay();
}


void printLetterLower(char ch){
  // make sure the character is within the alphabet bounds (defined by the font.h file)
  // if it's not, make it a blank character
  if (ch < 32 || ch > 126){
    ch = 32;
    }
  // subtract the space character (converts the ASCII number to the font index number)
  ch -= 32;
  // step through each byte of the character array
  for (int i=charWidth-1; i>-1; i--) {
    char b = pgm_read_byte_near(&(font[ch][i]));
    for (int j=0; j<charHeight; j++) {
      digitalWrite(LEDS[j+1], bitRead(b,j));
    }
    dwellDelay();
  }
  //clear the LEDs
  for (int i = 0; i < rows; i++)
    digitalWrite(LEDS[i] , LOW);

  // space between letters
  dwellDelay();
}  


bool touched(){
  // returns true if touched, false if not.  Light LED until touch released
  bool touchVal = digitalRead(touchPullUp);
  if (!touchVal){
    while(!digitalRead(touchPullUp)){ // wait till touch release
      delay(10);
      digitalWrite(LEDS[mode], LOW);
    }
    //digitalWrite(LEDS[0], LOW);
    return (true);
  }
  else{
    return (false);
  }
}


void checkButton(){
  // check button for mode change and display current mode
  if (touched()){
    mode += 1;
    if (mode >= modes){
      mode = 0;
    }
    EEPROM.write(0, mode);
    blipLEDs();
  }
}


void blipLEDs(){
  // something to show we are alive
  for(int LED=0; LED<(sizeof(LEDS)/sizeof(int)); LED++){
    digitalWrite(LEDS[LED], HIGH); 
    delay(10); 
    digitalWrite(LEDS[LED], LOW); 
  } 
  for(int LED=sizeof(LEDS)/sizeof(int); LED>mode; LED--){
    digitalWrite(LEDS[LED], HIGH); 
    delay(10); 
    digitalWrite(LEDS[LED], LOW); 
  } 
  digitalWrite(LEDS[mode], HIGH);
}



void EEPROMWritelong(int address, long value)  {
  //This function will write a 4 byte (32bit) long to the eeprom at
  //the specified address to address + 3.
  //https://playground.arduino.cc/Code/EEPROMReadWriteLong

  //Decomposition from a long to 4 bytes by using bitshift.
  //One = Most significant -> Four = Least significant byte
  byte four = (value & 0xFF);
  byte three = ((value >> 8) & 0xFF);
  byte two = ((value >> 16) & 0xFF);
  byte one = ((value >> 24) & 0xFF);

  //Write the 4 bytes into the eeprom memory.
  EEPROM.write(address, four);
  EEPROM.write(address + 1, three);
  EEPROM.write(address + 2, two);
  EEPROM.write(address + 3, one);
}


long EEPROMReadlong(long address){
  //Read the 4 bytes from the eeprom memory.
  long four = EEPROM.read(address);
  long three = EEPROM.read(address + 1);
  long two = EEPROM.read(address + 2);
  long one = EEPROM.read(address + 3);

  //Return the recomposed long by using bitshift.
  return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}


void clearArray(){
  for(int i=0; i<sizeof(charArray); i++){      // clear array
    charArray[i] = 0;
  }
}
1
  • I'm probably missing something. I'll just point out that the selection of the board/options sets the IDEs expectations for compiling the code and it sets the configuration that it would burn if you did "burn bootloader". If you tell the IDE that you have an 8mhz configuration when you in fact have a 1mhz configuration, your delays will be 8 times longer and your serial rates 1/8th. I don't know what that has to do with failing to upload though. Max ISP rate is either 1/4 so 1/6 actual clock rate. You can't use a 1mhz isp bitclock with a 1mhz clocked chip. Are you doing that? no idea.
    – timemage
    Commented Nov 14, 2022 at 7:17

1 Answer 1

2

When you first get one of those chips, it's always internally configured to down-clock the internal oscillator by 8. So, it's still running at 8MHz physical, but the actual chip clock speed is 1 MHz.

If you tell the IDE it's the 8 MHz version (divider turned off), it will take ~8x longer to run any sort of delay (which is why you're seeing the 7.75s LED pulse rather than 1s).

To fix this problem, set the IDE to the chip settings you want (8 MHz internal oscillator*), then click "Burn Bootloader." This won't actually set the bootloader, as this chip doesn't use one, but it will set the internal fuses so the clock divider is turned off and it will work at 8 MHz as expected.

Note that the programmer will still need to be set to the low speed (why it says "SLOW, for new [emphasis mine] or 1 MHz parts") when you do the "Burn Bootloader" as the chip will otherwise be running too slowly at 1 MHz to receive the commands at the programmer's default speed (the other cause of the rc=-1 error, aside from bad wiring).

Note that sometimes the programmer will also fail to work if any of the programming pins have LEDs or switches attached - are you removing the chip before programming, or is it still attached to the circuit? If the latter, also try isolating it before programming.

*Make sure you do not select any of the external clock options unless you know what you're doing. Doing this will brick your chip unless you happen to have an external clock source of that type or a high voltage programmer. I'm not sure how those work, but they're a special (expensive) variant that uses a higher-than normal voltage to override the fuse settings even if the chip won't clock.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.