2022-06-17 03:11 AM
I created a little assembly project on Uvision5 but I fastly reached the code size limit and I can't manage for my assembly code to run on stm32cubeide, do you know how to run assembly code please?
Solved! Go to Solution.
2022-06-17 06:17 AM
Yes, you can write assembler code with the STM32CubeIDE and compile it successfully. However, this is very rarely done because the effort required to write the program is much more complex and, above all, time-consuming compared to C programming.
In principle, you can insert assembler into any CubeIDE project, be it an existing one or a new one. However, it should be noted that the syntax of the GNU assembler probably differs in some respects from that of other assemblers.
Basically, you can add assembly code by creating it in STM32CubeIDE:
/*
* assembler.h
*
*/
#ifndef APPLICATION_USER_CORE_ASSEMBLER_H_
#define APPLICATION_USER_CORE_ASSEMBLER_H_
extern void ASM_SystemInit(void);
extern void ASM_Function(void);
#endif /* APPLICATION_USER_CORE_ASSEMBLER_H_ */
/* USER CODE BEGIN Includes */
#include "assembler.h"
/* USER CODE END Includes */
The content of assembler.s might look like this (this small example also contains an initialisation, as it was a pure ASM project):
/*
* assembler.s
*
*/
.syntax unified
.text
.global ASM_SystemInit
.global ASM_Function
.thumb_func
.equ RCC_BASE_ADDR, 0x40021000
.equ o_RCC_CR, 0x00 // offset to RCC_CR
.equ o_RCC_CFGR, 0x08 // offset to RCC_CFGR
.equ o_RCC_PLLCFGR, 0x0C // offset to RCC_PLLCFGR
.equ o_RCC_AHB2ENR, 0x4C // offset to RCC_AHB2ENR
.equ GPIOA_BASE_ADDR,0x48000000
.equ o_GPIOA_MODER, 0x00 // offset to GPIOA_MODER
.equ o_GPIOA_OTYPER, 0x04 // offset for GPIOA Output PP/OD
.equ o_GPIOA_OSPEEDR,0x08 // offset for GPIOA Output Speed
.equ o_GPIOA_PUPDR, 0x0C // offset for GPIOA PU/PD
.equ GPIOA_BSRR, 0x48000018 // GPIOA Bit set reset register
.equ COUNTER, 10000
//------------------------------------------------------------------------------------------------
ASM_SystemInit:
// Clock configuration, 16 MHz HSI as PLL clock source
LDR R1, =RCC_BASE_ADDR // Load RCC configuration register address in R1
LDR R0, =0x06000802 // PLLPDIV=0, PLLR=8, PLLQ=2, PLLP=7, PLLN=16, PLLM=1, HSI16->PLL
STR R0, [R1, o_RCC_PLLCFGR] // store into RCC_PLLCFGR
ORR R0, #0x01000000 // set PLLREN (bit 24)
STR R0, [R1, o_RCC_PLLCFGR] // store into RCC_PLLCFGR
LDR R0, [R1, o_RCC_CR] // read RCC_CR
ORR R0, #0x01000000 // set PLLON (bit 24)
STR R0, [R1, o_RCC_CR] // store into RCC_CR
ASM_SystemInit_1:
LDR R0, [R1, o_RCC_CR] // read RCC_CR
LSLS R0, #6 // Logical Shift Left by 6
BPL.N ASM_SystemInit_1 // Branch if N flag = 0 (= positive or zero, see PM0214, table 24)
[...]
BX LR // Return from function
//------------------------------------------------------------------------------------------------
ASM_Function:
turnON:
// Set output high
LDR R1, =GPIOA_BSRR
LDR R0, =0x00000020
STR R0, [R1]
LDR R2, =COUNTER
delay1:
SUBS R2, R2, #1 // R2 = R2 - 1, R2 = 0?
BNE delay1 // stay in loop delay1 if not equal to zero
turnOFF:
// Set output low
LDR R0, =0x00200000
STR R0, [R1]
LDR R2, =#COUNTER
LSL R2, #6 // Logical Shift Left
delay2:
SUBS R2, R2, #1 // R2 = R2 - 1, R2 = 0?
BNE delay2 // stay in loop delay1 if not equal to zero
delayDone:
B turnON // Jump to turnON
int main(void)
{
/* USER CODE BEGIN 1 */
ASM_SystemInit();
ASM_Function();
/* USER CODE END 1 */
}
Did that give you some inspiration and answer your question?
Regards
/Peter
2022-06-17 06:17 AM
Yes, you can write assembler code with the STM32CubeIDE and compile it successfully. However, this is very rarely done because the effort required to write the program is much more complex and, above all, time-consuming compared to C programming.
In principle, you can insert assembler into any CubeIDE project, be it an existing one or a new one. However, it should be noted that the syntax of the GNU assembler probably differs in some respects from that of other assemblers.
Basically, you can add assembly code by creating it in STM32CubeIDE:
/*
* assembler.h
*
*/
#ifndef APPLICATION_USER_CORE_ASSEMBLER_H_
#define APPLICATION_USER_CORE_ASSEMBLER_H_
extern void ASM_SystemInit(void);
extern void ASM_Function(void);
#endif /* APPLICATION_USER_CORE_ASSEMBLER_H_ */
/* USER CODE BEGIN Includes */
#include "assembler.h"
/* USER CODE END Includes */
The content of assembler.s might look like this (this small example also contains an initialisation, as it was a pure ASM project):
/*
* assembler.s
*
*/
.syntax unified
.text
.global ASM_SystemInit
.global ASM_Function
.thumb_func
.equ RCC_BASE_ADDR, 0x40021000
.equ o_RCC_CR, 0x00 // offset to RCC_CR
.equ o_RCC_CFGR, 0x08 // offset to RCC_CFGR
.equ o_RCC_PLLCFGR, 0x0C // offset to RCC_PLLCFGR
.equ o_RCC_AHB2ENR, 0x4C // offset to RCC_AHB2ENR
.equ GPIOA_BASE_ADDR,0x48000000
.equ o_GPIOA_MODER, 0x00 // offset to GPIOA_MODER
.equ o_GPIOA_OTYPER, 0x04 // offset for GPIOA Output PP/OD
.equ o_GPIOA_OSPEEDR,0x08 // offset for GPIOA Output Speed
.equ o_GPIOA_PUPDR, 0x0C // offset for GPIOA PU/PD
.equ GPIOA_BSRR, 0x48000018 // GPIOA Bit set reset register
.equ COUNTER, 10000
//------------------------------------------------------------------------------------------------
ASM_SystemInit:
// Clock configuration, 16 MHz HSI as PLL clock source
LDR R1, =RCC_BASE_ADDR // Load RCC configuration register address in R1
LDR R0, =0x06000802 // PLLPDIV=0, PLLR=8, PLLQ=2, PLLP=7, PLLN=16, PLLM=1, HSI16->PLL
STR R0, [R1, o_RCC_PLLCFGR] // store into RCC_PLLCFGR
ORR R0, #0x01000000 // set PLLREN (bit 24)
STR R0, [R1, o_RCC_PLLCFGR] // store into RCC_PLLCFGR
LDR R0, [R1, o_RCC_CR] // read RCC_CR
ORR R0, #0x01000000 // set PLLON (bit 24)
STR R0, [R1, o_RCC_CR] // store into RCC_CR
ASM_SystemInit_1:
LDR R0, [R1, o_RCC_CR] // read RCC_CR
LSLS R0, #6 // Logical Shift Left by 6
BPL.N ASM_SystemInit_1 // Branch if N flag = 0 (= positive or zero, see PM0214, table 24)
[...]
BX LR // Return from function
//------------------------------------------------------------------------------------------------
ASM_Function:
turnON:
// Set output high
LDR R1, =GPIOA_BSRR
LDR R0, =0x00000020
STR R0, [R1]
LDR R2, =COUNTER
delay1:
SUBS R2, R2, #1 // R2 = R2 - 1, R2 = 0?
BNE delay1 // stay in loop delay1 if not equal to zero
turnOFF:
// Set output low
LDR R0, =0x00200000
STR R0, [R1]
LDR R2, =#COUNTER
LSL R2, #6 // Logical Shift Left
delay2:
SUBS R2, R2, #1 // R2 = R2 - 1, R2 = 0?
BNE delay2 // stay in loop delay1 if not equal to zero
delayDone:
B turnON // Jump to turnON
int main(void)
{
/* USER CODE BEGIN 1 */
ASM_SystemInit();
ASM_Function();
/* USER CODE END 1 */
}
Did that give you some inspiration and answer your question?
Regards
/Peter
2022-06-17 07:28 AM
Most projects have a startup.s file, start by adding your code in there. At some point you can trim other files, and remove, and alter the linker script.
Publicly (.global) export the symbols so you can call as C functions.
GNU/GAS syntax is markedly different from KEIL/MDK, but the examples in startup.s should give you a understanding/template to follow.
Watch how you do comments and labels, and how it does functions without PROC/ENDP
2022-06-20 12:28 AM
Thank you very much Peter for your accurate explanation, it worked for me!
2022-06-20 12:29 AM
Thank you
2023-01-29 04:05 PM
The college has classes on assembler programming here, but they use keil.
that is pretty much windows only.
(yeah I know, "wine", but.. come on)
It would be SO NICE if someone could just set up and share a barebones assembly programming project in CUBE, that does nothing more than blink the lights, but then didnt have any of the extra stuff.
For example, showing off the SUPER_small code chunks in
https://www.mikrocontroller.net/articles/ARM-ASM-Tutorial
Pure assembly, with no #ifdefs or any C-like stuff.
I looked at the base GPIO example project for CUBE, and it has approximately a gazillion files, when all that is really needed is.. maybe 4, including a makefile?
Then more colleges could use CUBE for this sort of thing instead of keil.
2023-01-29 08:12 PM
One might hope college level professors could code a solution to the course they are teaching...
Peter has provided a good starting point.
2023-02-05 09:55 AM
no, he didnt. He replied to the actual question with basically, "I dont think you should do that, try this other way instead".
And he wrote 30 lines of code that were not asked for, instead of a much simpler output, that would actually answer the question, centered around something like
main.s
main:
nop
b main
2023-11-12 01:45 PM - edited 2023-11-12 01:50 PM
Great work, Peter!
In my eyes this solution works for a STM32F4xx - are there similar solutions for a earlier stm32f1xx?
Ok, it is a similar work for me ... very good solution also for the connection to c-code-applications.
But what is the basic-idea of my question:
Are there also more basically solutions in the integration of assembler files for a final binary upload on a stm32?
I work with a x86_64-Linux-system...
2023-11-12 02:02 PM
You can use startup.s or simply ADD foo.s to your project, or in the directory for make to process. The calling conventions are pretty simple, up to four parameters in registers, and subsequent ones passed on the stack frame. ARM defines their ABI (application binary interface).
You can generate example assembler for given C code using the disassemblers in objcopy or fromelf.
The Cortex-M3 and M4 code should be very similar, the latter allowing for FPU instructions and registers when enabled, and several more advanced commands. The Cortex-M0(+) parts add a little complication as they have a more restrictive instruction set where some operations now might take two operations, and are less tolerant of misaligned memory accesses.