Go Down

Topic: ATmega328 PB6/7 - Tutorial & Question about Analog/Digital pin numbering (Read 383 times) previous topic - next topic

Hauke

Dear all,

gathering information from all around the internet I finally managed to use PB6 and PB7 on a standalone ATmega328 running on its internal oscillator. Basically, I changed pins_arduino.h and boards.txt.

I decided to assign pin numbers 14 and 15 to PB6/7. Pin 16 to 21 are corresponding to A0 to A5. If I do a digital read from pin 16, it works. However, if I use the Ax scheme for reading digitally from a pin, like digitalRead from A0, which should be equivalent to reading from 16, it does not work. AnalogRead from A0 however works fine. When I did the edit of pins_arduino.h (I'll attach the full version below), I thought that I would need to adjust the lines like

#define PIN_A0   (14)

to

#define PIN_A0   (16)

to acommodate for the added two digital pins, but doing this made things even worse, since analogRead went to the wrong pins either then.

I am now rather confused what I do wrong. My goal is that I can use A0...A5 and 16...21 interchangably.

Since my solution works in principal and I could not find a good documentation on the web, I wrote a tutorial that also describes my problem in more detail. If anyone can hint me on what I'm doing wrong, I'll update the tutorial.

Thanks a lot!

Hauke

...and here my pins_arduino.h:

Code: [Select]
/*
  pins_arduino.h - Pin definition functions for Arduino
  Part of Arduino - http://www.arduino.cc/

  Copyright (c) 2007 David A. Mellis, modified by Hauke 2017

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

#ifndef Pins_Arduino_h
#define Pins_Arduino_h

#include <avr/pgmspace.h>

#define NUM_DIGITAL_PINS            22
#define NUM_ANALOG_INPUTS           6
#define analogInputToDigitalPin(p)  ((p < 6) ? (p) + 16 : -1)

#if defined(__AVR_ATmega8__)
#define digitalPinHasPWM(p)         ((p) == 9 || (p) == 10 || (p) == 11)
#else
#define digitalPinHasPWM(p)         ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11)
#endif

#define PIN_SPI_SS    (10)
#define PIN_SPI_MOSI  (11)
#define PIN_SPI_MISO  (12)
#define PIN_SPI_SCK   (13)

static const uint8_t SS   = PIN_SPI_SS;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK  = PIN_SPI_SCK;

#define PIN_WIRE_SDA        (18)
#define PIN_WIRE_SCL        (19)

static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;

#define LED_BUILTIN 13

#define PIN_A0   (14)
#define PIN_A1   (15)
#define PIN_A2   (16)
#define PIN_A3   (17)
#define PIN_A4   (18)
#define PIN_A5   (19)
#define PIN_A6   (20)
#define PIN_A7   (21)

static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;

#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 23) ? (&PCICR) : ((uint8_t *)0))
#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 15) ? 0 : 1))
#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK2) : (((p) <= 15) ? (&PCMSK0) : (((p) <= 23) ? (&PCMSK1) : ((uint8_t *)0))))
#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 15) ? ((p) - 8) : ((p) - 16)))

#define digitalPinToInterrupt(p)  ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT))

#ifdef ARDUINO_MAIN

// On the Arduino board, digital pins are also used
// for the analog output (software PWM).  Analog input
// pins are a separate set.

// ATMEL ATMEGA8 & 168 / ARDUINO
//
//                  +-\/-+
//            PC6  1|    |28  PC5 (AI 5/D 21)
//      (D 0) PD0  2|    |27  PC4 (AI 4/D 20)
//      (D 1) PD1  3|    |26  PC3 (AI 3/D 19)
//      (D 2) PD2  4|    |25  PC2 (AI 2/D 18)
// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1/D 17)
//      (D 4) PD4  6|    |23  PC0 (AI 0/D 16)
//            VCC  7|    |22  GND
//            GND  8|    |21  AREF
//     (D 14) PB6  9|    |20  AVCC
//     (D 15) PB7 10|    |19  PB5 (D 13)
// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)
// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM
//      (D 7) PD7 13|    |16  PB2 (D 10) PWM
//      (D 8) PB0 14|    |15  PB1 (D 9) PWM
//                  +----+
//
// (PWM+ indicates the additional PWM pins on the ATmega168.)

// ATMEL ATMEGA1280 / ARDUINO
//
// 0-7 PE0-PE7   works
// 8-13 PB0-PB5  works
// 14-21 PA0-PA7 works
// 22-29 PH0-PH7 works
// 30-35 PG5-PG0 works
// 36-43 PC7-PC0 works
// 44-51 PJ7-PJ0 works
// 52-59 PL7-PL0 works
// 60-67 PD7-PD0 works
// A0-A7 PF0-PF7
// A8-A15 PK0-PK7


// these arrays map port names (e.g. port B) to the
// appropriate addresses for various functions (e.g. reading
// and writing)
const uint16_t PROGMEM port_to_mode_PGM[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &DDRB,
(uint16_t) &DDRC,
(uint16_t) &DDRD,
};

const uint16_t PROGMEM port_to_output_PGM[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &PORTB,
(uint16_t) &PORTC,
(uint16_t) &PORTD,
};

const uint16_t PROGMEM port_to_input_PGM[] = {
NOT_A_PORT,
NOT_A_PORT,
(uint16_t) &PINB,
(uint16_t) &PINC,
(uint16_t) &PIND,
};

const uint8_t PROGMEM digital_pin_to_port_PGM[] = {
PD, /* 0 */
PD,
PD,
PD,
PD,
PD,
PD,
PD,
PB, /* 8 */
PB,
PB,
PB,
PB,
PB,
PB,
PB,
PC, /* 16 */
PC,
PC,
PC,
PC,
PC,
};

const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
_BV(0), /* 0, port D */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
_BV(6),
_BV(7),
_BV(0), /* 8, port B */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
_BV(6),
_BV(7),
_BV(0), /* 16, port C */
_BV(1),
_BV(2),
_BV(3),
_BV(4),
_BV(5),
};

const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
NOT_ON_TIMER, /* 0 - port D */
NOT_ON_TIMER,
NOT_ON_TIMER,
// on the ATmega168, digital pin 3 has hardware pwm
#if defined(__AVR_ATmega8__)
NOT_ON_TIMER,
#else
TIMER2B,
#endif
NOT_ON_TIMER,
// on the ATmega168, digital pins 5 and 6 have hardware pwm
#if defined(__AVR_ATmega8__)
NOT_ON_TIMER,
NOT_ON_TIMER,
#else
TIMER0B,
TIMER0A,
#endif
NOT_ON_TIMER,
NOT_ON_TIMER, /* 8 - port B */
TIMER1A,
TIMER1B,
#if defined(__AVR_ATmega8__)
TIMER2,
#else
TIMER2A,
#endif
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER, /* 16 - port C */
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
NOT_ON_TIMER,
};

#endif

// These serial port names are intended to allow libraries and architecture-neutral
// sketches to automatically default to the correct port name for a particular type
// of use.  For example, a GPS module would normally connect to SERIAL_PORT_HARDWARE_OPEN,
// the first hardware serial port whose RX/TX pins are not dedicated to another use.
//
// SERIAL_PORT_MONITOR        Port which normally prints to the Arduino Serial Monitor
//
// SERIAL_PORT_USBVIRTUAL     Port which is USB virtual serial
//
// SERIAL_PORT_LINUXBRIDGE    Port which connects to a Linux system via Bridge library
//
// SERIAL_PORT_HARDWARE       Hardware serial port, physical RX & TX pins.
//
// SERIAL_PORT_HARDWARE_OPEN  Hardware serial ports which are open for use.  Their RX & TX
//                            pins are NOT connected to anything by default.
#define SERIAL_PORT_MONITOR   Serial
#define SERIAL_PORT_HARDWARE Serial

#endif

pert

You were correct that you need to change those definitions. Unfortunately pin numbering assumptions were hardcoded into analogWrite() so you can't actually create a custom pinout only by adding a variant file:
https://github.com/arduino/Arduino/blob/1.8.2/hardware/arduino/avr/cores/arduino/wiring_analog.c#L54
This is how it converts the pin argument on ATmega328:
Code: [Select]
if (pin >= 14) pin -= 14; // allow for channel or pin numbers
so you would need to change that to:
Code: [Select]
if (pin >= 16) pin -= 16; // allow for channel or pin numbers
After updating the analog pin defines in pins_arduino.h and making that change to analogWrite() it should work correctly with your variant in all usages, of course that will break analogWrite() for the standard variant...

Hauke

Hi Pert,

thanks a lot, that explains it very well. I'll think about how to best incorporate this and try around a little bit. perhaps I'll try to get the new pins to D21/22 instead as others suggested but did not work for me. Perhaps I was just too stupid there.

Cheers

Hauke

DrAzzy

Doesn't MiniCore already support this out of the box? https://github.com/MCUdude/MiniCore
ATtiny core for 841+1634+828 and x313/x4/x5/x61/x7/x8 series Board Manager:
http://drazzy.com/package_drazzy.com_index.json
ATtiny breakouts (some assembled), mosfets and awesome prototyping board in my store http://tindie.com/stores/DrAzzy

pert

Yeah, MiniCore uses pins 20 and 21 for PB6 and PB7 so if nothing else that would be a good reference to see how it was done.

Hauke

Oh dear, thanks a lot - funny how you can spend ages searching the internet and not hit the page that answeres it all. Will try this out!
Edit: Just altered my code and included MiniCore - works like a charm! Wrote my tutorial for nothing :-/ :-) Thanks again for teh hint!

Go Up