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;
}
}
-
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– timemage2022年11月14日 07:17:17 +00:00Commented Nov 14, 2022 at 7:17
1 Answer 1
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.