From 6b8940421e169a751dd039d8c7d1b1c4a1465fb2 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 10 May 2020 15:07:58 +0200 Subject: [PATCH] avr: use standard pin numbering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit changes pin numbering for atmega328 based boards (Uno, Nano) to use the standard format, where pin number is determined by the pin/port. Previously, pin numbers were based on what the Uno uses, which does not seem to have a clear pattern. One difference is that counting starts at port B, as there is no port A. So PB0 is 0, PB1 is 1… PC0 is 8. This commit also moves PWM code to the atmega328 file, as it may not be generic to all ATmega chips. --- Makefile | 6 +- src/examples/button/button.go | 4 +- src/examples/pwm/pwm.go | 6 +- src/machine/board_arduino.go | 36 ++++++--- src/machine/board_arduino_nano.go | 36 ++++++--- src/machine/board_atmega328p.go | 37 +++++++++ src/machine/machine_atmega.go | 66 ---------------- src/machine/machine_atmega328p.go | 124 +++++++++++++++++++++++------- 8 files changed, 195 insertions(+), 120 deletions(-) create mode 100644 src/machine/board_atmega328p.go diff --git a/Makefile b/Makefile index bacd9b5d3..b027d9c0d 100644 --- a/Makefile +++ b/Makefile @@ -182,7 +182,7 @@ tinygo-test: .PHONY: smoketest smoketest: $(TINYGO) version - # test all examples + # test all examples (except pwm) $(TINYGO) build -size short -o test.hex -target=pca10040 examples/blinky1 @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 examples/adc @@ -203,8 +203,6 @@ smoketest: @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=microbit examples/microbit-blink @$(MD5SUM) test.hex - $(TINYGO) build -size short -o test.hex -target=pca10040 examples/pwm - @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 examples/serial @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=pca10040 examples/systick @@ -296,6 +294,8 @@ ifneq ($(AVR), 0) @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1 @$(MD5SUM) test.hex + $(TINYGO) build -size short -o test.hex -target=arduino examples/pwm + @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1 @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1 diff --git a/src/examples/button/button.go b/src/examples/button/button.go index 8c245cdae..ad028ea0d 100644 --- a/src/examples/button/button.go +++ b/src/examples/button/button.go @@ -5,11 +5,9 @@ import ( "time" ) -// This example assumes that the button is connected to pin 8. Change the value -// below to use a different pin. const ( led = machine.LED - button = machine.Pin(8) + button = machine.BUTTON ) func main() { diff --git a/src/examples/pwm/pwm.go b/src/examples/pwm/pwm.go index 104934e56..96220ee57 100644 --- a/src/examples/pwm/pwm.go +++ b/src/examples/pwm/pwm.go @@ -8,9 +8,9 @@ import ( // This example assumes that an RGB LED is connected to pins 3, 5 and 6 on an Arduino. // Change the values below to use different pins. const ( - redPin = 3 - greenPin = 5 - bluePin = 6 + redPin = machine.D3 + greenPin = machine.D5 + bluePin = machine.D6 ) // cycleColor is just a placeholder until math/rand or some equivalent is working. diff --git a/src/machine/board_arduino.go b/src/machine/board_arduino.go index 92f17dac4..001ba9c70 100644 --- a/src/machine/board_arduino.go +++ b/src/machine/board_arduino.go @@ -7,21 +7,39 @@ func CPUFrequency() uint32 { return 16000000 } +// Digital pins, marked as plain numbers on the board. +const ( + D0 = PD0 // RX + D1 = PD1 // TX + D2 = PD2 + D3 = PD3 + D4 = PD4 + D5 = PD5 + D6 = PD6 + D7 = PD7 + D8 = PB0 + D9 = PB1 + D10 = PB2 + D11 = PB3 + D12 = PB4 + D13 = PB5 +) + // LED on the Arduino -const LED Pin = 13 +const LED Pin = D13 // ADC on the Arduino const ( - ADC0 Pin = 0 - ADC1 Pin = 1 - ADC2 Pin = 2 - ADC3 Pin = 3 - ADC4 Pin = 4 // Used by TWI for SDA - ADC5 Pin = 5 // Used by TWI for SCL + ADC0 Pin = PC0 + ADC1 Pin = PC1 + ADC2 Pin = PC2 + ADC3 Pin = PC3 + ADC4 Pin = PC4 // Used by TWI for SDA + ADC5 Pin = PC5 // Used by TWI for SCL ) // UART pins const ( - UART_TX_PIN Pin = 1 - UART_RX_PIN Pin = 0 + UART_TX_PIN Pin = PD1 + UART_RX_PIN Pin = PD0 ) diff --git a/src/machine/board_arduino_nano.go b/src/machine/board_arduino_nano.go index 1e96c95f2..d84b23f36 100644 --- a/src/machine/board_arduino_nano.go +++ b/src/machine/board_arduino_nano.go @@ -7,21 +7,39 @@ func CPUFrequency() uint32 { return 16000000 } +// Digital pins. +const ( + D0 = PD0 // RX0 + D1 = PD1 // TX1 + D2 = PD2 + D3 = PD3 + D4 = PD4 + D5 = PD5 + D6 = PD6 + D7 = PD7 + D8 = PB0 + D9 = PB1 + D10 = PB2 + D11 = PB3 + D12 = PB4 + D13 = PB5 +) + // LED on the Arduino -const LED Pin = 13 +const LED Pin = D13 // ADC on the Arduino const ( - ADC0 Pin = 0 - ADC1 Pin = 1 - ADC2 Pin = 2 - ADC3 Pin = 3 - ADC4 Pin = 4 // Used by TWI for SDA - ADC5 Pin = 5 // Used by TWI for SCL + ADC0 Pin = PC0 + ADC1 Pin = PC1 + ADC2 Pin = PC2 + ADC3 Pin = PC3 + ADC4 Pin = PC4 // Used by TWI for SDA + ADC5 Pin = PC5 // Used by TWI for SCL ) // UART pins const ( - UART_TX_PIN Pin = 1 - UART_RX_PIN Pin = 0 + UART_TX_PIN Pin = PD1 + UART_RX_PIN Pin = PD0 ) diff --git a/src/machine/board_atmega328p.go b/src/machine/board_atmega328p.go new file mode 100644 index 000000000..030c78fe9 --- /dev/null +++ b/src/machine/board_atmega328p.go @@ -0,0 +1,37 @@ +// +build avr,atmega328p arduino arduino_nano + +package machine + +const ( + // Note: start at port B because there is no port A. + portB Pin = iota * 8 + portC + portD +) + +const ( + PB0 = portB + 0 + PB1 = portB + 1 + PB2 = portB + 2 + PB3 = portB + 3 + PB4 = portB + 4 + PB5 = portB + 5 + PB6 = portB + 6 + PB7 = portB + 7 + PC0 = portC + 0 + PC1 = portC + 1 + PC2 = portC + 2 + PC3 = portC + 3 + PC4 = portC + 4 + PC5 = portC + 5 + PC6 = portC + 6 + PC7 = portC + 7 + PD0 = portD + 0 + PD1 = portD + 1 + PD2 = portD + 2 + PD3 = portD + 3 + PD4 = portD + 4 + PD5 = portD + 5 + PD6 = portD + 6 + PD7 = portD + 7 +) diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go index cac2ce6ec..147d80ffe 100644 --- a/src/machine/machine_atmega.go +++ b/src/machine/machine_atmega.go @@ -7,72 +7,6 @@ import ( "runtime/interrupt" ) -// InitPWM initializes the registers needed for PWM. -func InitPWM() { - // use waveform generation - avr.TCCR0A.SetBits(avr.TCCR0A_WGM00) - - // set timer 0 prescale factor to 64 - avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00) - - // set timer 1 prescale factor to 64 - avr.TCCR1B.SetBits(avr.TCCR1B_CS11) - - // put timer 1 in 8-bit phase correct pwm mode - avr.TCCR1A.SetBits(avr.TCCR1A_WGM10) - - // set timer 2 prescale factor to 64 - avr.TCCR2B.SetBits(avr.TCCR2B_CS22) - - // configure timer 2 for phase correct pwm (8-bit) - avr.TCCR2A.SetBits(avr.TCCR2A_WGM20) -} - -// Configure configures a PWM pin for output. -func (pwm PWM) Configure() { - if pwm.Pin < 8 { - avr.DDRD.SetBits(1 << uint8(pwm.Pin)) - } else { - avr.DDRB.SetBits(1 << uint8(pwm.Pin-8)) - } -} - -// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a -// 8-bit value ranging from 0 to 255. -func (pwm PWM) Set(value uint16) { - value8 := uint8(value >> 8) - switch pwm.Pin { - case 3: - // connect pwm to pin on timer 2, channel B - avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1) - avr.OCR2B.Set(value8) // set pwm duty - case 5: - // connect pwm to pin on timer 0, channel B - avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1) - avr.OCR0B.Set(value8) // set pwm duty - case 6: - // connect pwm to pin on timer 0, channel A - avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1) - avr.OCR0A.Set(value8) // set pwm duty - case 9: - // connect pwm to pin on timer 1, channel A - avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1) - // this is a 16-bit value, but we only currently allow the low order bits to be set - avr.OCR1AL.Set(value8) // set pwm duty - case 10: - // connect pwm to pin on timer 1, channel B - avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1) - // this is a 16-bit value, but we only currently allow the low order bits to be set - avr.OCR1BL.Set(value8) // set pwm duty - case 11: - // connect pwm to pin on timer 2, channel A - avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1) - avr.OCR2A.Set(value8) // set pwm duty - default: - panic("Invalid PWM pin") - } -} - // I2CConfig is used to store config info for I2C. type I2CConfig struct { Frequency uint32 diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go index 60c36a37c..2e7926a97 100644 --- a/src/machine/machine_atmega328p.go +++ b/src/machine/machine_atmega328p.go @@ -12,44 +12,114 @@ const irq_USART0_RX = avr.IRQ_USART_RX // Configure sets the pin to input or output. func (p Pin) Configure(config PinConfig) { if config.Mode == PinOutput { // set output bit - if p < 8 { - avr.DDRD.SetBits(1 << uint8(p)) - } else if p < 14 { - avr.DDRB.SetBits(1 << uint8(p-8)) - } else { - avr.DDRC.SetBits(1 << uint8(p-14)) + switch p / 8 { + case 0: // port B + avr.DDRB.SetBits(1 << uint8(p)) + case 1: // port C + avr.DDRC.SetBits(1 << uint8(p-8)) + case 2: // port D + avr.DDRD.SetBits(1 << uint8(p-16)) } } else { // configure input: clear output bit - if p < 8 { - avr.DDRD.ClearBits(1 << uint8(p)) - } else if p < 14 { - avr.DDRB.ClearBits(1 << uint8(p-8)) - } else { - avr.DDRC.ClearBits(1 << uint8(p-14)) + switch p / 8 { + case 0: // port B + avr.DDRB.ClearBits(1 << uint8(p)) + case 1: // port C + avr.DDRC.ClearBits(1 << uint8(p-8)) + case 2: // port D + avr.DDRD.ClearBits(1 << uint8(p-16)) } } } // Get returns the current value of a GPIO pin. func (p Pin) Get() bool { - if p < 8 { - val := avr.PIND.Get() & (1 << uint8(p)) - return (val > 0) - } else if p < 14 { - val := avr.PINB.Get() & (1 << uint8(p-8)) - return (val > 0) - } else { - val := avr.PINC.Get() & (1 << uint8(p-14)) - return (val > 0) + var val uint8 + switch p / 8 { + case 0: // port B + val = avr.PINB.Get() & (1 << uint8(p)) + case 1: // port C + val = avr.PINC.Get() & (1 << uint8(p-8)) + case 2: // port D + val = avr.PIND.Get() & (1 << uint8(p-16)) } + return val != 0 } func (p Pin) getPortMask() (*volatile.Register8, uint8) { - if p < 8 { - return avr.PORTD, 1 << uint8(p) - } else if p < 14 { - return avr.PORTB, 1 << uint8(p-8) - } else { - return avr.PORTC, 1 << uint8(p-14) + switch p / 8 { + case 0: // port B + return avr.PORTB, 1 << uint8(p) + case 1: + return avr.PORTC, 1 << uint8(p-8) + default: + return avr.PORTD, 1 << uint8(p-16) + } +} + +// InitPWM initializes the registers needed for PWM. +func InitPWM() { + // use waveform generation + avr.TCCR0A.SetBits(avr.TCCR0A_WGM00) + + // set timer 0 prescale factor to 64 + avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00) + + // set timer 1 prescale factor to 64 + avr.TCCR1B.SetBits(avr.TCCR1B_CS11) + + // put timer 1 in 8-bit phase correct pwm mode + avr.TCCR1A.SetBits(avr.TCCR1A_WGM10) + + // set timer 2 prescale factor to 64 + avr.TCCR2B.SetBits(avr.TCCR2B_CS22) + + // configure timer 2 for phase correct pwm (8-bit) + avr.TCCR2A.SetBits(avr.TCCR2A_WGM20) +} + +// Configure configures a PWM pin for output. +func (pwm PWM) Configure() { + switch pwm.Pin / 8 { + case 0: // port B + avr.DDRB.SetBits(1 << uint8(pwm.Pin)) + case 2: // port D + avr.DDRD.SetBits(1 << uint8(pwm.Pin-16)) + } +} + +// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a +// 8-bit value ranging from 0 to 255. +func (pwm PWM) Set(value uint16) { + value8 := uint8(value >> 8) + switch pwm.Pin { + case PD3: + // connect pwm to pin on timer 2, channel B + avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1) + avr.OCR2B.Set(value8) // set pwm duty + case PD5: + // connect pwm to pin on timer 0, channel B + avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1) + avr.OCR0B.Set(value8) // set pwm duty + case PD6: + // connect pwm to pin on timer 0, channel A + avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1) + avr.OCR0A.Set(value8) // set pwm duty + case PB1: + // connect pwm to pin on timer 1, channel A + avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1) + // this is a 16-bit value, but we only currently allow the low order bits to be set + avr.OCR1AL.Set(value8) // set pwm duty + case PB2: + // connect pwm to pin on timer 1, channel B + avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1) + // this is a 16-bit value, but we only currently allow the low order bits to be set + avr.OCR1BL.Set(value8) // set pwm duty + case PB3: + // connect pwm to pin on timer 2, channel A + avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1) + avr.OCR2A.Set(value8) // set pwm duty + default: + panic("Invalid PWM pin") } }