mirror of https://github.com/tinygo-org/tinygo.git
Ayke van Laethem
6 years ago
14 changed files with 421 additions and 291 deletions
@ -0,0 +1,7 @@ |
|||
// +build attiny85,digispark
|
|||
|
|||
package machine |
|||
|
|||
const ( |
|||
LED = 1 |
|||
) |
@ -0,0 +1,262 @@ |
|||
// +build avr,atmega
|
|||
|
|||
package machine |
|||
|
|||
import ( |
|||
"device/avr" |
|||
) |
|||
|
|||
// Configure sets the pin to input or output.
|
|||
func (p GPIO) Configure(config GPIOConfig) { |
|||
if config.Mode == GPIO_OUTPUT { // set output bit
|
|||
if p.Pin < 8 { |
|||
*avr.DDRD |= 1 << p.Pin |
|||
} else { |
|||
*avr.DDRB |= 1 << (p.Pin - 8) |
|||
} |
|||
} else { // configure input: clear output bit
|
|||
if p.Pin < 8 { |
|||
*avr.DDRD &^= 1 << p.Pin |
|||
} else { |
|||
*avr.DDRB &^= 1 << (p.Pin - 8) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
|||
func (p GPIO) Set(value bool) { |
|||
if value { // set bits
|
|||
if p.Pin < 8 { |
|||
*avr.PORTD |= 1 << p.Pin |
|||
} else { |
|||
*avr.PORTB |= 1 << (p.Pin - 8) |
|||
} |
|||
} else { // clear bits
|
|||
if p.Pin < 8 { |
|||
*avr.PORTD &^= 1 << p.Pin |
|||
} else { |
|||
*avr.PORTB &^= 1 << (p.Pin - 8) |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Get returns the current value of a GPIO pin.
|
|||
func (p GPIO) Get() bool { |
|||
if p.Pin < 8 { |
|||
val := *avr.PIND & (1 << p.Pin) |
|||
return (val > 0) |
|||
} else { |
|||
val := *avr.PINB & (1 << (p.Pin - 8)) |
|||
return (val > 0) |
|||
} |
|||
} |
|||
|
|||
// InitPWM initializes the registers needed for PWM.
|
|||
func InitPWM() { |
|||
// use waveform generation
|
|||
*avr.TCCR0A |= avr.TCCR0A_WGM00 |
|||
|
|||
// set timer 0 prescale factor to 64
|
|||
*avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00 |
|||
|
|||
// set timer 1 prescale factor to 64
|
|||
*avr.TCCR1B |= avr.TCCR1B_CS11 |
|||
|
|||
// put timer 1 in 8-bit phase correct pwm mode
|
|||
*avr.TCCR1A |= avr.TCCR1A_WGM10 |
|||
|
|||
// set timer 2 prescale factor to 64
|
|||
*avr.TCCR2B |= avr.TCCR2B_CS22 |
|||
|
|||
// configure timer 2 for phase correct pwm (8-bit)
|
|||
*avr.TCCR2A |= avr.TCCR2A_WGM20 |
|||
} |
|||
|
|||
// Configure configures a PWM pin for output.
|
|||
func (pwm PWM) Configure() { |
|||
if pwm.Pin < 8 { |
|||
*avr.DDRD |= 1 << pwm.Pin |
|||
} else { |
|||
*avr.DDRB |= 1 << (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 := value >> 8 |
|||
switch pwm.Pin { |
|||
case 3: |
|||
// connect pwm to pin on timer 2, channel B
|
|||
*avr.TCCR2A |= avr.TCCR2A_COM2B1 |
|||
*avr.OCR2B = avr.RegValue(value8) // set pwm duty
|
|||
case 5: |
|||
// connect pwm to pin on timer 0, channel B
|
|||
*avr.TCCR0A |= avr.TCCR0A_COM0B1 |
|||
*avr.OCR0B = avr.RegValue(value8) // set pwm duty
|
|||
case 6: |
|||
// connect pwm to pin on timer 0, channel A
|
|||
*avr.TCCR0A |= avr.TCCR0A_COM0A1 |
|||
*avr.OCR0A = avr.RegValue(value8) // set pwm duty
|
|||
case 9: |
|||
// connect pwm to pin on timer 1, channel A
|
|||
*avr.TCCR1A |= avr.TCCR1A_COM1A1 |
|||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
|||
*avr.OCR1AL = avr.RegValue(value8) // set pwm duty
|
|||
case 10: |
|||
// connect pwm to pin on timer 1, channel B
|
|||
*avr.TCCR1A |= avr.TCCR1A_COM1B1 |
|||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
|||
*avr.OCR1BL = avr.RegValue(value8) // set pwm duty
|
|||
case 11: |
|||
// connect pwm to pin on timer 2, channel A
|
|||
*avr.TCCR2A |= avr.TCCR2A_COM2A1 |
|||
*avr.OCR2A = avr.RegValue(value8) // set pwm duty
|
|||
default: |
|||
panic("Invalid PWM pin") |
|||
} |
|||
} |
|||
|
|||
// I2CConfig is used to store config info for I2C.
|
|||
type I2CConfig struct { |
|||
Frequency uint32 |
|||
} |
|||
|
|||
// Configure is intended to setup the I2C interface.
|
|||
func (i2c I2C) Configure(config I2CConfig) { |
|||
// Default I2C bus speed is 100 kHz.
|
|||
if config.Frequency == 0 { |
|||
config.Frequency = TWI_FREQ_100KHZ |
|||
} |
|||
|
|||
// Activate internal pullups for twi.
|
|||
*avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D) |
|||
|
|||
// Initialize twi prescaler and bit rate.
|
|||
*avr.TWSR |= (avr.TWSR_TWPS0 | avr.TWSR_TWPS1) |
|||
|
|||
// twi bit rate formula from atmega128 manual pg. 204:
|
|||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
|||
// NOTE: TWBR should be 10 or higher for master mode.
|
|||
// It is 72 for a 16mhz board with 100kHz TWI
|
|||
*avr.TWBR = avr.RegValue(((CPU_FREQUENCY / config.Frequency) - 16) / 2) |
|||
|
|||
// Enable twi module.
|
|||
*avr.TWCR = avr.TWCR_TWEN |
|||
} |
|||
|
|||
// Tx does a single I2C transaction at the specified address.
|
|||
// It clocks out the given address, writes the bytes in w, reads back len(r)
|
|||
// bytes and stores them in r, and generates a stop condition on the bus.
|
|||
func (i2c I2C) Tx(addr uint16, w, r []byte) error { |
|||
if len(w) != 0 { |
|||
i2c.start(uint8(addr), true) // start transmission for writing
|
|||
for _, b := range w { |
|||
i2c.writeByte(b) |
|||
} |
|||
} |
|||
if len(r) != 0 { |
|||
i2c.start(uint8(addr), false) // re-start transmission for reading
|
|||
for i := range r { // read each char
|
|||
r[i] = i2c.readByte() |
|||
} |
|||
} |
|||
if len(w) != 0 || len(r) != 0 { |
|||
// Stop the transmission after it has been started.
|
|||
i2c.stop() |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// start starts an I2C communication session.
|
|||
func (i2c I2C) start(address uint8, write bool) { |
|||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
|||
*avr.TWCR = (avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN) |
|||
|
|||
// Wait till start condition is transmitted.
|
|||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 { |
|||
} |
|||
|
|||
// Write 7-bit shifted peripheral address.
|
|||
address <<= 1 |
|||
if !write { |
|||
address |= 1 // set read flag
|
|||
} |
|||
i2c.writeByte(address) |
|||
} |
|||
|
|||
// stop ends an I2C communication session.
|
|||
func (i2c I2C) stop() { |
|||
// Send stop condition.
|
|||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO) |
|||
|
|||
// Wait for stop condition to be executed on bus.
|
|||
for (*avr.TWCR & avr.TWCR_TWSTO) == 0 { |
|||
} |
|||
} |
|||
|
|||
// writeByte writes a single byte to the I2C bus.
|
|||
func (i2c I2C) writeByte(data byte) { |
|||
// Write data to register.
|
|||
*avr.TWDR = avr.RegValue(data) |
|||
|
|||
// Clear TWI interrupt flag and enable TWI.
|
|||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT) |
|||
|
|||
// Wait till data is transmitted.
|
|||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 { |
|||
} |
|||
} |
|||
|
|||
// readByte reads a single byte from the I2C bus.
|
|||
func (i2c I2C) readByte() byte { |
|||
// Clear TWI interrupt flag and enable TWI.
|
|||
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA) |
|||
|
|||
// Wait till read request is transmitted.
|
|||
for (*avr.TWCR & avr.TWCR_TWINT) == 0 { |
|||
} |
|||
|
|||
return byte(*avr.TWDR) |
|||
} |
|||
|
|||
// Configure the UART on the AVR. Defaults to 9600 baud on Arduino.
|
|||
func (uart UART) Configure(config UARTConfig) { |
|||
if config.BaudRate == 0 { |
|||
config.BaudRate = 9600 |
|||
} |
|||
|
|||
// Set baud rate based on prescale formula from
|
|||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
|||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
|||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1) |
|||
*avr.UBRR0H = avr.RegValue(ps >> 8) |
|||
*avr.UBRR0L = avr.RegValue(ps & 0xff) |
|||
|
|||
// enable RX, TX and RX interrupt
|
|||
*avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0 |
|||
|
|||
// 8-bits data
|
|||
*avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00 |
|||
} |
|||
|
|||
// WriteByte writes a byte of data to the UART.
|
|||
func (uart UART) WriteByte(c byte) error { |
|||
// Wait until UART buffer is not busy.
|
|||
for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 { |
|||
} |
|||
*avr.UDR0 = avr.RegValue(c) // send char
|
|||
return nil |
|||
} |
|||
|
|||
//go:interrupt USART_RX_vect
|
|||
func handleUSART_RX() { |
|||
// Read register to clear it.
|
|||
data := *avr.UDR0 |
|||
|
|||
// Ensure no error.
|
|||
if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 { |
|||
// Put data from UDR register into buffer.
|
|||
bufferPut(byte(data)) |
|||
} |
|||
} |
@ -0,0 +1,48 @@ |
|||
// +build avr,attiny
|
|||
|
|||
package machine |
|||
|
|||
import ( |
|||
"device/avr" |
|||
) |
|||
|
|||
// Configure sets the pin to input or output.
|
|||
func (p GPIO) Configure(config GPIOConfig) { |
|||
if config.Mode == GPIO_OUTPUT { // set output bit
|
|||
*avr.DDRB |= 1 << p.Pin |
|||
} else { // configure input: clear output bit
|
|||
*avr.DDRB &^= 1 << p.Pin |
|||
} |
|||
} |
|||
|
|||
// Set changes the value of the GPIO pin. The pin must be configured as output.
|
|||
func (p GPIO) Set(value bool) { |
|||
if value { // set bits
|
|||
*avr.PORTB |= 1 << p.Pin |
|||
} else { // clear bits
|
|||
*avr.PORTB &^= 1 << p.Pin |
|||
} |
|||
} |
|||
|
|||
// Get returns the current value of a GPIO pin.
|
|||
func (p GPIO) Get() bool { |
|||
val := *avr.PINB & (1 << p.Pin) |
|||
return (val > 0) |
|||
} |
|||
|
|||
// Configure is a dummy implementation. UART has not been implemented for ATtiny
|
|||
// devices.
|
|||
func (uart UART) Configure(config UARTConfig) { |
|||
} |
|||
|
|||
// WriteByte is a dummy implementation. UART has not been implemented for ATtiny
|
|||
// devices.
|
|||
func (uart UART) WriteByte(c byte) error { |
|||
return nil |
|||
} |
|||
|
|||
// Tx is a dummy implementation. I2C has not been implemented for ATtiny
|
|||
// devices.
|
|||
func (i2c I2C) Tx(addr uint16, w, r []byte) error { |
|||
return nil |
|||
} |
@ -0,0 +1,35 @@ |
|||
// +build avr,atmega
|
|||
|
|||
package runtime |
|||
|
|||
import ( |
|||
"device/avr" |
|||
) |
|||
|
|||
// Sleep for a given period. The period is defined by the WDT peripheral, and is
|
|||
// on most chips (at least) 3 bits wide, in powers of two from 16ms to 2s
|
|||
// (0=16ms, 1=32ms, 2=64ms...). Note that the WDT is not very accurate: it can
|
|||
// be off by a large margin depending on temperature and supply voltage.
|
|||
//
|
|||
// TODO: disable more peripherals etc. to reduce sleep current.
|
|||
func sleepWDT(period uint8) { |
|||
// Configure WDT
|
|||
avr.Asm("cli") |
|||
avr.Asm("wdr") |
|||
// Start timed sequence.
|
|||
*avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE |
|||
// Enable WDT and set new timeout
|
|||
*avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period) |
|||
avr.Asm("sei") |
|||
|
|||
// Set sleep mode to idle and enable sleep mode.
|
|||
// Note: when using something other than idle, the UART won't work
|
|||
// correctly. This needs to be fixed, though, so we can truly sleep.
|
|||
*avr.SMCR = (0 << 1) | avr.SMCR_SE |
|||
|
|||
// go to sleep
|
|||
avr.Asm("sleep") |
|||
|
|||
// disable sleep
|
|||
*avr.SMCR = 0 |
|||
} |
@ -0,0 +1,16 @@ |
|||
// +build avr,attiny
|
|||
|
|||
package runtime |
|||
|
|||
import ( |
|||
"device/avr" |
|||
) |
|||
|
|||
func sleepWDT(period uint8) { |
|||
// TODO: use the watchdog timer instead of a busy loop.
|
|||
for i := 0x45; i != 0; i-- { |
|||
for i := 0xff; i != 0; i-- { |
|||
avr.Asm("nop") |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
{ |
|||
"build-tags": ["avr", "js", "wasm"], |
|||
"linker": "avr-gcc", |
|||
"objcopy": "avr-objcopy" |
|||
} |
@ -0,0 +1,17 @@ |
|||
{ |
|||
"inherits": ["avr"], |
|||
"llvm-target": "avr-atmel-none", |
|||
"build-tags": ["digispark", "attiny85", "attiny", "avr2", "avr25"], |
|||
"pre-link-args": [ |
|||
"-nostartfiles", |
|||
"-mmcu=attiny85", |
|||
"-Wl,--defsym=_bootloader_size=2180", |
|||
"-Wl,--defsym=_stack_size=128", |
|||
"-T", "src/device/avr/attiny85.ld", |
|||
"-T", "targets/avr.ld", |
|||
"-Wl,--gc-sections", |
|||
"targets/avr.S", |
|||
"src/device/avr/attiny85.s" |
|||
], |
|||
"flash": "micronucleus --run {hex}" |
|||
} |
Loading…
Reference in new issue