MKIMG=arm-none-eabi-objcopy
# Compiler flags
-# TODO:Cortex-m3 or Cortex-m0?
LDFLAGS+= -mthumb -mcpu=cortex-m3
ASFLAGS+= -mcpu=cortex-m3 -mthumb -g
CFLAGS+= -mcpu=cortex-m3 -mthumb -g -ffreestanding
+# Include directory
INCLUDE+= -Iinclude
BIN = bin
--- /dev/null
+# CORTEX M3 FROM SCRATCH
+
+This is my little 'Cortex M3 from scratch' project. No external libraries
+or IDEs are used. Everything is written from scratch. My development environment
+consist of nothing more than:
+
+ * GCC-arm-none (standard ARM cross compiler)
+ * VIM editor
+ * stm32flash
+
+The board I use is a cheap Chinese STM32F103 ripoff. In theory, you should be able to
+port this code to any Cortex M0/M3/M4/M7 board.
+
+## PROGRESS STATUS
+Setup bare development environment [COMPLETED]
+ FILES: Makefile, link.d
+Boot and jump to C [COMPLETED]
+ FILES: start.asm, main.c, include/sys/mmap.h, include/sys/robsys.h
+Interrupt Handling [COMPLETED]
+ FILE: ivt.c, lib/string.c
+Basic input and output (UART) [COMPLETED]
+ FILES: driver/uart.c, lib/stdio.c
+SysTick [COMPLETED]
+ FILE: systick.c
+System Info [COMPLETED]
+ FILE: sysinfo.c
+High Speed External Clock / tuning [COMPLETED]
+ FILE: clock.c
+RTC (Real Time Clock) [COMPLETED]
+ FILE: rtc.c
+Built-in-shell [COMPLETED]
+ FILE: term.c
+Port printf/libc library [COMPLETED]
+ FILE: lib/tinyprintf.c
+Basic drivers
+ EEPROM: driver/at24c.c [COMPLETED]
+ UART drivers/uart.c [COMPLETED]
+ LED segment display: drivers/tm1637.c [COMPLETED]
+ Temperature sensor: drivers/tsensor.c [IN PROGRESS]
+ OLED display [PLANNED]
+ Joystick [PLANNED]
+Memory Management [IN PROGRESS]
+ FILE: lib/pool.c
+User Mode [PLANNED]
+System Call PendV implementation [PLANNED]
+Stack trace debug [IN PROGRESS]
+Memory Protection Unit [PLANNED]
+Loadable programs from EEPROM [PLANNED]
+Multiple processes and scheduling [PLANNED]
+
+## SCREENSHOTS
+Here is a screenshot that shows the terminal just after booting:
+
+![Screenshot](https://github.com/robinkrens/cortex-from-scratch/raw/master/img/
+screenshot.png "screenshot")
+
+
+
+
+
* Initial version
*
* $DESCRIPTION$
- * 1. Routines to setup the high speed external (HSE) clock.
+ * Routines to setup the high speed external (HSE) clock.
* Initially a (less accurate) high speed internal (HSI)
* clock is used. PPL is enabled; HSE is input for PPL.
* PPL is multiplied to get the desired clock speed.
#define TIMEOUT 5000
-#define DOT true
-#define NODOT false
-
#define WRITE_CMD 0x20
/* STM32F1 microcontrollers do not provide the ability to pull-up SDA and SCL lines. Their
*
* $LOG$
* 2019/8/4 - ROBIN KRENS
- * Initial version
+ * PreInitial version
*
* $DESCRIPTION$
- * Temperature sensor
+ * Temperature sensor
+ * [in dev]
*
* */
void * update_handler() {
- s1 = false;
+/* s1 = false;
s2 = false;
ccr1 = 0xFFFFFFFF;
ccr2 = 0xFFFFFFFF;
printf("EDGE UP\n");
s1 = false;
- s2 = false;
+ s2 = false; */
}
static void reset() {
if (ch == '\n') {
while(!rchkbit(USART1_SR, 6));
- regw_u8(USART1_DR, 0x0D, 0, OWRITE); // return line
+ rwrite(USART1_DR, 0x0D); // return line
}
while(!rchkbit(USART1_SR, 6));
- regw_u8(USART1_DR, ch, 0, OWRITE);
+ rwrite(USART1_DR, ch);
}
char uart_getc(void) {
void set_baudrate() {
-// rwrite(USART1_BRR, 0x000001A1); 48 MHZ
-// rwrite(USART1_BRR, 0x0000022B); 64 MHz
-// rwrite(USART1_BRR, 0x00000138); 36 MHz
// rwrite(USART1_BRR, 0x00000271); 72 MHz
#ifdef ENABLE_HSE
- rwrite(USART1_BRR, 0x00000138);
+ rwrite(USART1_BRR, 0x00000138); // 36 MHz
#else
- rwrite(USART1_BRR, 0x00000045);
+ rwrite(USART1_BRR, 0x00000045); // 8 Mhz
#endif
}
#ifndef __TM1637_H
#define __TM1637_H
+#define DOT true
+#define NODOT false
+
/* HELPER SUBROUTINES DECLARATIONS */
static void start_condition();
static void stop_condition();
/* regfunc.h */
-extern char * regtohex(uint32_t );
-extern uint32_t hextoreg(char *);
+/* DEPRECATED extern char * regtohex(uint32_t ); */
+extern uint32_t hextoreg(char *); // TODO: scanf
extern void rsetbit(volatile uint32_t *, short);
extern void rclrbit(volatile uint32_t *, short);
extern void rsetbitsfrom(volatile uint32_t *, short, int);
extern int rchkbit(volatile uint32_t *, short);
extern void rwrite(volatile uint32_t *, uint32_t);
-extern void regw_u8(volatile uint32_t *, uint8_t, short, short);
-extern void regw_u32(volatile uint32_t *, uint32_t, short, short);
+
+/* DEPRECATED
+ * extern void regw_u8(volatile uint32_t *, uint8_t, short, short);
+ * extern void regw_u32(volatile uint32_t *, uint32_t, short, short);
+*/
* rates etc.
*/
//#define ENABLE_HSE
-//efine CRYSTAL_MHZ 8
-//efine CLKSPEED_MHZ 72
+//#define CRYSTAL_MHZ 8
+//#define CLKSPEED_MHZ 72
extern void clock_init();
// extern int clock_test();
// extern void clock_reset();
void * dummy_isr( struct interrupt_frame * frame ) {
uint8_t nr = *SCB_VTOR_ST & 0xFF;
- //printf("PC:%p\n",frame->lr);
printf("EXCEPTION: %s\n", exception_message(nr));
printf("STACK TRACE:\n");
printf("R0:%p\n",frame->r0);
* relocated to other memory locations. We can do this by setting
* a register in the NVIC called the vector table offset register */
- regw_u32(SCB_VTOR, (uint32_t) &ivt, 0, OWRITE);
+ rwrite(SCB_VTOR, (uint32_t) &ivt);
}
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/7/20 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * Helper functions to set registers
+ *
+ * */
+
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
*reg = *reg & ~(0x1 << pos);
}
+// check if a bit is set
int rchkbit(volatile uint32_t * reg, short pos) {
if ((*reg >> pos) & 0x1)
return 1;
}
-/* write value (uint8_t) to register */
+/* DEPRECATED write value (uint8_t) to register
void regw_u8(volatile uint32_t * reg, uint8_t val, short shift, short flag) {
switch(flag) {
*reg = *reg & ~(val << shift);
break;
}
-}
+} */
-/* write value (uint32_t) to register */
+/* DEPRECATED write value (uint32_t) to register
void regw_u32(volatile uint32_t * reg, uint32_t val, short shift, short flag) {
switch(flag) {
*reg = *reg & ~(val << shift);
break;
}
-}
-
-/* Print out the hexidecimal representation of an integer
- After implementation of scanf or sth this will be obsolete. */
+} */
+/* Deprecated use printf instead
char hexbuf[8];
char * regtohex(uint32_t addr) {
char tmpbuf[6] = {'A', 'B', 'C', 'D', 'E', 'F'};
}
}
return &hexbuf[0];
-}
+} */
+
+// TODO: implement simple scanf functions
int singlehextoreg(char hex) {
int conv = 0;
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/7/23 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * The 'classic' putc and getchar functions. Should not be used directly
+ * use the tinyprintf library instead
+ *
+ * Can be extended for multiple interfaces (serial, tft or oled screens)
+ *
+ */
+
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/7/20 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * Re-implementation of POSIX String functions
+ *
+ */
+
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <lib/string.h>
-// TODO: add more
void *memcpy(void *dest, void *src, size_t count)
{
const char *sp = (const char *)src;
-/* */
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/7/20 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * Linker file for Cortex-M3 STM32 based boards
+ * Boards have similar FLASH and SRAM ORIGINs
+ * LENGTHs differs of course.
+ *
+ * _start flag is the first procedure to be
+ * executed (linked to beginning of FLASH at
+ * 0x08000000). The procedure should do some
+ * basic things, such as set up the stack and
+ * reset and hard fault handler (see start.asm)
+ * *
+ * _endofbss flag is used to calculate the end
+ * of .bss and the start of (a possible) kernel
+ * heap
+ *
+ * */
+
MEMORY
{
FLASH (xr) : ORIGIN = 0x08000000, LENGTH = 512K
* Initial version
*
* $DESCRIPTION$
- * Main intialize basic components of the boards
+ * Main initialize basic components of the board
* and jumps to a terminal
*
* */
#include <drivers/uart.h>
#include <drivers/led.h>
#include <drivers/tm1637.h>
-#include <drivers/at24c.h>
+//#include <drivers/at24c.h>
#include <drivers/tsensor.h>
-//void sleep() {
-//
-// __asm__ __volatile__("wfe");
-//
-//}
-
void main()
{
+
+ /* Initialize the clock system, */
clock_init();
+
+ /* Setup the interrupt vector table */
ivt_init();
+
+ /* Initialze basic input and output over serial */
uart_init();
-// cputs("ROBSYS LOADING...\n");
- //systick_init();
-// tsensor_output(0xFFFF, 0x7FFF);
+ /* Cortex M* integrated systick, can be replaced
+ * by the more accurate RTC.
+ systick_init();
+ */
+
+ /* Set up a very small libc library */
init_printf(NULL, putc);
- // SPEED_TEST
-/* cputs("START TEST (8MHz) \n");
- int a;
- for (int i = 0; i < 20000000; i++) {
- a + 2;
- }
- a = 0;
- cputs("END TEST\n");
-
- //!
- clock_init();
- cputs("START TEST (??MHz) \n");
- for (int i = 0; i < 20000000; i++) {
- a + 2;
- }
- cputs("END TEST\n"); */
+ /* Display some basic info at startup */
sysinfo();
-
-// tsensor_input(5000);
-// run();
-
+ /* On board LEDs*/
led_init();
-// eeprom_at24c_init();
-// eeprom_test();
- tm1637_init();
+ /* Real time clock */
rtc_init();
+ /* Eeprom Driver
+ eeprom_at24c_init();
+ eeprom_test();
+ */
+
+ /* LED Segment Driver */
+ tm1637_init();
- //uint32_t test = hextoreg("12345678");
-
-// cputs(regtohex(test));
-
- //extern void stub();
- //stub();
- //__asm__ __volatile__ ("ldc p1, cr1, r0");
-
-/* while(1) {
- int r;
- for (int i = 0; i < 50000; i++) {
- r = 0;
- }
- led_on();
- for (int i = 0; i < 50000; i++) {
- r = 0;
- }
- led_off();
- } */
+ /* Start up terminal */
terminal();
+ /* Should not be here, endless loop */
for(;;) {
}
grid2 = ((cntvalue % 1000) - grid0 - grid1) / 100;
grid3 = ((cntvalue % 10000) - grid0 - grid1 - grid2) / 1000;
- printf("%d, %d, %d, %d\n", grid0, grid1, grid2, grid3);
-
+ //printf("%d, %d, %d, %d\n", grid0, grid1, grid2, grid3);
char current[4] = { dn[grid3], dn[grid2], dn[grid1], dn[grid0] };
- //char current[4] = { dn[1], dn[1], dn[1], dn[1] };
for (int i = 0; i < 4; i++) {
set_grid(i, current[i], false);
}
set_display(true, 0);
-
}
// Simple LED blink
* Initial version
*
* $DESCRIPTION$
- *
- * */
+ */
+
+/* _start sets up the stack and jumps to the reset vector */
+
.equ STACK_TOP, 0x20010000 /* placed at 64kB, top of SRAM */
.text
.global _start
hardfault:
b hardfault
-.global stub
-stub:
- ldr R0,=10
- mov R1,#0
- ldc2 11, cr0, [r1, #4]
- udiv.w R2, R0, R1
-
- .data
- .word 'x'
+
.end
*
* $LOG$
* 2019/7/20 - ROBIN KRENS
- * Initial version
+ * Initial version
+ * Display some system information, calculate
+ * the amount of SRAM available
*
* */
uint32_t current_stack = get_msp();
uint32_t stack_usage = (SRAM_OFFSET + SRAM_SIZE) - current_stack;
- uint32_t data_bss = &_endofbss - SRAM_OFFSET;
+ uint32_t data_bss = (uint32_t) &_endofbss - SRAM_OFFSET;
uint32_t mem_free = SRAM_SIZE - stack_usage - data_bss;
printf("# TOTAL MEMORY: %#x\n", SRAM_SIZE);
-// cputs(regtohex(SRAM_SIZE));
-// cputchar('\n');
printf("# FREE MEMORY: %#x\n", mem_free);
-// cputs(regtohex(mem_free));
-// cputchar('\n');
printf("# STACK USAGE: %#x\n", stack_usage);
-// cputs(regtohex(stack_usage));
-// cputchar('\n');
}
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/8/14 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * SysTick of Cortex M* MCUs. Have a look at the more complex RTC
+ * in case more accurate timing is needed.
+ *
+ *
+ * */
+
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/mmap.h>
#include <lib/regfunc.h>
-#include <lib/stdio.h>
+#include <lib/tinyprintf.h>
struct interrupt_frame {
//__attribute__ ((interrupt))
void * systick_handler(/* struct interrupt_frame * frame */) {
-// cputs("TICKING\n");
-// for(;;);
+ printf("Ticking...\n");
}
void systick_init() {
/* Every time the counter counts down to zero
- * a systick exception is asserted. Systick has
+ * a systick exception is invoked. Systick has
* exception number 15. in the vector table */
ivt_set_gate(15, systick_handler, 0);
+/* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
+ *
+ * $LOG$
+ * 2019/8/14 - ROBIN KRENS
+ * Initial version
+ *
+ * $DESCRIPTION$
+ * Small terminal with some built-in debug commands
+ *
+ * */
+
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define WHITESPACE "\t\r\n "
#define BUILTINCMDS 4
-int help(int, char**);
-
/*
* Built in commands
* info -- shows basic info of system
* uptime -- uptime; read from the RTC register
* reset -- software reset TODO
- * show [ADDRESS-ADDRESS] -- shows SRAM range
- * switchmode -- switch to unprivileged mode
+ * showmem xxxxxxxx -- shows address value
+ * led -- led on/off
+ * switchmode -- switch to unprivileged mode TODO
* */
static char buf[BUFSIZE];
int (*function)(int argc, char ** argsv);
};
-struct cmd builtincmds[4];
+struct cmd builtincmds[BUILTINCMDS];
int info(int argc, char ** argsv) {
sysinfo();
}
int uptime(int arg, char ** argsv) {
- //cputs("CURRENT UPTIME: ");
- //cputs(regtohex(*RTC_CNTL));
- //cputchar('\n');
- printf("CURRENT UPTIME: %p\n", *RTC_CNTL);
+ printf("CURRENT UPTIME: %d seconds \n", *RTC_CNTL);
}
int led(int argc, char ** argsv) {
-
if (argsv[1] != NULL) {
if (strcmp(argsv[1], "on")) {
- cputs("LED ON\n");
+ printf("LED ON\n");
led_on();
}
else if (strcmp(argsv[1], "off")) {
- cputs("LED OFF\n");
+ printf("LED OFF\n");
led_off();
}
}
return 0;
}
-int show(int argc, char ** argsv) {
+int showmem(int argc, char ** argsv) {
if ((argsv[1] != NULL) && (strlen(argsv[1]) == 8)) {
uint32_t * check = (uint32_t *) hextoreg(argsv[1]);
- cputs("REGISTER 0x");
- cputs(argsv[1]);
- cputs(" VALUE: ");
- cputs(regtohex(*check));
- cputchar('\n');
+ printf("LOCATION 0x%s, VALUE: %#x\n", argsv[1], *check);
return 1;
}
// save and scan past next arg
if (argc == MAXARGS-1) {
- cputs("Too many arguments");
+ printf("Too many arguments\n");
return 0;
}
argv[argc++] = buf;
if (strcmp(argv[0], builtincmds[i].name))
return builtincmds[i].function(argc, argv);
}
- cputs("Unknown command");
+ printf("Unknown command\n");
return 0;
}
builtincmds[1].name = "led";
builtincmds[1].function = led;
- builtincmds[2].name = "show";
- builtincmds[2].function = show;
+ builtincmds[2].name = "showmem";
+ builtincmds[2].function = showmem;
builtincmds[3].name = "uptime";
builtincmds[3].function = uptime;
char *buf;
- //cputs("WELCOME TO ROBSYS!\n");
while (1) {
buf = readline("root# ");