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\avr1円.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;
 }
}
asked Nov 14, 2022 at 1:11
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. 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.

answered Nov 16, 2022 at 19:22

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.