tinygo/src/machine/machine_avr.go
2018-11-16 23:08:41 +01:00

316 строки
8,3 КиБ
Go

// +build avr
package machine
import (
"device/avr"
)
type GPIOMode uint8
const (
GPIO_INPUT = iota
GPIO_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)
}
}
}
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")
}
}
// InitADC initializes the registers needed for ADC.
func InitADC() {
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
*avr.ADCSRA |= (avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
// enable a2d conversions
*avr.ADCSRA |= avr.ADCSRA_ADEN
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure() {
return // no pin specific setup on AVR machine.
}
// Get returns the current value of a ADC pin, in the range 0..0xffff. The AVR
// has an ADC of 10 bits precision so the lower 6 bits will be zero.
func (a ADC) Get() uint16 {
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits), masked to only turn on one ADC at a time.
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
// bits. This has the same effect as shifting the return value left by 6
// bits.
*avr.ADMUX = avr.RegValue(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07))
// start the conversion
*avr.ADCSRA |= avr.ADCSRA_ADSC
// ADSC is cleared when the conversion finishes
for ok := true; ok; ok = (*avr.ADCSRA & avr.ADCSRA_ADSC) > 0 {
}
low := uint16(*avr.ADCL)
high := uint16(*avr.ADCH)
return uint16(low) | uint16(high<<8)
}
// I2C on the Arduino.
type I2C struct {
}
// I2C0 is the only I2C interface on the Arduino.
var I2C0 = I2C{}
// 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)
}
// UART
var (
// UART0 is the hardware serial port on the AVR.
UART0 = &UART{}
)
// 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))
}
}