Bootloader Jump Function. How to Jump to the right Address? - microcontroller

I am trying to create a bootloader that jumps to my application code on a MKE02Z32VFM4 (KEO2 Series from Freescale). I am working with the Keil IDE 5 and the Armv6 Compiler v6.16.
After Issuing the Jump Instruction to the application start address, the code Jumps to "a" reset handler. And when the instruction to jump to __main is reached, it jumps to the main of the bootloader. The Flash Memory is defined by the linker file as followed:
#define m_interrupts_start 0x00000000
#define m_interrupts_size 0x00000200
#define m_flash_config_start 0x00000400
#define m_flash_config_size 0x00000010
#define bootloader_start 0x00000410
#define bootloader_size 0x00000800 //2kb size 0x410+0x800=0xC10 ==> 256 byte aligned => 0xE00
#define ota_part_0_start 0x00000E00 //Vector Table interrupt must be 256 byte aligned
#define ota_part_0_size 0x00003800 //14KB (14336 Byte) 0xE00+0x3800 => 0x4600
#define ota_part_1_start 0x00004600
#define ota_part_1_size 0x00003800 //14KB (14336 Byte) 0x4600+0x3800 = 0x7E00 || flash_end == 0x0000 7FFF => 0x100(256) byte frei
#define m_data_start 0x1FFFFC00 //ram start
#define m_data_size 0x00001000 //4kb
The application linker file (scatter file) is working with these defines:
#define m_interrupts_start 0x00000E00 //Address of the application reset handler
#define m_interrupts_size 0x00000200
#define m_flash_config_start 0x00001000 //some config bytes, defined by manufacturer
#define m_flash_config_size 0x00000010
#define m_text_start 0x00001010 // start address of application code
#define m_text_size 0x000035F0
#define m_data_start 0x1FFFFC00 //ram start
#define m_data_size 0x00001000 //4kb
The reset handler is written in assembler, i tried to comment the instructions:
Reset_Handler:
cpsid i /* Mask interrupts */
.equ VTOR, 0xE000ED08 //.equ is like #define in C. VTOR = predefined ARMv6 label. 0xE000ED08 VectorTableOffsetRegister.
ldr r0, =VTOR // load word from memory. load value from word at VTOR address to r0. R0 now contains the offset for the vector table.
ldr r1, =__Vectors // load word from memory. load value of word at __Vectors address to r1. --> the first word at __Vectors is the initial stack pointer
str r1, [r0] //store Register to memory. content of r1 is stored to memory adress in r0(==VTOR) --> initial stack pointer is stored to the first word of the Vector table
ldr r2, [r1] //load word from memory. r2 is set to the value of the word in memory at address in r1. --> r2 is set to the address of the initial stack pointer
msr msp, r2 //move to special register. move value of r2 to special register msp (main stack pointer) --> main stack pointer is set to the valjue of the initial stack pointer
ldr r0,=SystemInit //set register 0 to address of SystemInit function. (
blx r0 // branch with link ( to address of r0)
cpsie i /* Unmask interrupts */
ldr r0,=__main
bx r0
.pool
.size Reset_Handler, . - Reset_Handler
The bootloader code is as followed:
Address in this first test is the value 0x00000E00 (start of user app)
__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
__asm("MSR MSP,r0");
__asm("BX r1");
}
static void BootJump( uint32_t *Address )
{
if( CONTROL_nPRIV_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped.
{ /* not in privileged mode */
EnablePrivilegedMode( ) ;
}
NVIC->ICER[0] = 0xFFFFFFFF ;
NVIC->ICPR[0] = 0xFFFFFFFF ;
SysTick->CTRL = 0 ;
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk ;
if( CONTROL_SPSEL_Msk & __get_CONTROL( ) ) //THIS is from the arm doku, but it is always false in our implementation and skipped. (only 1 stack pointer used)
{ /* MSP is not active */
__set_MSP( __get_PSP( ) ) ;
__set_CONTROL( __get_CONTROL( ) & ~CONTROL_SPSEL_Msk ) ;
}
SCB->VTOR = ( uint32_t )Address ; //Setting the Vector Table Offset Register to the start of the user app.
BootJumpASM( Address[ 0 ], Address[ 1 ] ) ; //This function is taken from the Arm Documentation
}
After
SCB->VTOR = (uint32_t)Address; // Set VTOR to 0xE00
The VTOR register IS updated to 0xE00. However after executing the function:
__attribute__( ( naked, noreturn ) ) void BootJumpASM( uint32_t SP, uint32_t RH )
{
__asm("MSR MSP,r0");
__asm("BX r1"); //<-- This is the Point where VTOR changes it value to 0x00 again
}
VTOR is 0x00 again and im in the resethandler. This resethandler connects to the bootloader main. So i assume im in the reset handler at 0x00 and not the one at 0xE00. I checked the flash memory and am positive that a Vector Table is located at 0x000 AND 0xE00. I am positive that the firmware of the application is also at the right place in the flash.
I am assuming that I either:
Defined the Memory space wrong.
The BootJumpASM function jumps to a illegal location and the MCU restarts over at 0x00 with a reset VTOR Register.
I am not sure, why the BootJumpASM function uses r0 and r1 and what it does with the arguments of the function. I am simply new at assembler and all the specific compiler attributes. The function like described above is directly copied from:
https://developer.arm.com/documentation/ka002218/latest
And while i do not understand how the compiler manages to put the Function arguments to register r0 and r1 I am sure that the mistake is at my side and not in the official arm docs.
Can someone explain to me, why after the second instruction of the "BootJumpASM" function "VTOR" is reset to 0x00?
and why the resethandler ,the debugger is in right after, connects to the bootloader main and not the application main. And how do i manage to jump to the right location in memory.
Thanks for your time. I hope this explanation is not too confusing.

The problem was not the jump instruction, but the Debugger of the Keil IDE. I set up the debug environment according to arm and Keil documentation but after the jump out of the code environment of the bootloader into the application memory area, the Debugger triggered a reset. (Bootloader is a seperate Keil project.)
Starting the debugger within the application project, no such reset is triggered after the jump instruction and following the dissasembly view the bootloader executes as expected and the jump instruction works.
Thanks to all for taking time to try and find the error with me.

Related

PORTB and INT external interrupts stucks the code

I am working on a project with PIC16F877 (using MPLABX). I use RB0 pin external interrupt and RB4 pin portb interrupt to detect zero cross detection. I did everything correct, in proteus simulation everything was okey. Then I set up the circuit on breadboard, the LCD wasnt displaying the numbers (just the white dots). I thought the problem is the RB0 and PORTB interrupt. I wrote a simple code just includeshe PORTB interrupt and LCD and simulated. Everything is okey until the interrupt occures, when interrupt comes the code stops. I am new to PIC, this is the code I wrote:
/*
* File: lcd_deneme_16f877a.c
* Author: BATUHAN
*
* Created on 28 Aral?k 2022 Çar?amba, 13:52
*/
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#pragma config FOSC = XT // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = ON // Data EEPROM Memory Code Protection bit (Data EEPROM code-protected)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = ON // Flash Program Memory Code Protection bit (All program memory code-protected)
#define _XTAL_FREQ 4000000
void __interrupt() interrpt()
{
if(INTF)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
INTF=0;
RD0=RD0^1;
}
if(RBIF==1 && RB4==1)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD0=RD0^1;
}
}
void main(void)
{
TRISD=0X00;
PORTD=0X00;
TRISB=0b00010001;
PORTB=0X00;
INTCON=0b11011000; // GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF
OPTION_REGbits.nRBPU = 1;
INTEDG=1;
int V=0;
while(1)
{
V++;
__delay_ms(200);
}
return;
}
I tried the PORTB and RB0 interrupts separately and the problem still occurs.
What could be the problem. Thanks in advance
This is because your program stucks in interrupt routine due to the lack of proper handling of interrupts. You don't seem to handle the INT interrupt at all. For RB interrupt-on-change (IOC), you have to handle it sort of a little different and end the mismatch condition before clearing the flag. According to the PIC16F877A Datasheet this how the IOC works and must be handled:
Four of the PORTB pins, RB7:RB4, have an interrupt-on-change feature. Only pins configured as inputs can cause this interrupt to occur (i.e., any RB7:RB4 pin configured as an output is excluded from the interrupt-on-change comparison). The input pins (of RB7:RB4)are compared with the old value latched on the last read of PORTB. The “mismatch” outputs of RB7:RB4
are OR’ed together to generate the RB port change interrupt with flag bit RBIF (INTCON<0>). This interrupt can wake the device from Sleep. The user, in the Interrupt Service Routine, can clear the interrupt in the following manner:
a) Any read or write of PORTB. This will end the mismatch condition.
b) Clear flag bit RBIF.
A mismatch condition will continue to set flag bit RBIF. Reading PORTB will end the mismatch condition and allow flag bit RBIF to be cleared.
So your interrupt service code should look like the following:
void __interrupt() interrpt()
{
if(RBIF && RB4)
{
volatile uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD1=RD1^1;
}
else if(INTIF) {
INTIF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}
A friendly reminder
The proteus simulation is ok for some cases. However the simulation runs in ideal conditions. That's why you may not get the same expected behaviour in the real world conditions compared to proteus' ideal simulation conditions.
void __interrupt() interrpt()
{
if(RBIF)
{
PORTB; // Read PORTB to end the mismatch condition
RBIF=0;
if(RB4)
RD1=RD1^1;
}
else if(INTF) {
INTF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}

How to use VSPI on ESP32 with Adafruit_SPIFlash

I'm trying to use VSPI port of ESP32-WROVER with "Adafruit_SPIFlash.h" library https://github.com/adafruit/Adafruit_SPIFlash
This question is similar to How to use SPI with ESP32 and Arduino , but I'm using "Adafruit_SPIFlash.h" library.
This library seems to be using ESP32-WROVER "internal" SPI port, where WROVER SPI FLASH chip is connected, as a default. I have another SPI FLASH chip connected to VSPI port which I'm trying to use to store some sensor data.
I tried changing CUSTOM_SPI statement, but code doesn't compile
#define CUSTOM_SPI SPI //tried changing SPI to: VSPI, &VSPI, SPI1, SPI2, &SPI2
I asked this from Adafruit forum and got answer: "Create a Adafruit_FlashTransport_SPI object for the secondary SPI interface, then feed that to the Adafruit_SPIFlash object when you create it." Unfortunately I'm so nooby on FW things that this didn't help me and my follow up question is not answered.
For example how would I need to modify sketch below to use VSPI?
// The MIT License (MIT)
// Copyright (c) 2019 Ha Thach for Adafruit Industries
#include "SdFat.h"
#include "Adafruit_SPIFlash.h"
// Uncomment to run example with custom SPI and SS e.g with FRAM breakout
// #define CUSTOM_CS A5
// #define CUSTOM_SPI SPI
#if defined(CUSTOM_CS) && defined(CUSTOM_SPI)
Adafruit_FlashTransport_SPI flashTransport(CUSTOM_CS, CUSTOM_SPI);
#elif defined(ARDUINO_ARCH_ESP32)
// ESP32 use same flash device that store code.
// SPIFlash can use partition table to detect fatfs partition
// Therefore there is no need to specify the SPI and SS
Adafruit_FlashTransport_ESP32 flashTransport;
#elif defined(ARDUINO_ARCH_RP2040)
// RP2040 use same flash device that store code.
// Therefore there is no need to specify the SPI and SS
// Use default (no-args) constructor to be compatible with CircuitPython partition scheme
Adafruit_FlashTransport_RP2040 flashTransport;
// For generic usage: Adafruit_FlashTransport_RP2040(start_address, size)
// If start_address and size are both 0, value that match filesystem setting in
// 'Tools->Flash Size' menu selection will be used
#else
// On-board external flash (QSPI or SPI) macros should already
// defined in your board variant if supported
// - EXTERNAL_FLASH_USE_QSPI
// - EXTERNAL_FLASH_USE_CS/EXTERNAL_FLASH_USE_SPI
#if defined(EXTERNAL_FLASH_USE_QSPI)
Adafruit_FlashTransport_QSPI flashTransport;
#elif defined(EXTERNAL_FLASH_USE_SPI)
Adafruit_FlashTransport_SPI flashTransport(EXTERNAL_FLASH_USE_CS, EXTERNAL_FLASH_USE_SPI);
#else
#error No QSPI/SPI flash are defined on your board variant.h !
#endif
#endif
Adafruit_SPIFlash flash(&flashTransport);
/* If you want to use a specific flash device, for example for a custom built board, first look for it in Adafruit_SPIFlash\src\flash_devices.h
* If it isn't in there you need to create your own definition like the W25Q80DLX_EXAMPLE example below.
* These definitions need to be edited to match information on the data sheet of the flash device that you want to use.
* If you are not sure what the manufacture ID, memory type and capacity values should be, try running the sketch anyway and look at the serial output
* The flash device will report these values to you as a single hexadecimal value (the JDEC ID)
* For example, the first device on the list - the W25Q80DLX - will report its JDEC ID as 0xef4014, which is made of these three values:
* manufacturer_id = 0xef
* memory_type = 0x40
* capacity = 0x14
* With this macro properly defined you can then create an array of device definitions as shown below, this can include any from the list of devices in flash_devices.h, and any you define yourself here
* You need to update the variable on line 71 to reflect the number of items in the array
* You also need to uncomment line 84 and comment out line 81 so this array will be passed to the flash memory driver.
*
* Example of a user defined flash memory device:
#define W25Q80DLX_EXAMPLE \
{ \
.total_size = 1*1024*1024, \
.start_up_time_us = 5000, .manufacturer_id = 0xef, \
.memory_type = 0x40, .capacity = 0x14, .max_clock_speed_mhz = 80, \
.quad_enable_bit_mask = 0x02, .has_sector_protection = false, \
.supports_fast_read = true, .supports_qspi = true, \
.supports_qspi_writes = false, .write_status_register_split = false, \
.single_status_byte = false, .is_fram = false, \
}
*/
/*
* Create an array of data structures and fill it with the settings we defined above.
* We are using two devices, but more can be added if you want.
*/
//static const SPIFlash_Device_t my_flash_devices[] = {
// W25Q80DLX_EXAMPLE,
//};
/*
* Specify the number of different devices that are listed in the array we just created. If you add more devices to the array, update this value to match.
*/
//const int flashDevices = 1;
// the setup function runs once when you press reset or power the board
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(100); // wait for native usb
Serial.println("Adafruit Serial Flash Info example");
flash.begin();
//Using a flash device not already listed? Start the flash memory by passing it the array of device settings defined above, and the number of elements in the array.
//flash.begin(my_flash_devices, flashDevices);
Serial.print("JEDEC ID: 0x"); Serial.println(flash.getJEDECID(), HEX);
Serial.print("Flash size: "); Serial.print(flash.size() / 1024); Serial.println(" KB");
}
void loop()
{
// nothing to do
}

STM32Duino ADC does't give sampled data

I try original O-Scope project (PigOScope) without touchscreen based on STM32F103C8T6 Bluepill board, but got some problem:
I used newest rogerclarkmelbourne/Arduino_STM32 Core and downloaded
pingumacpenguin/STM32-O-Scope sketch. I compiled and uploaded it to the device via UART from 0x08000000 address. Then I started the device. The grid and coordinate lines were displayed on the screen. Also on the screen were displayed inscriptions below 0.0 uS/Sample etc... But any noise or pulse signal from PB1 on my Probe. Why the chart is not drawn?
Also I tried to log my steps in Usart in DMA activation code function:
void takeSamples()
{
// This loop uses dual interleaved mode to get the best performance out of
the ADCs
Serial.println("Init DMA");
dma_init(DMA1);
dma_attach_interrupt(DMA1, DMA_CH1, DMA1_CH1_Event);
Serial.println("Enable DMA for ADC");
adc_dma_enable(ADC1);
dma_setup_transfer(DMA1, DMA_CH1, &ADC1->regs->DR, DMA_SIZE_32BITS,
dataPoints32, DMA_SIZE_32BITS, (DMA_MINC_MODE |
DMA_TRNS_CMPLT));// Receive buffer
Serial.println("Set DMA transfer");
Serial.println(maxSamples / 2);
dma_set_num_transfers(DMA1, DMA_CH1, maxSamples / 2);
dma1_ch1_Active = 1;
Serial.println("Enable the channel and start the transfer");
dma_enable(DMA1, DMA_CH1); // Enable the channel and start the transfer.
samplingTime = micros();
Serial.println(samplingTime);
while (dma1_ch1_Active); // SOME BUG OR WHAT.... PROGRAM STOP HERE!!!
samplingTime = (micros() - samplingTime);
Serial.println("Disable DMA");
dma_disable(DMA1, DMA_CH1); //End of trasfer, disable DMA and Continuous
mode.
}
Event handler for stop interrupt
static void DMA1_CH1_Event()
{
dma1_ch1_Active = 0;
}
Volatile flag to stop routine
volatile static bool dma1_ch1_Active = 0;
Program keep crushing on while loop i think... And program does't work beyond takeSamples() function.
Why program does't exit the loop?

Ada i2c demo on the microbit with the Ada Drivers Library?

Overview:
I'm trying to program a microbit with Ada using the Ada Drivers Library and I can't understand how to use the i2c functions to establish communications with another chip. I'd like to establish a simple demo so I can understand what's happening because the demos in the components directory of the Ada Drivers Library are going over my head (I'm pretty new to Ada too and that doesn't help matters).
The simplest i2c demo in the Ada Drivers Library appears to be for the AK8963 three axis compass (located in /components/src/motion/ak8963/). But that's still going over my head and I don't have the chip to run and debug the code.
Here's what I've tried:
I've created two different demos with arduinos. In both demos the transmitter sends an 'A' and then a 'B' all the way to 'Z' and then loops back to 'A'. In the first demo the master transmits the next character every 500 ms and the slave receives it. And in the second demo the master requests the next character every 500 ms and the slave transmits it.
My demos are adapted from the arduino Wire examples found here and here.
I figured it out.
Let's start with the two Arduino programs to prove that the Arduino code works.
Arduino Slave transmit:
/*
Sends the next letter of the alphabet with each
request for data from master.
Watch the serial monitor to see what's happening.
*/
#include <avr/wdt.h>
#include <Wire.h>
// A note about I2C addresses.
// The Ada program is looking for the slave on address 16
// but this code says the slave is on 8.
// What's happening? As best as I can tell it works
// like this:
// 16 in binary is 10000. But arduino strips the read/write bit
// (which is the last bit) off of the address so it becomes
// 1000 in binary. And 1000 in binary is 8.
const int SLAVE_ADDRESS = 8;
byte letter = 65; // letter A
unsigned long counter = 0;
void setup()
{
wdt_reset();
wdt_enable(WDTO_8S);
Serial.begin(9600);
Serial.println("beginning");
Wire.begin(SLAVE_ADDRESS); // join i2c bus
Wire.onRequest(requestEvent); // register event
}
void loop()
{
wdt_reset();
counter++;
if(counter % 1000 == 0)
{
// Display a heart beat so we know the arduino has not hung.
Serial.print("looping: ");
Serial.println(counter);
}
delay(5);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void requestEvent()
{
// send the current letter on I2C
Wire.write(letter);
Serial.print("transmitting: ");
Serial.println(char(letter));
letter++;
if(letter > 90) // if greater than Z
{
letter = 65; // reset to A
}
}
Arduino Master receive:
/*
Requests a character from the slave every 500 ms and prints it
to the serial monitor.
*/
#include <avr/wdt.h>
#include <Wire.h>
const int SLAVE_ADDRESS = 8;
void setup()
{
wdt_reset();
wdt_enable(WDTO_8S);
Wire.begin(); // join i2c bus
Serial.begin(9600);
}
void loop()
{
// reset the watchdog timer
wdt_reset();
// request one byte from the slave
Wire.requestFrom(SLAVE_ADDRESS, 1);
while(Wire.available()) // slave may send less than requested
{
// receive a byte as character
char c = Wire.read();
Serial.println(c);
}
delay(500);
}
These two Arduino sketches will happily pass characters all day. Now replace the Arduino master receiver with the Ada version below and physically disconnect the Arduino master receiver.
Ada master receiver (main.abd):
-- Request a character from the I2C slave and
-- display it on the 5x5 display in a loop.
with HAL.I2C; use HAL.I2C;
with MicroBit.Display; use MicroBit.Display;
with MicroBit.I2C;
with MicroBit.Time;
procedure Main is
Ctrl : constant Any_I2C_Port := MicroBit.I2C.Controller;
Addr : constant I2C_Address := 16;
Data : I2C_Data (0 .. 0);
Status : I2C_Status;
begin
MicroBit.I2C.Initialize (MicroBit.I2C.S100kbps);
if MicroBit.I2C.Initialized then
-- Successfully initialized I2C
Display ('I');
else
-- Error initializing I2C
Display ('E');
end if;
MicroBit.Time.Delay_Ms (2000);
MicroBit.Display.Clear;
loop
-- Request a character
Ctrl.Master_Receive (Addr => Addr, Data => Data, Status => Status);
-- Display the character or the error
if Status = Ok then
Display (Character'Val (Data (0)));
else
MicroBit.Display.Display (Status'Image);
end if;
-- Give the user time to read the display
MicroBit.Time.Delay_Ms (1000);
MicroBit.Display.Clear;
MicroBit.Time.Delay_Ms (250);
end loop;
end Main;
And here is the Ada project file for completeness:
with "..\..\Ada_Drivers_Library\boards\MicroBit\microbit_zfp.gpr";
project I2C_Master_Receive_Demo is
for Runtime ("ada") use Microbit_Zfp'Runtime ("Ada");
for Target use "arm-eabi";
for Main use ("main.adb");
for Languages use ("Ada");
for Source_Dirs use ("src");
for Object_Dir use "obj";
for Create_Missing_Dirs use "True";
package Compiler renames Microbit_Zfp.Compiler;
package Linker is
for Default_Switches ("ada") use Microbit_Zfp.Linker_Switches & ("-Wl,--print-memory-usage", "-Wl,--gc-sections", "-U__gnat_irq_trap");
end Linker;
package Ide is
for Program_Host use ":1234";
for Communication_Protocol use "remote";
for Connection_Tool use "pyocd";
end Ide;
end I2C_Master_Receive_Demo;
Tips:
you need to observe the I2C address offsets (16 in Ada = 8 on Arduino in my case). See the explanation in the comments of the slave transmit arduino code above. It took me a long time to figure that out.
nothing worked with three devices connected to the I2C bus, even if one of them was not powered. I don't know exactly what's happening there but I suspect its related to documentation stating that the I2C bus cannot pull its lines back to HIGH. Some documentation recommends placing a resistor on both I2C lines connected to your source voltage so the line voltages return to HIGH after the devices pulls them low.
this work would be easier with an oscilloscope. I could have figured out this problem much more quickly if I had had one.
I have not been able to test the code below, but it should at least give you some direction. Please note that the micro:bit acts as a master. I don't think the micro:bit can act as a slave on a I2C bus (but I might be wrong here). Also note that you may have to change the path to the microbit_zfp.gpr in the project file.
default.gpr
with "../Ada_Drivers_Library/boards/MicroBit/microbit_zfp.gpr";
project Default is
for Runtime ("ada") use MicroBit_ZFP'Runtime ("Ada");
for Target use "arm-eabi";
for Main use ("main.adb");
for Languages use ("Ada");
for Source_Dirs use ("src");
for Object_Dir use "obj";
for Create_Missing_Dirs use "True";
package Compiler renames MicroBit_ZFP.Compiler;
package Linker is
for Default_Switches ("Ada") use
MicroBit_ZFP.Linker_Switches &
("-Wl,--print-memory-usage",
"-Wl,--gc-sections",
"-U__gnat_irq_trap");
end Linker;
end Default;
main.adb
with MicroBit.Display; use MicroBit.Display;
with MicroBit.Time; use MicroBit.Time;
with MicroBit.I2C; use MicroBit.I2C;
with HAL.I2C; use HAL.I2C;
procedure Main is
begin
MicroBit.I2C.Initialize (S400kbps); -- Change to desired speed.
declare
Ctrl : constant Any_I2C_Port := MicroBit.I2C.Controller;
Addr : constant I2C_Address := 16#08#; -- Change to correct address.
Data : I2C_Data (0 .. 0);
Status : I2C_Status;
begin
loop
-- Data to be send (here: character 'x').
Data (0) := Character'Pos ('x');
-- Display a dot to indicate where we are.
Display ('.');
-- Send 1 byte of data (length of Data array is 1).
Ctrl.Master_Transmit (Addr, Data, Status);
-- Additional status checking could be done here....
-- Display a colon to indicate where we are.
Display (':');
-- Wait for response (1 byte as the length of the Data array is 1).
Ctrl.Master_Receive (Addr, Data, Status);
-- Check status, and display character if OK.
if Status = Ok then
Display (Character'Val (Data (0)));
else
Display ('!');
end if;
-- Take a short nap (time in milliseconds).
Sleep (250);
end loop;
end;
end Main;
I have a current interest in the BBC micro:bit and i2c and tried the program, having earlier got a program to build and upload successfully. Building with these two files have should have easier, still not got it to build, struggling with GPS ... I'll try again soon...

cannot jump into arduino boot loader

I want to jump from my application to the bootloader ( I load via bluetooth and have an application command to jump to the boot loader).
the following work :
void* bl = (void *) 0x3c00;
goto *bl;
or
asm volatile { jmp BOOTL ::}
asm volatile { .org 0x3c00
BOOTL: }
(but code size grows to 0x3c00)
BUT, the most obvious option
asm volatile { jmp 0x3c00 ::}
does not (seems it does not even produce code }
Any idea why ?
The question as stated is not clear, as to what is working and what is failing. And about your environment, which is important. That said I guess your stating the void and/or "jmp BOOTL" work as desired, but makes the code appear to be huge.
I tried it on Arduino IDE 1.0.5 and only saw less than a 1/2K of code. Note 16K or Huge.
void* bl = (void *) 0x3c00;
void setup()
{
// put your setup code here, to run once:
}
void loop()
{
goto *bl;
// put your main code here, to run repeatedly:
}
with a compile output of...
Binary sketch size: 474 bytes (of a 32,256 byte maximum)
Estimated used SRAM memory: 11 bytes (of a 2048 byte maximum)
I suspect your observation is that the linker is seeing the pointer out at 0x3C00 the location of the BOOTSECTOR (noting it is at end of code) So it only looks like it is huge. I suspect there is a lot of white space between you may want to use the "avr-objdump.exe -d output.elf" to see what it is actually doing, versus what you expect.
0x3C00 is a 16-bit word address.
Use 0x7800 in GCC if you are using goto. GCC uses byte address (0x3C00 * 2 = 0x7800).
Example:
void *bl = (void *) 0x7800;
goto *bl;
will create the following assembly language (see *.lss output file):
c4: 0c 94 00 3c jmp 0x7800 ; 0x7800 <__stack+0x6d01>
#define GO_TO_ADRR_FLASH_MEMORY_BOOT_LOADER asm volatile ("JMP 0x7800")
GO_TO_ADRR_FLASH_MEMORY_BOOT_LOADER;

Resources