diff options
Diffstat (limited to 'kernel/mcu')
-rw-r--r-- | kernel/mcu/atmega2560/clock.c | 37 | ||||
-rw-r--r-- | kernel/mcu/atmega2560/context.c | 98 | ||||
-rw-r--r-- | kernel/mcu/atmega2560/include/mcu/context.h | 123 | ||||
-rw-r--r-- | kernel/mcu/atmega2560/include/mcu/usart.h | 12 | ||||
-rw-r--r-- | kernel/mcu/atmega2560/usart.c | 148 |
5 files changed, 418 insertions, 0 deletions
diff --git a/kernel/mcu/atmega2560/clock.c b/kernel/mcu/atmega2560/clock.c new file mode 100644 index 0000000..4f53372 --- /dev/null +++ b/kernel/mcu/atmega2560/clock.c @@ -0,0 +1,37 @@ +#include <avr/interrupt.h> +#include "mux/clock.h" +#include "mux/sched.h" +#include "mcu/context.h" + +static void (*on_tick)(); + +void clock_init(int ms, void (*tick)()) { + TCCR3A = 0; + TCCR3B = 0; + TCCR3C = 0; + + TCCR3B = (1 << WGM32); // turn on CTC mode: + TCCR3B |= (1 << CS32) | (0 << CS31) | (1 << CS30); // set to 1024 prescaler + + unsigned long int fcpu = (unsigned long int) F_CPU; + unsigned long int hz = 1000l / ((unsigned long int) ms); + + unsigned long int hz_counter = fcpu / (1024l * (hz)) - 1; + OCR3A = hz_counter; + on_tick = tick; +} + +void clock_start() { + TIMSK3 |= (1 << OCIE3A); +} + +void clock_stop() { + TIMSK3 &= ~(1 << OCIE3A); +} + +ISR(TIMER3_COMPA_vect, ISR_NAKED) { + context_save(); + on_tick(); + context_restore(); + reti(); +} diff --git a/kernel/mcu/atmega2560/context.c b/kernel/mcu/atmega2560/context.c new file mode 100644 index 0000000..137a985 --- /dev/null +++ b/kernel/mcu/atmega2560/context.c @@ -0,0 +1,98 @@ +#include "mcu/context.h" + +char* stack_init(const char* const mem_low, const char* const mem_high, void (*entry)(char), char args) { + char* sp = (char*) mem_high; + unsigned long address = (unsigned long) entry; + + // pattern for debugging purposes + *sp = (char) 0x1; + sp--; + *sp = (char) 0x2; + sp--; + *sp = (char) 0x1; + sp--; + *sp = (char) 0x2; + sp--; + + // put return address on stack + *sp = (char) ( address & (unsigned short) 0x00ff ); + sp--; + + *sp = (char) ( (address >> 8) & ( unsigned short ) 0x00ff ); + sp--; + + *sp = (char) ( (address >> 16) & ( unsigned short ) 0x00ff ); + sp--; + + // save registers + *sp = (char) 0x00; //r0 + sp--; + *sp = (char) 0x80; //SREG, enable interrupts when task starts + sp--; + *sp = ( char ) 0x00; //r1 + sp--; + *sp = ( char ) 0x00; //r2 + sp--; + *sp = ( char ) 0x00; //r3 + sp--; + *sp = ( char ) 0x00; //r4 + sp--; + *sp = ( char ) 0x00; //r5 + sp--; + *sp = ( char ) 0x00; //r6 + sp--; + *sp = ( char ) 0x00; //r7 + sp--; + *sp = ( char ) 0x00; //r8 + sp--; + *sp = ( char ) 0x00; //r9 + sp--; + *sp = ( char ) 0x00; //r10 + sp--; + *sp = ( char ) 0x00; //r11 + sp--; + *sp = ( char ) 0x00; //r12 + sp--; + *sp = ( char ) 0x00; //r13 + sp--; + *sp = ( char ) 0x00; //r14 + sp--; + *sp = ( char ) 0x00; //r15 + sp--; + *sp = ( char ) 0x00; //r16 + sp--; + *sp = ( char ) 0x00; //r17 + sp--; + *sp = ( char ) 0x00; //r18 + sp--; + *sp = ( char ) 0x00; //r19 + sp--; + *sp = ( char ) 0x00; //r20 + sp--; + *sp = ( char ) 0x00; //r21 + sp--; + *sp = ( char ) 0x00; //r22 + sp--; + *sp = ( char ) 0x00; //r23 + sp--; + + *sp = (char) ( args ); //place first argument in register 24 + sp--; + + *sp = ( char ) 0x00; //r25 + sp--; + *sp = ( char ) 0x00; //r26 + sp--; + *sp = ( char ) 0x00; //r27 + sp--; + *sp = ( char ) 0x00; //r28 + sp--; + *sp = ( char ) 0x00; //r29 + sp--; + *sp = ( char ) 0x00; //r30 + sp--; + *sp = ( char ) 0x00; //r31 + sp--; + + return sp; +} diff --git a/kernel/mcu/atmega2560/include/mcu/context.h b/kernel/mcu/atmega2560/include/mcu/context.h new file mode 100644 index 0000000..5f6e947 --- /dev/null +++ b/kernel/mcu/atmega2560/include/mcu/context.h @@ -0,0 +1,123 @@ +#ifndef MCU_CONTEXT_H +#define MCU_CONTEXT_H + +#include <avr/interrupt.h> +#define ret() asm volatile ( "ret" ) + +/** Default stack size, you may use this definition for declaring tasks. */ +#define DEFAULT_STACK_SIZE 256 + +/** Stack size to be allocated for the idle task. */ +#define IDLE_STACK_SIZE 64 + +/* + * The macros save_context(), restore_context() as well as the code contained in + * init_stack is adapted from the FreeRTOS kernel (http://www.freertos.org/). + * Here by copyright, credits attributed to wherever they belong. + */ + +/** + * Save context to memory location specified by first two chars of + * a symbol named 'current' (this is why 'sp' has to be the first element of + * of task control blocks). After executing this macro, the stack pointer will + * be set to the address contained in 'kstack'. + */ +#define context_save() \ + asm volatile ( \ + "push r0 \n\t" \ + "in r0, __SREG__ \n\t" \ + "cli \n\t" \ + "push r0 \n\t" \ + "push r1 \n\t" \ + "clr r1 \n\t" \ + "push r2 \n\t" \ + "push r3 \n\t" \ + "push r4 \n\t" \ + "push r5 \n\t" \ + "push r6 \n\t" \ + "push r7 \n\t" \ + "push r8 \n\t" \ + "push r9 \n\t" \ + "push r10 \n\t" \ + "push r11 \n\t" \ + "push r12 \n\t" \ + "push r13 \n\t" \ + "push r14 \n\t" \ + "push r15 \n\t" \ + "push r16 \n\t" \ + "push r17 \n\t" \ + "push r18 \n\t" \ + "push r19 \n\t" \ + "push r20 \n\t" \ + "push r21 \n\t" \ + "push r22 \n\t" \ + "push r23 \n\t" \ + "push r24 \n\t" \ + "push r25 \n\t" \ + "push r26 \n\t" \ + "push r27 \n\t" \ + "push r28 \n\t" \ + "push r29 \n\t" \ + "push r30 \n\t" \ + "push r31 \n\t" \ + "lds r26, current \n\t" \ + "lds r27, current +1 \n\t" \ + "in r0, __SP_L__ \n\t" \ + "st x+, r0 \n\t" \ + "in r0, __SP_H__ \n\t" \ + "st x+, r0 \n\t" \ + ) + + +/** + * Restore context to memory location specified by first two chars of + * a symbol named 'current'. + */ +#define context_restore() \ + asm volatile ( \ + "lds r26, current \n\t" \ + "lds r27, current + 1 \n\t" \ + "ld r28, x+ \n\t" \ + "out __SP_L__, r28 \n\t" \ + "ld r29, x+ \n\t" \ + "out __SP_H__, r29 \n\t" \ + "pop r31 \n\t" \ + "pop r30 \n\t" \ + "pop r29 \n\t" \ + "pop r28 \n\t" \ + "pop r27 \n\t" \ + "pop r26 \n\t" \ + "pop r25 \n\t" \ + "pop r24 \n\t" \ + "pop r23 \n\t" \ + "pop r22 \n\t" \ + "pop r21 \n\t" \ + "pop r20 \n\t" \ + "pop r19 \n\t" \ + "pop r18 \n\t" \ + "pop r17 \n\t" \ + "pop r16 \n\t" \ + "pop r15 \n\t" \ + "pop r14 \n\t" \ + "pop r13 \n\t" \ + "pop r12 \n\t" \ + "pop r11 \n\t" \ + "pop r10 \n\t" \ + "pop r9 \n\t" \ + "pop r8 \n\t" \ + "pop r7 \n\t" \ + "pop r6 \n\t" \ + "pop r5 \n\t" \ + "pop r4 \n\t" \ + "pop r3 \n\t" \ + "pop r2 \n\t" \ + "pop r1 \n\t" \ + "pop r0 \n\t" \ + "out __SREG__, r0 \n\t" \ + "pop r0 \n\t" \ + ) + +/** Initialize the given memory addresses to contain a valid, initial stackframe. */ +char* stack_init(const char* const mem_low, const char* const mem_high, void (*entry)(char), char args); + +#endif diff --git a/kernel/mcu/atmega2560/include/mcu/usart.h b/kernel/mcu/atmega2560/include/mcu/usart.h new file mode 100644 index 0000000..e06f0cf --- /dev/null +++ b/kernel/mcu/atmega2560/include/mcu/usart.h @@ -0,0 +1,12 @@ +#ifndef MCU_USART +#define MCU_USART + +#include "mux/io.h" + +#define USART_BUFFER_SIZE 64 +#define IOCTL_SET_BAUD 0 + +extern struct file usart0; + + +#endif
\ No newline at end of file diff --git a/kernel/mcu/atmega2560/usart.c b/kernel/mcu/atmega2560/usart.c new file mode 100644 index 0000000..f07e137 --- /dev/null +++ b/kernel/mcu/atmega2560/usart.c @@ -0,0 +1,148 @@ +#include "mux/io.h" +#include "mux/list.h" +#include "mux/rbuffer.h" +#include "mux/sched.h" +#include "mcu/usart.h" +#include "mcu/context.h" +#include <avr/interrupt.h> +#include <avr/io.h> + +struct usart_private { + char __rx_buffer[USART_BUFFER_SIZE]; + char __tx_buffer[USART_BUFFER_SIZE]; + + struct rbuffer_t rx_buffer; + struct rbuffer_t tx_buffer; + + struct list_head rx_queue; + struct list_head tx_queue; + + volatile unsigned char* const ucsrxa; + volatile unsigned char* const ubrrxh; + volatile unsigned char* const ubrrxl; + volatile unsigned char* const ucsrxb; + char u2xx; + char rxenx; + char txenx; + char rxciex; + char udriex; +}; + +int usart_open(struct file* usart) { + struct usart_private* priv = (struct usart_private*) usart->private_data; + + INIT_LIST_HEAD(&priv->rx_queue); + INIT_LIST_HEAD(&priv->tx_queue); + INIT_RBUFFER(&priv->rx_buffer, priv->__rx_buffer, USART_BUFFER_SIZE); + INIT_RBUFFER(&priv->tx_buffer, priv->__tx_buffer, USART_BUFFER_SIZE); + + return 0; +} + +int usart_ioctl(struct file* usart, int cmd, long args) { + struct usart_private* priv = (struct usart_private*) usart->private_data; + + switch (cmd) { + case IOCTL_SET_BAUD: { + long baud = args; + (*priv->ucsrxa) |= (1 << priv->u2xx); + long baud_setting = (F_CPU / 4 / baud - 1) / 2; + (*priv->ubrrxh) = baud_setting >> 8; + (*priv->ubrrxl) = baud_setting; + (*priv->ucsrxb) |= (1 << priv->rxenx) | (1 << priv->txenx) | (1 << priv->rxciex); + (*priv->ucsrxb) &= ~(1 << priv->udriex); + return 0; + } + default: + return -1; + } +} + +ssize_t usart_write(struct file* usart, const char* const buffer, size_t length) { + struct usart_private* priv = (struct usart_private*) usart->private_data; + volatile unsigned char* ucsrxb = (priv->ucsrxb); + char write_enable = (1 << priv->udriex); + + ssize_t wrote = 0; + int r = 0; + do { + cli(); + r = rbuffer_write(&priv->tx_buffer, buffer[wrote]); + wrote += 1; + sei(); + *ucsrxb |= write_enable; + } while (r == 0 && wrote < length); + return wrote; +} + +ssize_t usart_read(struct file* usart, char* const buffer, size_t length) { + struct usart_private* priv = (struct usart_private*) usart->private_data; + while (rbuffer_empty(&priv->rx_buffer)) { + sleep_queue(&priv->rx_queue); + yield(); + } + size_t read = 0; + size_t r = 0; + while (!rbuffer_empty(&priv->rx_buffer) && read < length && r == 0) { + cli(); + r = rbuffer_read(&priv->rx_buffer, buffer + read); + read += 1; + sei(); + } + return read; +} + +int usart_close(struct file* usart) { + return 0; +} + +struct file_operations usart_fops = { + .open = usart_open, + .ioctl = usart_ioctl, + .read = usart_read, + .write = usart_write, + .close = usart_close +}; + +struct usart_private _usart0 = { + .ucsrxa = &UCSR0A, + .ubrrxh = &UBRR0H, + .ubrrxl = &UBRR0L, + .ucsrxb = &UCSR0B, + .u2xx = U2X0, + .rxenx = RXEN0, + .txenx = TXEN0, + .rxciex = RXCIE0, + .udriex = UDRIE0 +}; + +struct file usart0 = { + .fops = &usart_fops, + .private_data = &_usart0 +}; + +//called when data register is empty +ISR(USART0_UDRE_vect) { + struct usart_private* priv = (struct usart_private*) usart0.private_data; + char c; + + if (rbuffer_read(&priv->tx_buffer, &c) == 0) { + UDR0 = c; + } else { + UCSR0B &= ~(1 << UDRIE0); //buffer empty, disable interruot + } +} + +//called when byte is received +ISR(USART0_RX_vect) { + //context_save(); + + struct usart_private* priv = (struct usart_private*) usart0.private_data; + + char c = UDR0; + rbuffer_write(&priv->rx_buffer, c); + wake_all_queue(&priv->rx_queue); + + //context_restore(); + //asm volatile ("reti"); +}
\ No newline at end of file |