aboutsummaryrefslogtreecommitdiff
path: root/kernel/mcu
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/mcu')
-rw-r--r--kernel/mcu/atmega2560/clock.c37
-rw-r--r--kernel/mcu/atmega2560/context.c98
-rw-r--r--kernel/mcu/atmega2560/include/mcu/context.h123
-rw-r--r--kernel/mcu/atmega2560/include/mcu/usart.h12
-rw-r--r--kernel/mcu/atmega2560/usart.c148
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