library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity Max1000TRNG is port ( ClkIn : in std_logic; Led1Out : out std_logic; Led2Out : out std_logic; Led3Out : out std_logic; Led4Out : out std_logic; Led5Out : out std_logic; Led6Out : out std_logic; Led7Out : out std_logic; Led8Out : out std_logic; Star1Out : out std_logic; Star2Out : out std_logic; Star3Out : out std_logic; Star4Out : out std_logic; Star5Out : out std_logic; Star6Out : out std_logic; Star7Out : out std_logic; Star8Out : out std_logic ); end entity; architecture A of Max1000TRNG is signal CounterD : std_logic_vector(23 downto 0); signal CounterQ : std_logic_vector(23 downto 0); signal FreeOsc1A : std_logic; signal FreeOsc1B : std_logic; signal FreeOsc1C : std_logic; signal FreeOsc1D : std_logic; signal FreeOsc1E : std_logic; signal FreeOsc2A : std_logic; signal FreeOsc2B : std_logic; signal FreeOsc2C : std_logic; signal FreeOsc2D : std_logic; signal FreeOsc2E : std_logic; attribute keep : boolean; attribute keep of FreeOsc1A : signal is true; attribute keep of FreeOsc1B : signal is true; attribute keep of FreeOsc1C : signal is true; attribute keep of FreeOsc1D : signal is true; attribute keep of FreeOsc1E : signal is true; attribute keep of FreeOsc2A : signal is true; attribute keep of FreeOsc2B : signal is true; attribute keep of FreeOsc2C : signal is true; attribute keep of FreeOsc2D : signal is true; attribute keep of FreeOsc2E : signal is true; signal RandomD : std_logic_vector(7 downto 0); signal RandomQ : std_logic_vector(7 downto 0); signal LatchD : std_logic_vector(7 downto 0); signal LatchQ : std_logic_vector(7 downto 0); begin process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then CounterQ <= CounterD; RandomQ <= RandomD; LatchQ <= LatchD; end if; end process; -- free oscillator 1 FreeOsc1A <= not(FreeOsc1E); FreeOsc1B <= not(FreeOsc1A); FreeOsc1C <= not(FreeOsc1B); FreeOsc1D <= not(FreeOsc1C); FreeOsc1E <= not(FreeOsc1D); -- free oscillator 2 FreeOsc2A <= not(FreeOsc2E); FreeOsc2B <= not(FreeOsc2A); FreeOsc2C <= not(FreeOsc2B); FreeOsc2D <= not(FreeOsc2C); FreeOsc2E <= not(FreeOsc2D); -- random bit generated from jittering between free oscillators RandomD <= RandomQ(6 downto 0) & (FreeOsc1A xor FreeOsc2A); -- latch the random number every time counter overflows CounterD <= std_logic_vector(unsigned(CounterQ) + 1); LatchD <= RandomQ when (CounterQ = std_logic_vector(to_unsigned(0, 24))) else LatchQ; Led1Out <= LatchQ(0); Led2Out <= LatchQ(1); Led3Out <= LatchQ(2); Led4Out <= LatchQ(3); Led5Out <= LatchQ(4); Led6Out <= LatchQ(5); Led7Out <= LatchQ(6); Led8Out <= LatchQ(7); Star1Out <= LatchQ(0); Star2Out <= LatchQ(1); Star3Out <= LatchQ(2); Star4Out <= LatchQ(3); Star5Out <= LatchQ(4); Star6Out <= LatchQ(5); Star7Out <= LatchQ(6); Star8Out <= LatchQ(7); end architecture;
$ ./configure --prefix=/opt/sdcc --disable-pic14-port --disable-pic16-port $ make $ make install
#include <stdint.h> __sfr __at (0xB0) P3; // led at P3.2 (https://github.com/wuxx/nanoCH55x/blob/master/hardware/nanoCH55x-v1.1.pdf) __sfr __at (0x96) P3_MOD_OC; __sfr __at (0x97) P3_DIR_PU; __sfr __at (0xB9) CLOCK_CFG; __sfr __at (0x88) TCON; __sfr __at (0x89) TMOD; __sfr __at (0x8A) TL0; __sfr __at (0x8C) TH0; __sfr __at (0xA8) IE; __sfr __at (0xA1) SAFE_MOD; __sfr __at (0xC9) T2MOD; uint16_t x; void timer0Init() { const uint16_t TIMER_0_INIT_VALUE = 65535 - 2000; T2MOD &= 0xEF; // T0_CLK=0 --> freq = Fsys / 12 = 24 MHz / 2 = 2 MHz TMOD = (TMOD & 0xF0) | 0x01; // mode 1 (16 bit timer), T0_GATE=0, T0_CT=0 TH0 = (uint8_t) (TIMER_0_INIT_VALUE >> 8); TL0 = (uint8_t) (TIMER_0_INIT_VALUE & 0x00FF); TCON = (TCON & 0xCF) | 0x10; // clear timer flag (TF0=0) and start timer (TR0=1) } void timerIsr() __interrupt (1) { // Timer 0 ISR x++; if (x == 1000) { // at 1 KHz, so each 1000 calls we toggle led to toggle it each second P3 ^= 0x04; // switch P3.2 x = 0; } timer0Init(); } int main() { // set Fsys = 24 MHz SAFE_MOD = 0x55; SAFE_MOD = 0xAA; CLOCK_CFG = (CLOCK_CFG & 0xF8) | 0x06; SAFE_MOD = 0; // configure P3.2 as push-pull output pin P3_MOD_OC &= 0xFB; P3_DIR_PU |= 0x04; // configure timer 0 at Fsys / 12 = 2 MHz: T0_CT=0, T0_CLK=0, T0_GATE=0 timer0Init(); // enable timer 0 interrupts IE |= 0x80; // global IE |= 0x02; // timer 0 x = 0; // sleep while (1) ; }
# compilamos $ /opt/sdcc/bin/sdcc -mmcs51 -c -o main.rel main.c # enlazamos $ /opt/sdcc/bin/sdcc -mmcs51 --xram-loc 0 --xram-size 1024 --code-loc 0 --code-size 14336 -o main.ihx main.rel # convertimos el fichero hex a binario (objcopy es utilidad estándar en Linux) $ objcopy -Iihex -Obinary main.ihx main.bin # tostamos el binario en el CH552 (previamente insertado mientras pulsamos el botón de bootloader) $ ch55xtool -f main.bin
$ echo 0 > /dev/loquesea # apagar el led $ echo 1 > /dev/loquesea # encender el led
__code const deviceDescriptor myDeviceDescriptor = { 18, // bLength 0x01, // bDescriptorType (device) 0x0110, // bcdUSB (USB 1.1) 0x00, // bDeviceClass 0x00, // bDeviceSubClass 0x00, // bDeviceProtocol 64, // bMaxPacketSize 0xF055, // idVendor 0x0001, // idProduct 0x0000, // bcdDevice 0x00, // iManufacturer 0x00, // iProduct 0x00, // iSerialNumber 0x01 // bNumConfigurations };
__code const myConfigurationDescriptorType myConfigurationDescriptor = { { // configuration descriptor 9, // bLength 0x02, // bDescriptorType sizeof(configurationDescriptor) + sizeof(interfaceDescriptor) + sizeof(endpointDescriptor) + sizeof(endpointDescriptor), // wTotalLength 1, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration 0x80, // bmAttributes 5 // bMaxPower (5 * 2 = 10 mA) }, { // interface descriptor 9, // bLength; 0x04, // bDescriptorType 0, // bInterfaceNumber 0, // bAlternateSetting 2, // bNumEndpoints 0xFF, // bInterfaceClass (vendor defined) 0xFF, // bInterfaceSubClass (vendor defined) 0xFF, // bInterfaceProtocol (vendor defined) 0 // iInterface }, { // endpoint descriptor (ep1 in) 7, // bLength 0x05, // bDescriptorType 0x01, // bEndpointAddress 0x02, // bmAttributes 64, // wMaxPacketSize 10 // bInterval }, { // endpoint descriptor (ep1 out) 7, // bLength 0x05, // bDescriptorType 0x81, // bEndpointAddress 0x02, // bmAttributes 64, // wMaxPacketSize 10 // bInterval } };
void usbInit() { IE_EX &= ~0x04; // disable usb interrupts USB_CTRL = 0; // device mode // endpoint 0 configuration UEP0_DMA_L = 0x00; // configure ep0 buffer address at XRAM 0x0000 UEP0_DMA_H = 0x00; UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK, IN transfer return NAK // endpoint 1 configuration UEP1_DMA_L = 0x40; // configure ep1 buffer address at XRAM 0x0040 UEP1_DMA_H = 0x00; UEP1_CTRL = 0x00; // ep1 OUT transfer return ACK, OUT transfer return ACK UEP1_T_LEN = 0; // ep1 IN transfers always tx 0 bytes UEP4_1_MOD = 0xC0; // enable ep1 IN (tx) and OUT (rx) // rest of usb device configuration USB_DEV_AD = 0x00; // initial device address = 0 UDEV_CTRL = 0x80; // disable internal DP/DM pull-down USB_CTRL = 0x29; // UC_DEV_PU_EN=1, UC_INT_BUSY=1, UC_DMA_EN=1 (0b00101001) UDEV_CTRL |= 0x01 ; // UD_PORT_EN=1 (0b00000001) USB_INT_FG = 0xFF; // clear interrupt flags USB_INT_EN = 0x03; // enable usb reset and transfer interrupts IE_EX |= 0x04; // enable usb interrupts IE |= 0x80; // enable global interrupts usbStatus = USB_STATUS_WAIT_SETUP; }
Si es un evento de reset USB: Se pone la dirección del dispositivo a 0. Se configuran de nuevo los endpoints, por si acaso. Si es un evento de transferencia USB terminada: Si es un token de SETUP y es el endpoint 0: Leemos el paquete de SETUP que estará en el buffer de recepción del endpoint 0. Si es una petición de tipo GET_DESCRIPTOR: Ponemos el descriptor que nos solicita el host en el buffer del endpoint 0. Nos preparamos para recibir un token IN. Si es una petición de tipo SET_ADDRESS: Guardamos en una variable la nueva dirección que tenemos que usar. Preparamos un paquete de respuesta de 0 bytes para el próximo token IN que llegue. Nos preparamos para recibir un token IN. Si es una petición de tipo SET_CONFIGURATION: Preparar próxima transferencia con token IN de 0 bytes a modo de ACK. Si es una petición de tipo GET_STATUS: Preparar próxima transferencia con token IN con 2 bytes a 0 para indicar status ok. Si es un token IN y es el endpoint 0: Si mandé un descriptor: Preparar próxima transferencia de tipo OUT a modo de ack desde el host. Si justo antes llegó un SET_ADDRESS: Ya se puede configurar la dirección USB del dispositivo con la que se almacenó en una variable. Preparar endpoint 0 de nuevo para que llegue otro token SETUP. Si justo antes llegó un SET_CONFIGURATION o un GET_STATUS: Preparar endpoint 0 de nuevo para que llegue otro token SETUP. Si es un token OUT y es el endpoint 0: Preparar endpoint 0 de nuevo para que llegue otro token SETUP. Si es un token IN y es el endpoint 1: Se habrán transferido 0 bytes desde el CH552 al host (PC). Nada que hacer. Si es un token OUT y es el endpoint 1: Encender o apagar el led en función del primer byte del buffer del endpoint 1.
void usbIsr() __interrupt (8) { // USB ISR if (USB_INT_FG & 0x01) { // usb reset interrupt UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK expecting DATA0, IN transfer return NAK USB_DEV_AD = 0x00; // initial device address = 0 usbStatus = USB_STATUS_WAIT_SETUP; } else if (USB_INT_FG & 0x02) { // usb transfer interrupt uint8_t token = (USB_INT_ST >> 4) & 0x03; // 0=out, 1=sof, 2=in, 3=setup uint8_t endpoint = (USB_INT_ST & 0x0F); if ((token == 3) && (endpoint == 0) && (USB_RX_LEN == 8)) { // SETUP token #define P ((setupPacket *) ep0Buffer) if ((P->bmRequestType == 0x80) && (P->bRequest == 0x06)) { // GET_DESCRIPTOR uint8_t descriptorType = P->wValue >> 8; uint8_t descriptorIndex = P->wValue & 0x00FF; if (descriptorType == 0x01) { // GET_DESCRIPTOR (device) descriptorPtr = (uint8_t *) &myDeviceDescriptor; descriptorPtrToSend = (P->wLength < sizeof(deviceDescriptor)) ? P->wLength : sizeof(deviceDescriptor); descriptorPtrNextToSend = descriptorPtrToSend; memcpy(ep0Buffer, descriptorPtr, descriptorPtrNextToSend); UEP0_T_LEN = descriptorPtrNextToSend; UEP0_CTRL = 0xC0; // UEP_R_TOG=1, UEP_T_TOG=1, UEP_R_RES=ack, UEP_T_RES=ack usbStatus = USB_STATUS_WAIT_IN_AFTER_GET_DESCRIPTOR; } else if (descriptorType == 0x02) { // GET_DESCRIPTOR (configuration) descriptorPtr = (uint8_t *) &myConfigurationDescriptor; descriptorPtrToSend = (P->wLength < sizeof(myConfigurationDescriptorType)) ? P->wLength : sizeof(myConfigurationDescriptorType); descriptorPtrNextToSend = descriptorPtrToSend; memcpy(ep0Buffer, descriptorPtr, descriptorPtrNextToSend); UEP0_T_LEN = descriptorPtrNextToSend; UEP0_CTRL = 0xC0; // UEP_R_TOG=1, UEP_T_TOG=1, UEP_R_RES=ack, UEP_T_RES=ack usbStatus = USB_STATUS_WAIT_IN_AFTER_GET_DESCRIPTOR; } else { // } } else if ((P->bmRequestType == 0x00) && (P->bRequest == 0x05)) { // SET_ADDRESS addressToUse = (P->wValue & 0x00FF); // response with a DATA1 with 0 length UEP0_T_LEN = 0; UEP0_CTRL = 0x40; // next IN transfer (response) with DATA1 usbStatus = USB_STATUS_WAIT_IN_AFTER_SET_ADDRESS; } else if ((P->bmRequestType == 0x00) && (P->bRequest == 0x09)) { // SET_CONFIGURATION // response with a DATA1 with 0 length UEP0_T_LEN = 0; UEP0_CTRL = 0x40; // next IN transfer (response) with DATA1 usbStatus = USB_STATUS_WAIT_IN_AFTER_SET_CONFIGURATION; } else if (((P->bmRequestType & 0x80) == 0x80) && (P->bRequest == 0x00)) { // GET_STATUS ep0Buffer[0] = ep0Buffer[1] = 0; // always return status ok UEP0_T_LEN = 2; // status word = 2 bytes UEP0_CTRL = 0xC0; // UEP_R_TOG=1, UEP_T_TOG=1, UEP_R_RES=ack, UEP_T_RES=ack usbStatus = USB_STATUS_WAIT_IN_AFTER_GET_STATUS; } } else if ((token == 2) && (endpoint == 0)) { // IN token if (usbStatus == USB_STATUS_WAIT_IN_AFTER_GET_DESCRIPTOR) { descriptorPtrToSend -= descriptorPtrNextToSend; descriptorPtr += descriptorPtrNextToSend; if (descriptorPtrToSend > 0) { descriptorPtrNextToSend = (descriptorPtrToSend > 8) ? 8 : descriptorPtrToSend; memcpy(ep0Buffer, descriptorPtr, descriptorPtrNextToSend); UEP0_T_LEN = descriptorPtrNextToSend; UEP0_CTRL ^= 0x40; // manual toggle UEP_T_TOG } else { UEP0_CTRL = 0x80; // expect OUT transfer with DATA 1 (status stage) usbStatus = USB_STATUS_WAIT_OUT_AFTER_IN_AFTER_GET_DESCRIPTOR; } } else if (usbStatus == USB_STATUS_WAIT_IN_AFTER_SET_ADDRESS) { USB_DEV_AD = (USB_DEV_AD & 0x80) | addressToUse; UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK, IN transfer return NAK usbStatus = USB_STATUS_WAIT_SETUP; } else if (usbStatus == USB_STATUS_WAIT_IN_AFTER_SET_CONFIGURATION) { P3 |= 0x04; // switch on led when device configured UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK, IN transfer return NAK usbStatus = USB_STATUS_WAIT_SETUP; } else if (usbStatus == USB_STATUS_WAIT_IN_AFTER_GET_STATUS) { UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK, IN transfer return NAK usbStatus = USB_STATUS_WAIT_SETUP; } } else if ((token == 0) && (endpoint == 0)) { // OUT token UEP0_CTRL = 0x02; // ep0 OUT transfer return ACK, expect DATA0 (SETUP), IN transfer return NAK usbStatus = USB_STATUS_WAIT_SETUP; } // data in & out at endpoint 1 else if ((token == 2) && (endpoint == 1)) { // IN token: CH552 --> USB host (always tx 0 bytes) UEP1_CTRL ^= 0x40; // manual toggle UEP_T_TOG } else if ((token == 0) && (endpoint == 1)) { // OUT token: USB host --> CH552 (if first byte is '0' then switch off led, else if '1' then switch on len) if (ep1Buffer[0] == '1') P3 |= 0x04; else if (ep1Buffer[0] == '0') P3 &= ~0x04; UEP1_CTRL ^= 0x80; // manual toggle UEP_R_TOG } } USB_INT_FG = 0xFF; }
int main() { // set Fsys = 24 MHz SAFE_MOD = 0x55; SAFE_MOD = 0xAA; CLOCK_CFG = (CLOCK_CFG & 0xF8) | 0x06; SAFE_MOD = 0; // configure P3.2 as push-pull output pin P3_MOD_OC &= 0xFB; P3_DIR_PU |= 0x04; P3 = 0; // turn off led // configure usb device usbInit(); // sleep while (1) ; }
$ journalctl -f
... feb 02 18:57:33 avelino kernel: usb 3-2: new full-speed USB device number 2 using xhci_hcd feb 02 18:57:33 avelino kernel: usb 3-2: New USB device found, idVendor=f055, idProduct=0001, bcdDevice= 0.00 feb 03 18:57:33 avelino kernel: usb 3-2: New USB device strings: Mfr=0, Product=0, SerialNumber=0 ...
$ lsusb ... Bus 003 Device 002: ID f055:0001 ...
$ modprobe usbserial vendor=0xf055 product=0x0001
$ echo 1 > /dev/ttyUSB0 # encender $ echo 0 > /dev/ttyUSB0 # apagar
__code const myConfigurationDescriptorType myConfigurationDescriptor = { { // configuration descriptor 9, // bLength 0x02, // bDescriptorType sizeof(configurationDescriptor) + sizeof(interfaceDescriptor) + sizeof(hidDescriptor) + sizeof(endpointDescriptor) + sizeof(endpointDescriptor), // wTotalLength 1, // bNumInterfaces 1, // bConfigurationValue 0, // iConfiguration 0x80, // bmAttributes 5 // bMaxPower (5 * 2 = 10 mA) }, { // interface descriptor 9, // bLength 0x04, // bDescriptorType 0, // bInterfaceNumber 0, // bAlternateSetting 2, // bNumEndpoints 0x03, // bInterfaceClass (HID) 0x00, // bInterfaceSubClass (none) 0x00, // bInterfaceProtocol (none) 0 // iInterface }, { // hid descriptor 9, // bLength 0x21, // bDescriptorType 0x0101, // bcdHID 0, // bCountryCode 1, // bNumDescriptors 0x22, // bReportDescriptorType (report) sizeof(hidReportDescriptor) // wReportDescriptorLength }, { // endpoint descriptor (ep1 in) 7, // bLength 0x05, // bDescriptorType 0x81, // bEndpointAddress 0x03, // bmAttributes (interrupt endpoint) 64, // wMaxPacketSize 10 // bInterval (max. ms between IN tokens) }, { // endpoint descriptor (ep1 out) 7, // bLength 0x05, // bDescriptorType 0x01, // bEndpointAddress 0x03, // bmAttributes (interrupt endpoint) 64, // wMaxPacketSize 10 // bInterval (max. ms between OUT tokens) } };
__code const uint8_t hidReportDescriptor[] = { // hid report descriptor for a raw 64 byte rx/tx pipe 0x06, 0xAB, 0xFF, // usage_page 0xFFAB 0x0A, 0x00, 0x02, // usage 0x0200 0xA1, 0x01, // collection 0x01 0x75, 0x08, // report size = 8 bits 0x15, 0x00, // logical minimum = 0 0x26, 0xFF, 0x00, // logical maximum = 255 0x95, 64, // report count = 64 bytes 0x09, 0x01, // usage 0x81, 0x02, // Input (array) 0x95, 64, // report count = 64 bytes 0x09, 0x02, // usage 0x91, 0x02, // Output (array) 0xC0 // end collection };
... feb 05 13:53:18 avelino kernel: usb 3-2: new full-speed USB device number 18 using xhci_hcd feb 05 13:53:18 avelino kernel: usb 3-2: New USB device found, idVendor=f055, idProduct=0001, bcdDevice= 0.00 feb 05 13:53:18 avelino kernel: usb 3-2: New USB device strings: Mfr=0, Product=0, SerialNumber=0 feb 05 13:53:23 avelino kernel: hid-generic 0003:F055:0001.0008: hiddev0,hidraw3: USB HID v1.01 Device [HID f055:0001] on usb-0000:00:14.0-2/input0 ...
$ echo 1 > /dev/hidraw3 # encender $ echo 0 > /dev/hidraw3 # apagar
#include "Stepper.H" using namespace avelino; using namespace std; void Stepper::init() { this->gpioConfigure(); this->gpioWrite(0); } void Stepper::start() { this->doStart = true; } void Stepper::stop() { this->doStart = false; } void Stepper::run() { Status localStatus = this->status; do { this->status = localStatus; if (localStatus == Status::STOPPED) { if (this->doStart) { this->gpioWrite(this->lastMask); this->timer = this->ticksPerStep; //cout << "Stepper::run: STOPPED --> RUNNING_1, timer=" << this->timer << endl; localStatus = Status::RUNNING; } } else if (localStatus == Status::RUNNING) { this->timer--; int32_t threshold = (int32_t) this->ticksPerStep - (int32_t) this->getTicksPerPulse(); if (this->timer < threshold) this->gpioWrite(0); if (this->timer <= 0) { this->lastMask = this->getNextMask(this->lastMask, this->wise); this->gpioWrite(this->lastMask); if (this->listener != NULL) this->listener->stepDone(*this); this->timer = this->ticksPerStep; //cout << "Stepper::run: RUNNING_1 --> RUNNING_2, timer=" << this->timer << endl; } if (!this->doStart) { this->gpioWrite(0); //cout << "Stepper::run: RUNNING_1 --> STOPPED" << endl; localStatus = Status::STOPPED; } } } while (localStatus != this->status); }
#include "MyStepper.H" using namespace avelino; using namespace std; #define RCU_APB2EN *((uint32_t *) 0x40021018) #define GPIOA_CTL0 *((uint32_t *) 0x40010800) #define GPIOA_OCTL *((uint32_t *) 0x4001080C) #define GPIOB_CTL0 *((uint32_t *) 0x40010C00) #define GPIOB_OCTL *((uint32_t *) 0x40010C0C) void MyStepper::gpioConfigure() const { // enable clock on ports A and B RCU_APB2EN = RCU_APB2EN | (((uint32_t) 3) << 2); // configure A5, A6 and A7 as push/pull output GPIOA_CTL0 = (GPIOA_CTL0 & 0x000FFFFF) | 0x22200000; // configure B0 as push/pull output GPIOB_CTL0 = (GPIOB_CTL0 & 0xFFFFFFF0) | 0x00000002; } void MyStepper::gpioWrite(uint32_t mask) const { if (mask & 1) GPIOA_OCTL |= (((uint32_t) 1) << 5); else GPIOA_OCTL &= ~(((uint32_t) 1) << 5); if (mask & 2) GPIOA_OCTL |= (((uint32_t) 1) << 6); else GPIOA_OCTL &= ~(((uint32_t) 1) << 6); if (mask & 4) GPIOA_OCTL |= (((uint32_t) 1) << 7); else GPIOA_OCTL &= ~(((uint32_t) 1) << 7); if (mask & 8) GPIOB_OCTL |= ((uint32_t) 1); else GPIOB_OCTL &= ~((uint32_t) 1); } uint32_t MyStepper::getNextMask(uint32_t prevMask, Wise wise) const { if (wise == Stepper::Wise::CLOCK) return ((prevMask << 1) | ((prevMask >> 3) & 1)) & 0x0F; else return ((prevMask >> 1) | ((prevMask & 1) << 3)) & 0x0F; } const uint32_t MyStepper::getStepsPerRevolution() const { return 4076; } const uint32_t MyStepper::getTicksPerPulse() const { return 20; // 20 * 10 = 200 ms }
#ifndef __STATE_H__ #define __STATE_H__ #include "Task.H" extern "C++" { namespace avelino { using namespace std; template <typename T> class State : public Task { protected: T *data; public: virtual void onLoad(T &data) = 0; virtual void run() = 0; virtual State<T> &getNextState() = 0; virtual T &onUnload() = 0; }; template <typename T> class StateManager : public Task { protected: State<T> *currentState; public: StateManager(State<T> &initialState, T &initialData) : currentState(&initialState) { this->currentState->onLoad(initialData); }; virtual void run() { if (this->currentState != NULL) { this->currentState->run(); State<T> &nextState = this->currentState->getNextState(); if (this->currentState != &nextState) { T &data = this->currentState->onUnload(); this->currentState = &nextState; this->currentState->onLoad(data); } } }; }; } } #endif // __STATE_H__
#include "Task.H" #include "Timer.H" #include "MyStepper.H" #include "MyKeypad.H" #include "MySevenSegments.H" #include "SharedData.H" #include "State.H" #include "MyIdleState.H" #include "MyConfigureStepsState.H" #include "ConfigureDaysState.H" #include "interrupt.H" #include "Heartbeat.H" using namespace avelino; using namespace std; class MyTimerListener : public TimerListener { public: Task **tasks; uint16_t numTasks; MyTimerListener(Task **tasks, uint16_t numTasks) : tasks(tasks), numTasks(numTasks) { }; virtual void timerExpired(); }; void MyTimerListener::timerExpired() { Task **t = this->tasks; for (uint16_t i = 0; i < this->numTasks; i++, t++) (*t)->run(); } int main() { interruptInit(); MyKeypad keypad; keypad.init(); MySevenSegments sevenSegments; sevenSegments.init(); MyStepper stepper; stepper.init(); Heartbeat heartbeat; // states SharedData data; MyConfigureStepsState configureStepsState(keypad, sevenSegments, stepper); ConfigureDaysState configureDaysState(keypad, sevenSegments, stepper); MyIdleState idleState(keypad, sevenSegments, stepper); // link states configureStepsState.setNextState(configureDaysState); configureDaysState.setNextState(idleState); idleState.setNextState(configureStepsState); StateManager<SharedData> stateManager(configureStepsState, data); // round robin tasks const int NUM_TASKS = 5; Task *tasks[NUM_TASKS] = { &keypad, &sevenSegments, &stepper, &heartbeat, &stateManager }; MyTimerListener myTimerListener(tasks, NUM_TASKS); Timer timer; timer.init(myTimerListener, 10_msForTimer); while (true) asm volatile ("wfi"); return 0; }
#include "Screen.H" #include "ScreenTitle.H" #include "ScreenMenu.H" #include "ScreenFileSelectionMenu.H" #include "ScreenBoardSelectionMenu.H" #include "GameStatus.H" #include "ScreenDeleteFileMenu.H" #include "ScreenConfirmStartBoardMenu.H" #include "ScreenBoard.H" #include "ScreenPauseMenu.H" using namespace std; using namespace avelino; typedef void (*fptr)(void); #define ISR_PTR *((fptr *) 0x03007FFC) #define IME *((volatile uint32_t *) 0x04000208) #define IE *((volatile uint16_t *) 0x04000200) #define IF *((volatile uint16_t *) 0x04000202) #define IFBIOS *((volatile uint16_t *) 0x03007FF8) #define DISPSTAT *((volatile uint16_t *) 0x04000004) void isr() __attribute__((target("arm"))); void isr() { // mark interrupt as served IF = 1; IFBIOS |= 1; } void enableInterrupts() { IME = 0; ISR_PTR = isr; DISPSTAT = ((uint16_t) 1) << 3; IE = 1; // v-blank IME = 1; } int main() { enableInterrupts(); GameStatus status; // define screens ScreenTitle st; ScreenFileSelectionMenu sfsm; ScreenBoardSelectionMenu sbsm; ScreenDeleteFileMenu sdfm; ScreenConfirmStartBoardMenu scsbm; ScreenBoard sb; ScreenPauseMenu spm; // link screens st.nextScreen = &sfsm; sfsm.backScreen = &st; sfsm.nextScreen = &sbsm; sbsm.backScreen = &sfsm; sbsm.deleteFileScreen = &sdfm; sbsm.boardSelectedScreen = &scsbm; sdfm.backScreen = &sbsm; sdfm.justDeletedScreen = &sfsm; scsbm.backScreen = &sbsm; scsbm.yesScreen = &sb; sb.pauseScreen = &spm; sb.afterFinishScreen = &sbsm; spm.continueScreen = &sb; spm.finishScreen = &sbsm; // start screen manager ScreenManager m; m.init(st, status); while (true) { asm volatile ("swi 0x05"); // wait for v-blank m.onVBlank(); } }
// Screen.H class InterScreenData { // any data }; class Screen { public: virtual void onLoad(InterScreenData *dataFromPreviousScreen) = 0; virtual Screen *onVBlank(Keypad &keypad) = 0; // return "this" to stay on same screen or other screen to switch to virtual InterScreenData *onUnload() = 0; }; class ScreenManager { public: Screen *currentScreen; Keypad keypad; ScreenManager() : currentScreen(NULL) { }; void init(Screen &initialScreen, InterScreenData &initialInterScreenData); void onVBlank(); }; // Screen.cc void ScreenManager::init(Screen &initialScreen, InterScreenData &initialInterScreenData) { this->keypad.init(); this->currentScreen = &initialScreen; // start with first screen this->currentScreen->onLoad(&initialInterScreenData); } void ScreenManager::onVBlank() { this->keypad.onVBlank(); Screen *next = this->currentScreen->onVBlank(this->keypad); if (next != this->currentScreen) { // switch to another screen InterScreenData *isd = this->currentScreen->onUnload(); // get data from outgoing screen next->onLoad(isd); // and passing it to incomming screen this->currentScreen = next; } }
class InterScreenData { // any data }; class Screen { public: virtual void onLoad(InterScreenData *dataFromPreviousScreen) = 0; virtual Screen *onVBlank(Keypad &keypad) = 0; // return "this" to stay on same screen or other screen to switch to virtual InterScreenData *onUnload() = 0; };
void Screen::onLoad(InterScreenData *dataFromPreviousScreen)
Screen *Screen:onVBlank(Keypad &keypad)
InterScreenData *Screen::onUnload()
void ScreenTitle::print(const char *text) { char *p = (char *) text; // calculate string length uint16_t n = 0; while (*p != 0) { p++; n++; } this->objUsed = n; // draw sprites as text const int16_t Y = 100; int16_t x = 120 - ((n << 3) >> 1); uint16_t i = 0; p = (char *) text; while (*p != 0) { OAM[i++] = Y | (((uint16_t) 1) << 13); OAM[i++] = x & 0x01FF; OAM[i++] = 512 + ((*p - ' ') << 1); i++; x += 8; p++; } } void ScreenTitle::onLoad(InterScreenData *dataFromPreviousScreen) { this->interScreenData = dataFromPreviousScreen; this->alpha = 0; this->objUsed = 0; this->timer = 5 * 60; // 5 seconds // force blank to access VRAM DISPCNT = ((uint16_t) 1) << 7; // draw on framebuffer //memcpy16(MODE3_VRAM_FB, ANA, 240 * 160); memcpy16(MODE3_VRAM_FB, TITLE_RGB_IMAGE, 240 * 160); // configure OBJ 256 color palette (all colors white) memcpy16(OBJ_PALETTE, PALETTE, 256); // load OBJ tiles from FONT_1 memcpy16((uint16_t *) OBJ_TILES_VRAM, (uint16_t *) FONT_1, 3072); // configure OBJs to print a text (this call updates this->objUsed to free the sprites on unload this->print("- Press START -"); // 1st target is OBJ, 2nd target is BG2, fx = alpha blending BLDCNT = (((uint16_t) 1) << 4) | (((uint16_t) 1) << 6) | (((uint16_t) 1) << 10); // transparency at 50% (8/16 for both OBJ and BG2 intensities) BLDALPHA = (((uint16_t) this->alpha) << 8) | ((uint16_t) (16 - this->alpha)); // enable mode 3 with background layer 2, enable obj, enable obj 1-d mapping DISPCNT = ((uint16_t) 3) | (((uint16_t) 1) << 10) | (((uint16_t) 1) << 12) | (((uint16_t) 1) << 6); }
Screen *ScreenTitle::onVBlank(Keypad &keypad) { // recalculate sprite alpha blending this->alpha += ALPHA_INC; uint16_t alphaIntegerPart = this->alpha >> 8; if (alphaIntegerPart > 16) { this->alpha = 0; alphaIntegerPart = 0; } BLDALPHA = (((uint16_t) alphaIntegerPart) << 8) | ((uint16_t) (16 - alphaIntegerPart)); // next screen when pressing START, else keep screen if (keypad.mask & KEY_START) return this->nextScreen; else return this; }
InterScreenData *ScreenTitle::onUnload() { // free the sprites (OBJs) used uint16_t n = this->objUsed; uint16_t j = 0; while (n > 0) { OAM[j] = SCREEN_HEIGHT | (((uint16_t) 1) << 13); OAM[j + 1] = SCREEN_WIDTH & 0x01FF; j += 4; n--; } // disable alpha blending BLDCNT = 0; return this->interScreenData; }
__attribute__((section(".cartridge_header"))) const uint8_t CARTRIDGE_HEADER[256] = { 0x3E, 0x00, 0x00, 0xEA, // branch to +0x100 0b 11101010 00000000 00000000 00111110 --> 0xEA 0x00 0x00 0x3E 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, // nintendo logo 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, 0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85, 0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff, 0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63, 0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07, 'E', 'J', 'E', 'M', 'P', 'L', 'O', 0x00, 0x00, 0x00, 0x00, 0x00, // game title 'A', 'E', 'J', 'S', // game code 'A' + two characters + 'S' (for spanish) '0', '1', // maker code 0x96, // fixed value 0x00, // GBA 0x00, // device type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, // software version 0xC1, // complement check 0x00, 0x00, // reserved 'S', 'R', 'A', 'M', '_', 'V', '1', '1', '3', 0x00, 0x00, 0x00 // backup id string after official header to indicate the backup method (SRAM at 0x0E000000) };
# primero generamos un fichero binario con la forma del nivel, # indicando las celdas transitables con 0x0000 y las celdas no transitables con 0x0080 (nótese que los valores son little-endian) # 9x9 valores de 16 bits = 162 bytes echo “0000 0000 8000 8000 0000 0000 0000 0000 0000” | xxd -r -ps > board1.bin echo “0000 0000 8000 8000 0000 0000 0000 0000 0000” | xxd -r -ps >> board1.bin echo “0000 0000 0000 0000 0000 0000 0000 0000 0000” | xxd -r -ps >> board1.bin echo “0000 0000 0000 0000 0000 0000 0000 0000 0000” | xxd -r -ps >> board1.bin echo “0000 0000 0000 0000 0000 0000 0000 0000 8000” | xxd -r -ps >> board1.bin echo “0000 0000 8000 0000 0000 0000 0000 0000 8000” | xxd -r -ps >> board1.bin echo “0000 0000 8000 0000 0000 0000 0000 0000 8000” | xxd -r -ps >> board1.bin echo “0000 0000 8000 8000 8000 0000 0000 8000 8000” | xxd -r -ps >> board1.bin echo “0000 0000 8000 8000 8000 0000 0000 8000 8000” | xxd -r -ps >> board1.bin # luego decidimos los bloques que queremos generar con una máscara de bits: el bloque rojo vale 1, el verde 2, el azul 4 y el amarillo 8 # pasando un 7 significa que queremos generar un nivel que use los bloques rojo, verde y azul # el tercer parámetro es el número de iteraciones máximas ./level_generator board1.bin 7 2000
Offset (hex) Longitud (dec) Descripción 0000 4 Código ARM de 32 bits que se ejecuta al arrancar el cartucho. 0004 156 156 bytes fijos que contienen en logo de Nintendo comprimido. Siempre son los mismos bytes. 00A0 12 Nombre del juego (ASCII). 00AC 4 Código del juego (ASCII). 00B0 2 Código del fabricante (ASCII). 00B2 1 Valor fijo (96h). 00B3 1 Código del dispositivo (00h). 00B4 1 Tipo de dispositivo (00h). 00B5 7 Reservado (todo a 00h). 00BC 1 Versión del software (lo que queramos aquí). 00BD 1 Checksum calculado a partir de los bytes A0 a BC (ambos inclusive). 00BE 2 Reservado (todo a 00h) 00C0 n Resto de la ROM (el código y los datos)
Inicio Fin Tamaño Descipción 00000000 00003FFF 16 Kbytes ROM BIOS 02000000 0203FFFF 256 Kbytes WRAM (lenta) 03000000 03007FFF 32 Kbytes WRAM (rápida) 04000000 040003FE Registros E/S 05000000 050003FF 1 Kbyte Paletas LCD 06000000 06017FFF 96 Kbytes VRAM LCD 07000000 070003FF 1 Kbyte Sprites LCD 08000000 09FFFFFF 32 Mbytes ROM/Flash cartucho 0A000000 0BFFFFFF 32 Mbytes ROM/Flash cartucho 0C000000 0DFFFFFF 32 Mbytes ROM/Flash cartucho 0E000000 0E00FFFF 64 Kbytes RAM cartucho
int32_t chk = 0; for (int n = 0xA0; n <= 0xBC; n++) chk = chk - headerData[n]; chk = (chk - 0x19) & 0xFF;
const uint8_t CARTRIDGE_HEADER[256] = { 0x3E, 0x00, 0x00, 0xEA, // branch to +0x100 0b 11101010 00000000 00000000 00111110 --> 0xEA 0x00 0x00 0x3E 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, // nintendo logo 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, 0x40, 0xa7, 0x0e, 0xfd, 0xff, 0x52, 0xfe, 0x03, 0x6f, 0x95, 0x30, 0xf1, 0x97, 0xfb, 0xc0, 0x85, 0x60, 0xd6, 0x80, 0x25, 0xa9, 0x63, 0xbe, 0x03, 0x01, 0x4e, 0x38, 0xe2, 0xf9, 0xa2, 0x34, 0xff, 0xbb, 0x3e, 0x03, 0x44, 0x78, 0x00, 0x90, 0xcb, 0x88, 0x11, 0x3a, 0x94, 0x65, 0xc0, 0x7c, 0x63, 0x87, 0xf0, 0x3c, 0xaf, 0xd6, 0x25, 0xe4, 0x8b, 0x38, 0x0a, 0xac, 0x72, 0x21, 0xd4, 0xf8, 0x07, 'E', 'J', 'E', 'M', 'P', 'L', 'O', 0x00, 0x00, 0x00, 0x00, 0x00, // game title 'A', 'E', 'J', 'S', // game code 'A' + two characters + 'S' (for spanish) '0', '1', // maker code 0x96, // fixed value 0x00, // GBA 0x00, // device type 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved 0x00, // software version 0xC1, // complement check 0x00, 0x00 // reserved };
SECTIONS { . = 0x08000000 ; .cartridge_header : { startup.o (.cartridge_header) } . = 0x08000100 ; .text : { _linker_code = . ; startup.o (.startup) *(.text) *(.text.*) *(.rodata*) *(.gnu.linkonce.t*) *(.gnu.linkonce.r*) } .preinit_array : { __preinit_array_start = . ; *(.preinit_array) __preinit_array_end = . ; } .init_array : { __init_array_start = . ; *(.init_array) __init_array_end = . ; } .fini_array : { __fini_array_start = . ; *(.fini_array) __fini_array_end = . ; } .ctors : { __CTOR_LIST__ = . ; LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) *(.ctors) LONG(0) __CTOR_END__ = . ; } .dtors : { __DTOR_LIST__ = . ; LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) *(.dtors) LONG(0) __DTOR_END__ = . ; } flash_sdata = . ; . = 0x03000000 ; ram_sdata = . ; .data : AT (flash_sdata) { _linker_data = . ; *(.data) *(.data.*) *(.gnu.linkonce.d*) } ram_edata = . ; data_size = ram_edata - ram_sdata; ram_sbssdata = . ; .bss : AT (LOADADDR(.data) + SIZEOF(.data)) { _linker_bss = . ; *(.bss) *(.bss.*) *(.gnu.linkonce.b.*) *(.COMMON) } ram_ebssdata = . ; bssdata_size = ram_ebssdata - ram_sbssdata; _linker_end = . ; end = . ; }
__attribute__((section(".cartridge_header"))) const uint8_t CARTRIDGE_HEADER[256] = { 0x3E, 0x00, 0x00, 0xEA, // branch to +0x100 0b 11101010 00000000 00000000 00111110 --> 0xEA 0x00 0x00 0x3E 0x24, 0xff, 0xae, 0x51, 0x69, 0x9a, 0xa2, 0x21, 0x3d, 0x84, 0x82, 0x0a, // nintendo logo 0x84, 0xe4, 0x09, 0xad, 0x11, 0x24, 0x8b, 0x98, 0xc0, 0x81, 0x7f, 0x21, 0xa3, 0x52, 0xbe, 0x19, 0x93, 0x09, 0xce, 0x20, 0x10, 0x46, 0x4a, 0x4a, 0xf8, 0x27, 0x31, 0xec, 0x58, 0xc7, 0xe8, 0x33, 0x82, 0xe3, 0xce, 0xbf, 0x85, 0xf4, 0xdf, 0x94, 0xce, 0x4b, 0x09, 0xc1, 0x94, 0x56, 0x8a, 0xc0, 0x13, 0x72, 0xa7, 0xfc, 0x9f, 0x84, 0x4d, 0x73, 0xa3, 0xca, 0x9a, 0x61, 0x58, 0x97, 0xa3, 0x27, 0xfc, 0x03, 0x98, 0x76, 0x23, 0x1d, 0xc7, 0x61, 0x03, 0x04, 0xae, 0x56, 0xbf, 0x38, 0x84, 0x00, ... };
void _startup() __attribute__((section(".startup"), naked, target("arm"))); void _thumb_startup(); void _startup() { _thumb_startup(); } void _thumb_startup() { _initDataRAM(); _initBssRAM(); _callConstructors(); _callInitArray(); main(); _callFiniArray(); _callDestructors(); while (true) ; }
# con esta línea compilamos "startup.cc", el código de arranque RUTA_BARE_METAL_ARM/bin/arm-none-eabi-g++ -std=c++11 -march=armv4t -mcpu=arm7tdmi -mtune=arm7tdmi -fno-exceptions -fno-rtti -nostartfiles -mthumb -mthumb-interwork -o startup.o startup.cc # con esta línea compilamos el resto de ficheros fuente que necesitemos RUTA_BARE_METAL_ARM/bin/arm-none-eabi-g++ -std=c++11 -march=armv4t -mcpu=arm7tdmi -mtune=arm7tdmi -fno-exceptions -fno-rtti -nostartfiles -mthumb -mthumb-interwork -o main.o main.cc # enlazamos (generamos un fichero .elf) indicando al enlazador # que use nuestro script de enlazado personalizado "gba.ld" RUTA_BARE_METAL_ARM/bin/arm-none-eabi-g++ -std=c++11 -march=armv4t -mcpu=arm7tdmi -mtune=arm7tdmi -fno-exceptions -fno-rtti -nostartfiles -mthumb -mthumb-interwork -Wl,-Tgba.ld -o main.elf startup.o main.o # generamos el binario (.gba) a partir del .elf RUTA_BARE_METAL_ARM/bin/arm-none-eabi-objcopy -O binary main.elf main.gba
#include <stdint.h> #include "Ana.H" // .H con datos generados a partir de una imagen de 240x160 (RGB555) using namespace std; using namespace avelino; #define DISPCNT *((volatile uint16_t *) 0x04000000) #define MODE3_VRAM_FB ((uint16_t *) 0x06000000) void memcpy16(uint16_t *dest, const uint16_t *source, uint32_t size) { uint16_t *s = (uint16_t *) source; while (size > 0) { *dest = *s; s++; dest++; size--; } } int main() { // force blank to access VRAM DISPCNT = ((uint16_t) 1) << 7; // draw on framebuffer memcpy16(MODE3_VRAM_FB, ANA, 240 * 160); // enable mode 3 with background layer 2 DISPCNT = ((uint16_t) 3) | (((uint16_t) 1) << 10); while (true) ; }
typedef void (*fptr)(void); #define ISR_PTR *((fptr *) 0x03007FFC) #define IME *((volatile uint32_t *) 0x04000208) #define IE *((volatile uint16_t *) 0x04000200) #define IF *((volatile uint16_t *) 0x04000202) #define IFBIOS *((volatile uint16_t *) 0x03007FF8) void isr() __attribute__((target("arm"))); void isr() { // // ... // // mark v-blank interrupt as served IF = 1; IFBIOS |= 1; } void enableInterrupts() { IME = 0; ISR_PTR = isr; DISPSTAT = ((uint16_t) 1) << 3; IE = 1; // enable v-blank IME = 1; } int main() { enableInterrupts(); // // ... // while (true) { asm volatile ("swi 0x05"); // wait for v-blank } }
int main() { enableInterrupts(); // force blank to access VRAM DISPCNT = ((uint16_t) 1) << 7; // draw on framebuffer memcpy16(MODE3_VRAM_FB, ANA, 240 * 160); // configure OBJ 256 color palette (all colors white) OBJ_PALETTE[0] = 0; // transparent (color don't care) OBJ_PALETTE[1] = 0; // black OBJ_PALETTE[2] = 0x7FFF; // white // OBJ tile 0 memcpy16((uint16_t *) OBJ_TILES_VRAM, BALL_TILE, 32); // MUST copy 16 bit words // configure OBJ 0 ballX = 120 - 4; ballY = 80 - 4; // (y, no rotation/scaling, enabled, no mosaic, 256 colors with 1 palette, shape = square) OAM[0] = (ballY & 0x00FF) | ((uint16_t) 1) << 13; // (x, no horizontal flip, no vertical flip, size = 8x8) OAM[1] = (ballX & 0x01FF); // (tile number = 512 (first obj tile with mode 3), priority = 0) OAM[2] = 512; // enable mode 3 with background layer 2, enable obj, enable obj 1-d mapping DISPCNT = ((uint16_t) 3) | (((uint16_t) 1) << 10) | (((uint16_t) 1) << 12) | (((uint16_t) 1) << 6); ballDX = 1; ballDY = 1; while (true) { asm volatile ("swi 0x05"); // wait for v-blank } }
int32_t ballX, ballY, ballDX, ballDY; void isr() __attribute__((target("arm"))); void isr() { // calculate next location ballX += ballDX; ballY += ballDY; // check collisions if (ballX < 0) { ballX = 0; ballDX = 1; } else if (ballX > (240 - 8)) { ballX = 240 - 8; ballDX = -1; } if (ballY < 0) { ballY = 0; ballDY = 1; } else if (ballY > (160 - 8)) { ballY = 160 - 8; ballDY = -1; } // locate sprite OAM[0] = (ballY & 0x00FF) | ((uint16_t) 1) << 13; OAM[1] = (ballX & 0x01FF); // mark interrupt as served IF = 1; IFBIOS |= 1; }
./configure --prefix=/opt/sdcc --disable-pic14-port --disable-pic16-port
make
make install
.module crt0gb .globl _main .area _HEADER (ABS) .org 0x0100 nop jp init ; nintendo logo .db 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E ; game title (16 bytes) .ascii "PRUEBA" .db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; licensee code (0 for non super gameboy games) .db 0x00, 0x00, 0x00 ; cartridge type (0 for ROM only) .db 0x00 ; ROM size (0 for 32 KBytes) .db 0x00 ; RAM size (0 for none) .db 0x00 ; country code (1 for non japanese) .db 0x01 ; licensee code (0xA4 for Konami) .db 0xA4 ; version number .db 0x00 ; header checksum (run "header_checksum_calc < main.gb" to get this value) .db 0x83 ; PUT HERE THE VALUE CALCULATED BY header_checksum_calc AND RECOMPILE ; checksum of all cartridge (GameBoy ignores this) .db 0x00, 0x00 .org 0x0150 init: call gsinit call _main exit_loop:: jp exit_loop .area _HOME .area _CODE .area _GSINIT gsinit:: .area _GSFINAL ret .area _DATA .area _BSS .area _HEAP
#include <stdint.h> #define IE *((uint8_t *) 0xFFFF) #define LCDC *((uint8_t *) 0xFF40) #define STAT *((uint8_t *) 0xFF41) #define BGP *((uint8_t *) 0xFF47) #define BG_TILES ((uint8_t *) 0x8800) #define BG_DATA ((uint8_t *) 0x9800) const uint8_t TILE0[16] = { 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011 }; void memcpy(void *dest, void *src, uint16_t n) { uint8_t *d = (uint8_t *) dest; uint8_t *s = (uint8_t *) src; while (n > 0) { *d = *s; d++; s++; n--; } } void main() { // disable LCD before accessing VRAM while ((STAT & 0x03) != 0x01) ; LCDC = 0x00; // copy tile data to tile data VRAM memcpy(BG_TILES, TILE0, 16); // disable all LCD interrupts STAT = 0x00; // disable interrupts IE = 0x00; // configure palette BGP = 0b11100100; // 0b11 for full black, 0b10 for dark gray, 0b01 for light gray and 0b00 for white // enable LCD, disable window, background tile data at 0x8800, background tile indices at 0x9800, obj not displayed, background display on LCDC = 0x81; // with LCD enabled, wait vblank before accessing VRAM while ((STAT & 0x03) != 0x01) ; BG_DATA[0] = 0x80; // halt while (1) ; }
.module crt0gb .globl _main .globl _vblankISR .area _HEADER (ABS) .org 0x0040 call _vblankISR reti .org 0x0100 nop jp init ...
void vblankISR() __interrupt { // access VRAM here // ... }
#include <stdint.h> #define IE *((uint8_t *) 0xFFFF) #define LCDC *((uint8_t *) 0xFF40) #define STAT *((uint8_t *) 0xFF41) #define BGP *((uint8_t *) 0xFF47) #define BG_TILES ((uint8_t *) 0x8800) #define BG_DATA ((uint8_t *) 0x9800) const uint8_t TILE0[16] = { 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011, 0b01010101, 0b00110011 }; const uint8_t TILE1[16] = { 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000 }; const uint8_t TILE2[16] = { 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111, 0b00000000, 0b11111111 }; const uint8_t TILE3[16] = { 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111 }; void memcpy(void *dest, void *src, uint16_t n) { uint8_t *d = (uint8_t *) dest; uint8_t *s = (uint8_t *) src; while (n > 0) { *d = *s; d++; s++; n--; } } volatile uint8_t frameCounter; volatile uint8_t frameIndex; void vblankISR() __interrupt { if (frameCounter == 0) { // show tiles BG_DATA[0] = 0x80 + frameIndex; BG_DATA[1] = 0x80 + ((frameIndex + 1) & 0x03); BG_DATA[2] = 0x80 + ((frameIndex + 2) & 0x03); BG_DATA[3] = 0x80 + ((frameIndex + 3) & 0x03); frameIndex = (frameIndex + 1) & 0x03; frameCounter = 60; } else frameCounter--; } void main() { // disable LCD before accessing VRAM while ((STAT & 0x03) != 0x01) ; LCDC = 0x00; // copy tile data to tile data VRAM memcpy(BG_TILES, TILE0, 16); memcpy(BG_TILES + 16, TILE1, 16); memcpy(BG_TILES + 32, TILE2, 16); memcpy(BG_TILES + 48, TILE3, 16); // disable all LCD interrupts STAT = 0x00; // enable only vblank interrupt frameCounter = 0; frameIndex = 0; IE = 0x01; __asm__ ("ei"); // configure palette BGP = 0b11100100; // 0b11 for full black, 0b10 for dark gray, 0b01 for light gray and 0b00 for white // enable LCD, disable window, background tile data at 0x8800, background tile indices at 0x9800, obj not displayed, background display on LCDC = 0x81; // halt while (1) ; }
#include <iostream> #include <iomanip> using namespace std; int main() { cin.ignore(0x0134); uint8_t x = 0; for (int i = 0x0134; i <= 0x014C; i++) x = x - ((uint8_t) cin.get()) - 1; cout << "0x" << hex << setw(2) << setfill('0') << ((int) x) << endl; return 0; }
# ensamblamos crt0gb.s /opt/sdcc/bin/sdasgb -o crt0gb.rel crt0gb.s # compilamos main.c /opt/sdcc/bin/sdcc -msm83 -c -o main.rel main.c # enlazamos para generar la ROM “casi-final” indicando que la RAM está en 0xC000 /opt/sdcc/bin/sdcc -msm83 --data-loc 0xC000 --no-std-crt0 -o main.ihx crt0gb.rel main.rel /opt/src/Hex2bin-2.5/hex2bin -s 0 -e gb main.ihx # ahora tenemos la ROM “casi-final” en el fichero “main.gb” # compilamos en el host el generador de suma de control g++ -std=c++11 -o header_checksum_calc header_checksum_calc.cc # lo ejecutamos con el "main.gb" para que nos diga el valor de la suma de control ./header_checksum_calc < main.gb 0x83 # este valor 0x83 es el que debo poner en el fichero crt0gb.s como checksum (dirección 0x014D de la ROM) # si al poner este valor, se modifica el fichero crt0gb.s, hay que recompilar desde el principio # Nótese que este valor se calcula a partir de la cabecera, no a partir de toda la ROM, por lo que en circunstancias normales apenas cambia
... type WordArray32 is array(0 to 31) of std_logic_vector(31 downto 0); signal RegisterD : WordArray32; signal RegisterQ : WordArray32; ...
... process (Clk) begin if ((Clk = '1') and Clk'event) then for i in 0 to 31 loop RegisterQ(i) <= RegisterD(i); end loop; IRQ <= IRD; PCQ <= PCD; FSMQ <= FSMD; CounterQ <= CounterD; end if; end process; ...
... -- RegSelForALUOut: 1..31 (R1..R31) RegisterD(0) <= (others => '0'); -- r0 cannot be altered, always zero value Gen1: for I in 1 to 31 generate RegisterD(I) <= ALUOut when (DecodedMuxReg(I) = '1') else RegisterQ(I); end generate; Gen1X: for I in 0 to 31 generate DecodedMuxReg(I) <= '1' when (I = to_integer(unsigned(RegSelForALUOut))) else '0'; end generate; ...
... ALUOut <= std_logic_vector(signed(ALUIn1) + signed(ALUIn2)) when (ALUSel = ALU_SEL_ADD) else std_logic_vector(signed(ALUIn1) - signed(ALUIn2)) when (ALUSel = ALU_SEL_SUB) else (ALUIn1 xor ALUIn2) when (ALUSel = ALU_SEL_XOR) else (ALUIn1 or ALUIn2) when (ALUSel = ALU_SEL_OR) else (ALUIn1 and ALUIn2) when (ALUSel = ALU_SEL_AND) else (ALUIn1(30 downto 0) & '0') when (ALUSel = ALU_SEL_SLL) else ('0' & ALUIn1(31 downto 1)) when (ALUSel = ALU_SEL_SRL) else (ALUIn1(31) & ALUIn1(31 downto 1)) when (ALUSel = ALU_SEL_SRA) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_LT) and (signed(ALUIn1) < signed(ALUIn2)) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_LT) and (signed(ALUIn1) >= signed(ALUIn2)) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_LTU) and (unsigned(ALUIn1) < unsigned(ALUIn2)) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_LTU) and (unsigned(ALUIn1) >= unsigned(ALUIn2)) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_EQ) and (ALUIn1 = ALUIn2) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_EQ) and (ALUIn1 /= ALUIn2) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_GE) and (signed(ALUIn1) >= signed(ALUIn2)) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_GE) and (signed(ALUIn1) < signed(ALUIn2)) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_GEU) and (unsigned(ALUIn1) >= unsigned(ALUIn2)) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_GEU) and (unsigned(ALUIn1) < unsigned(ALUIn2)) else std_logic_vector(to_signed(1, 32)) when (ALUSel = ALU_SEL_NE) and (ALUIn1 /= ALUIn2) else std_logic_vector(to_signed(0, 32)) when (ALUSel = ALU_SEL_NE) and (ALUIn1 = ALUIn2) else ALUIn1; -- default operation, identity ...
/* * ROM from 0x00000000 to 0x00000FFF * GPIO from 0x00001000 to 0x00001003 * RAM from 0x00001004 to 0x00001FFF */ SECTIONS { . = 0x00000000 ; .text : { startup.o (.startup) *(.text) *(.text.*) *(.rodata*) } . = 0x00001004 ; .data : { *(.data) *(.data.*) } }
#include <stdint.h> using namespace std; extern int main(); void _startup() __attribute__((section(".startup"), naked)); // startup located at RESET_VECTOR void _startup() { asm volatile ( "la sp, 0x00001FFC" // point SP to the end of SRAM (4 Kb ROM + 4 Kb RAM = 8 Kb total) ); main(); while (true) ; }
#include <stdint.h> using namespace std; #define GPIO *((uint8_t *) 0x00001000) int main() { GPIO = 0; while (true) { for (int i = 0; i < 5; i++) ; GPIO = GPIO ^ 1; } }
# /opt/baremetalriscv/bin/riscv32-none-elf-objdump -M no-aliases,numeric -D main.elf main.elf: file format elf32-littleriscv Disassembly of section .text: 00000000 <_Z8_startupv>: 0: 00002137 lui x2,0x2 4: ffc10113 addi x2,x2,-4 # 1ffc8: 008000ef jal x1,10 c: 0000006f jal x0,c <_Z8_startupv+0xc> 00000010 : 10: fe010113 addi x2,x2,-32 14: 00812e23 sw x8,28(x2) ...
00000000 <_Z8_startupv>: 0: 00002137 lui x2,0x2 4: ffc10113 addi x2,x2,-4 # 1ffc8: 008000ef jal x1,10 c: 0000006f jal x0,c <_Z8_startupv+0xc> 00000010 : 10: fe010113 addi x2,x2,-32 14: 00812e23 sw x8,28(x2) ...
Entrada ADC | Entrada oscilador | Salida mezclador (multiplicador) |
---|---|---|
-1 | -1 | +1 |
-1 | +1 | -1 |
+1 | -1 | -1 |
+1 | +1 | +1 |
Entrada ADC | Entrada oscilador | Salida mezclador (multiplicador) |
---|---|---|
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity CycloneIIAMReceiver is port ( ClkIn : in std_logic; -- 50 MHz AntennaIn : in std_logic; DeltaSigmaOut : out std_logic; SpeakerOut : out std_logic ); end entity; architecture A of CycloneIIAMReceiver is -- 1-bit ADC signal DeltaSigmaADCD : std_logic; signal DeltaSigmaADCQ : std_logic; -- COPE AM Las Palmas: 837 KHz -- (837000 / 50000000) * (2 ^ 64) = 308798495793897920 (64 bit) -- upper 32 bit: 71897752 -- lower 32 bit: 2297979328 ----constant UPPER_LOCAL_OSC_INC : integer := 71897752; ----constant LOWER_LOCAL_OSC_INC : integer := 2297979328; -- RNE AM Las Palmas: 576 KHz -- (576000 / 50000000) * (2 ^ 64) = 212506491729134048 (64 bit) -- upper 32 bit: 49478023 -- lower 32 bit: 1073398240 constant UPPER_LOCAL_OSC_INC : integer := 49478023; constant LOWER_LOCAL_OSC_INC : integer := 1073398240; constant LOCAL_OSC_INC : std_logic_vector(63 downto 0) := std_logic_vector(to_unsigned(UPPER_LOCAL_OSC_INC, 32)) & std_logic_vector(to_unsigned(LOWER_LOCAL_OSC_INC, 32)); signal LocalOscAccD : std_logic_vector(63 downto 0); signal LocalOscAccQ : std_logic_vector(63 downto 0); signal LocalOscOut : std_logic; -- mixer signal MixerOut : std_logic; signal NumericMixerOut : std_logic_vector(10 downto 0); -- decimator (factor = 1024 = 2^10, so pass from 50 MHz to 48.8 KHz (50000000 / 1024) signal DecimatorCounterD : std_logic_vector(9 downto 0); signal DecimatorCounterQ : std_logic_vector(9 downto 0); signal DecimatorAccD : std_logic_vector(10 downto 0); signal DecimatorAccQ : std_logic_vector(10 downto 0); signal DecimatorLatchD : std_logic_vector(10 downto 0); signal DecimatorLatchQ : std_logic_vector(10 downto 0); signal DemodulatedOutput : std_logic_vector(9 downto 0); begin -- delta-sigma ADC for input process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then DeltaSigmaADCQ <= DeltaSigmaADCD; end if; end process; DeltaSigmaADCD <= AntennaIn; DeltaSigmaOut <= DeltaSigmaADCQ; -- local oscillator process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then LocalOscAccQ <= LocalOscAccD; end if; end process; LocalOscAccD <= std_logic_vector(unsigned(LocalOscAccQ) + unsigned(LOCAL_OSC_INC)); LocalOscOut <= LocalOscAccQ(63); -- mixer (multiplier) MixerOut <= LocalOscOut xnor DeltaSigmaADCQ; NumericMixerOut <= std_logic_vector(to_unsigned(1, 11)) when (MixerOut = '1') else std_logic_vector(to_unsigned(0, 11)); -- decimator process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then DecimatorCounterQ <= DecimatorCounterD; end if; end process; DecimatorCounterD <= std_logic_vector(unsigned(DecimatorCounterQ) + to_unsigned(1, 10)); process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then DecimatorAccQ <= DecimatorAccD; end if; end process; DecimatorAccD <= NumericMixerOut when (unsigned(DecimatorCounterQ) = 0) else std_logic_vector(unsigned(DecimatorAccQ) + unsigned(NumericMixerOut)); process (ClkIn) begin if (ClkIn'event and (ClkIn = '1')) then DecimatorLatchQ <= DecimatorLatchD; end if; end process; DecimatorLatchD <= DecimatorAccQ when (unsigned(DecimatorCounterQ) = 0) else DecimatorLatchQ; DemodulatedOutput <= DecimatorLatchQ(10 downto 1); -- PWM for speaker output SpeakerOut <= '1' when (unsigned(DecimatorCounterQ) > unsigned(DemodulatedOutput)) else '0'; end architecture;
... // poner a 1 el bit 3 del CSR "msi" asm volatile ( "csrsi mstatus, 8" ); ... // escribir 0x08000000 en el CSR número 0x307 (cuando no podemos usar el nombre) asm volatile ( "csrw 0x307, %[reg]" : : [reg] "r" (0x08000000) ); ...
#include <stdint.h> using namespace std; #define RCU_APB2EN *((volatile uint32_t *) 0x40021018) #define GPIOC_CTL1 *((volatile uint32_t *) 0x40011004) #define GPIOC_OCTL *((volatile uint32_t *) 0x4001100C) #define MTIME *((volatile uint64_t *) 0xD1000000) #define MTIMECMP *((volatile uint64_t *) 0xD1000008) #define MTIME_INTERRUPT_PERIOD 12000000 // 24 MHz, so 12000000 generates an interrupt period of exactly half a second void interruptHandler() __attribute__ ((interrupt, section(".interrupt_handler"))); void interruptHandler() { MTIME = 0; MTIMECMP = MTIME_INTERRUPT_PERIOD; GPIOC_OCTL = GPIOC_OCTL ^ (((uint32_t) 1) << 13); } int main() { // enable clock on port C RCU_APB2EN = RCU_APB2EN | (((uint32_t) 1) << 4); // PC13 pin is output, low speed, push-pull GPIOC_CTL1 = 0x44244444; // basic (non vectored) interrupt handler (to force non vectored, set 0 to lower two bits of mtvec, so force 4 byte aligned on linker script for interrupt handler) asm volatile ( "csrw mtvec, %[reg]" : : [reg] "r" ((uint32_t) interruptHandler) ); // machine interrupt enable asm volatile ( "csrw mie, %[reg]" : : [reg] "r" ((uint32_t) 0x80) ); asm volatile ( "csrsi mstatus, 8" ); // configure interrupt period MTIME = 0; MTIMECMP = MTIME_INTERRUPT_PERIOD; // sleep while (true) asm volatile ("wfi"); }
SECTIONS { . = 0x08000000 ; .text : { startup.o (.startup0) } . = 0x08000200 ; .text : { _linker_code = . ; startup.o (.startup1) *(.text) *(.text.*) *(.rodata*) *(.gnu.linkonce.t*) *(.gnu.linkonce.r*) } . = ALIGN(4); /* to force lower 2 bits of address to 0 (mtvec.mode = 0 to select non vectored interrupt handler) */ .interrupt_handler : { *(.interrupt_handler) } ... }
#define CLIC_BASE 0xD2000000 #define CLIC_IP(source) *(volatile uint8_t *)(CLIC_BASE + 0x00001000 + ((source) * 4)) #define CLIC_IE(source) *(volatile uint8_t *)(CLIC_BASE + 0x00001001 + ((source) * 4)) #define CLIC_ATTR(source) *(volatile uint8_t *)(CLIC_BASE + 0x00001002 + ((source) * 4)) #define CLIC_CTL(source) *(volatile uint8_t *)(CLIC_BASE + 0x00001003 + ((source) * 4)) #define CLIC_CFG *(volatile uint8_t *)(CLIC_BASE + 0x00000000) #define CLIC_MTH *(volatile uint8_t *)(CLIC_BASE + 0x0000000B) #define CLIC_IP_IE_ATTR_CTL(source) *(volatile uint32_t *)(CLIC_BASE + 0x00001000 + ((source) * 4))
SECTIONS { . = 0x08000000 ; .text : { startup.o (.startup0) } . = 0x0800001C ; .clic_int_tmr_vector : { LONG(CLIC_INT_TMR_ADDRESS); } . = 0x08000200 ; .text : { _linker_code = . ; startup.o (.startup1) *(.text) *(.text.*) *(.rodata*) *(.gnu.linkonce.t*) *(.gnu.linkonce.r*) } . = ALIGN(4); CLIC_INT_TMR_ADDRESS = . ; .clic_int_tmr : { *(.clic_int_tmr) } ... }
void clicIntTmr() __attribute__ ((interrupt, section(".clic_int_tmr"))); void clicIntTmr() { MTIME = 0; MTIMECMP = MTIME_1_SEC; GPIOC_OCTL = GPIOC_OCTL ^ (((uint32_t) 1) << 13); // toggle led CLIC_IP(7) = 0; // not pending }
int main() { // enable clock on port C RCU_APB2EN = RCU_APB2EN | (((uint32_t) 1) << 4); // PC13 pin is output, low speed, push-pull GPIOC_CTL1 = 0x44244444; // clear CLIC config register and set vectored interrupts CLIC_CFG = 0; CLIC_MTH = 0; // specific register for GD32VF103 ECLIC for (uint16_t i = 0; i < 4096; i++) CLIC_IP_IE_ATTR_CTL(i) = 0; // use CLIC vectored interrupt handler (put 1 on mode (lower two) bits of mtvec) asm volatile ( "csrw mtvec, %[reg]" : : [reg] "r" (0x00000003) ); // machine interrupt enable asm volatile ( "csrsi mstatus, 8" ); // set CLIC interrupt vector table (mtvt = 0x307) asm volatile ( "csrw 0x307, %[reg]" : : [reg] "r" (0x08000000) ); CLIC_ATTR(7) = 0b11000001; // machine mode, level triggered, vectored CLIC_IE(7) = 1; // enable interrupt 7 CLIC_IP(7) = 0; // not pending CLIC_CTL(7) = 0; // priority // configure interrupt period MTIME = 0; MTIMECMP = MTIME_1_SEC; while (true) asm volatile ("wfi"); }
reset_vector:
la sp, 0x20005000 ; la SRAM acaba en 0x20005000 y la pila "crece" hacia abajo
call main ; llamamos a la función main
SECTIONS {
. = 0x08000000 ;
.text : {
startup.o (.startup0)
}
. = 0x08000200 ;
.text : {
_linker_code = . ;
startup.o (.startup1)
*(.text)
*(.text.*)
*(.rodata*)
*(.gnu.linkonce.t*)
*(.gnu.linkonce.r*)
}
.preinit_array : {
__preinit_array_start = . ;
*(.preinit_array)
__preinit_array_end = . ;
}
.init_array : {
__init_array_start = . ;
*(.init_array)
__init_array_end = . ;
}
.fini_array : {
__fini_array_start = . ;
*(.fini_array)
__fini_array_end = . ;
}
.ctors : {
__CTOR_LIST__ = . ;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
*(.ctors)
LONG(0)
__CTOR_END__ = . ;
}
.dtors : {
__DTOR_LIST__ = . ;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
*(.dtors)
LONG(0)
__DTOR_END__ = . ;
}
...
...
flash_sdata = . ;
. = 0x20000000 ;
ram_sdata = . ;
.data : AT (flash_sdata) {
_linker_data = . ;
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
}
ram_edata = . ;
data_size = ram_edata - ram_sdata;
ram_sbssdata = . ;
.bss : AT (LOADADDR(.data) + SIZEOF(.data)) {
_linker_bss = . ;
*(.bss)
*(.bss.*)
*(.gnu.linkonce.b.*)
*(.COMMON)
}
ram_ebssdata = . ;
bssdata_size = ram_ebssdata - ram_sbssdata;
_linker_end = . ;
end = . ;
}
void _startup_0() __attribute__((section(".startup0"), naked)); void _startup_0() { asm volatile ( "j %0" : : "i" (_startup_1) ); }
void _startup_1() __attribute__((section(".startup1"), naked)); void _startup_1() { asm volatile ( "la sp, 0x20005000" // point SP to the end of SRAM ); _initClock(); _initDataRAM(); _initBssRAM(); _callConstructors(); _callInitArray(); main(); _callFiniArray(); _callDestructors(); while (true) ; }
#includeusing namespace std; #define RCU_APB2EN *((uint32_t *) 0x40021018) #define GPIOC_CTL1 *((uint32_t *) 0x40011004) #define GPIOC_OCTL *((uint32_t *) 0x4001100C) int main() { // enable clock on port C RCU_APB2EN |= ((uint32_t) 1) << 4; // PC13 pin is output, low speed, push-pull GPIOC_CTL1 = 0x44244444; while (true) { for (uint32_t i = 0; i < 200000; i++) ; GPIOC_OCTL ^= (((uint32_t) 1) << 13); } }
dfu-util --dfuse-address 0x08000000 -D main.bin