I have an ARM stm32f107 chip. I'm porting a project from IAR to GCC
IAR provides the following functions to enable and disable interrupts:
#define __disable_interrupt() ...
#define __enable_interrupt() ...
How do I enable / disable interrupt for my chip using GCC?
When developing for the STM32, RM0008 is your best friend. From Section 10.2.4 on page 199:
To generate the interrupt, the interrupt line should be configured and
enabled. This is done by programming the two trigger registers with
the desired edge detection and by enabling the interrupt request by
writing a ‘1’ to the corresponding bit in the interrupt mask register.
So you need to set the appropriate mask bits in the appropriate registers. For external interrupts, that's the EXTI_IMR and EXTI_EMR registers. There are many others.
I can't answer for ARM but the same function in Coldfire boils down to setting/clearing the Interrupt Priority Level masking register in the CPU. Setting it to the highest number disables/ignores all but non-maskable, setting it to 0 enables all (YMMV).
Worth noting that it's handy to read-back the value when "disabling" and restore when "enabling" to ensure that stacked interrupts don't break each other:
ipl = DisableInts(); // Remember what the IPL was
<"Risky" code happens here>
EnableInts(ipl); // Restore value
This is useful when twiddling interrupt masks, which may cause spurious interrupts, or doing stuff that shouldn't be interrupted.
Functions come out as:
uint8 DisableInts(void)
{
return(asm_set_ipl(7));
}
uint8 EnableInts(uint8 ipl)
{
return(asm_set_ipl(ipl));
}
Both of which map to this asm:
asm_set_ipl:
_asm_set_ipl:
/* Modified for CW7.2! */
link A6,#-8
movem.l D6-D7,(SP)
move.l D0,D6 /* save argument */
move.w SR,D7 /* current sr */
move.l D7,D0 /* prepare return value */
andi.l #0x0700,D0 /* mask out IPL */
lsr.l #8,D0 /* IPL */
andi.l #0x07,D6 /* least significant three bits */
lsl.l #8,D6 /* move over to make mask */
andi.l #0x0000F8FF,D7 /* zero out current IPL */
or.l D6,D7 /* place new IPL in sr */
move.w D7,SR
movem.l (SP),D6-D7
//lea 8(SP),SP
unlk A6
rts
The ARM Documentation says that _enable_irq(); compiles to “CPSIE I” that means Clear All Masks. On the other hand _disable_irq(); compiles to Set Mask.
Related
I wrote the following code to program a STM32F439 microcontroller based on the ARM Cortex-M4 processor core. I defined a timer interrupt handler that is triggered every time when TIM7 counts to the end of 1 second so that it executes a specified piece of code every second. The contents of functions InitRCC() (which initialises RCC to enable GPIOs) and ConfGPIO() (which configures GPIO pins) are omitted.
#include "main.h"
#include "stm32f439xx.h"
#include "core_cm4.h"
#include "gpioControl.h"
// Configure Timer 7 and automatically start the timer
void ConfTimer7()
{
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
// Reset the peripheral interface
RCC->APB1RSTR |= RCC_APB1RSTR_TIM7RST;
// Wait a minimum of two clock cycles
__ASM("NOP");
__ASM("NOP");
// Clear the reset bit
RCC->APB1RSTR &= ~(RCC_APB1RSTR_TIM7RST);
// Wait a minimum of two clock cycles
__ASM("NOP");
__ASM("NOP");
// Disable Timer 7
TIM7->CR1 &= ~(TIM_CR1_CEN);
// Clear the prescaler register of Timer 7
TIM7->PSC &= ~(TIM_PSC_PSC_Msk);
// Set the prescaler value of Timer 7 to 24
TIM7->PSC |= 2499; // Timer 7 frequency = 42*10^6/(2499+1) = 16.8 kHz
// Clear the auto-reload register of Timer 7
TIM7->ARR &= ~(TIM_ARR_ARR_Msk);
// Set the count to 16800 (count to 1s)
TIM7->ARR |= 16800;
// Set Timer 7 to run in "free-run" mode
TIM7->CR1 &= ~(TIM_CR1_OPM);
// Enable timer interrupt for Timer 7
TIM7->DIER |= TIM_DIER_UIE;
// Enable Timer 7
TIM7->CR1 |= TIM_CR1_CEN;
}
void TIM7_IRQHandler()
{
TIM7->SR &= ~(TIM_SR_UIF); // Clear the timer interrupt flag
// Code to be executed every second
}
int main(void)
{
InitRCC();
ConfGPIO();
// Disable interrupts before configuring the system
_disable_irq();
// Set the timer interrupt to priority 0
NVIC_SetPriority(TIM7_DAC_IRQn, 0);
// Configure Timer 7
ConfTimer7();
// Enable the global interrupt system
_enable_irq();
while (1)
{
}
}
When I tried to build the target in Keil µVision 5, the following warnings and errors are shown:
src\main.c(526): warning: #223-D: function "_disable_irq" declared implicitly
_disable_irq();
src\main.c(529): error: #20: identifier "TIM7_DAC_IRQn" is undefined
NVIC_SetPriority(TIM7_DAC_IRQn, 0);
src\main.c(535): warning: #223-D: function "_enable_irq" declared implicitly
_enable_irq();
How to fix these errors and warnings? Are there any more header files I need to add so that functions void _enable_irq(void) and void _disable_irq(void) and the identifier "TIM7_DAC_IRQn" are defined? Or is there any alternative to these functions or identifiers in the existing header files?
Never include core_cm4.h or stm32f439xx.h directly.
You need to define the correct part number macro STM32F439xx using a command line flag eg: -DSTM32F439xx.
After that you should only include "stm32f4xx.h". This will include the correct CMSIS headers which define _enable_irq and _disable_irq and all the valid IRQ numbers for the part.
Regarding TIM7_DAC_IRQn, this is incorrect. The DAC shares an interrupt with TIM6, and TIM7 has its own separate one. Chose either TIM6_DAC_IRQn or TIM7_IRQn.
Vector table definitions is in the startup files, not CMSIS. Vector table has to be laced in that particular place in memory so the appropriate linker script is also needed.
To have all of those missed pieces I would rather recommend to use CubeMx, create the project and extract needed files. Linker script, startup files etc etc.
After a major refactoring of an embedded system (IAR C on TI CC2530), I've ended up in the following situation:
After basic initialization of peripherals and global interrupt enable, the execution incorrectly ends up in an interrupt handler that communicates with external hardware. Since this hardware is not ready (remember, we end up in the ISR incorrectly), the program freezes triggering a watchdog reset.
If I insert 1, 2, 3, 5, 6, 7 etc NOPs in main(), everything works fine.
But If I insert 0, 4, 8 etc NOPs, I get the faulty behaviour.
CC2530 fetches 4 bytes of instructions from flash memory, on 4-byte boundaries.
This tells me that something is misaligned when it comes to code memory, but I simply doesn't know where to start. Nothing has changed when it comes the target settings AFAIK.
Anyone here who has seen this situation before, or can point me in the right direction?
#include <common.h>
#include <timer.h>
#include <radio.h>
#include <encryption.h>
#include "signals.h"
#include "lock.h"
#include "nfc.h"
#include "uart1_trace.h"
#include "trace.h"
//------------------------------------------------------------------------------
// Public functions
//------------------------------------------------------------------------------
void main(void)
{
setTp;
// Initialize microcontroller and peripherals
ClockSourceInit();
WatchdogEnable();
PortsInit();
TraceInit();
Timer4Init();
SleepInit();
RadioInit();
Uart1Init();
LoadAesKey("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
clrTp;
NfcInit();
__enable_interrupt();
asm("nop");
// Initialize threads
LockInit();
while (true)
{
WDR();
LockRun();
}
}
void NfcInit(void)
{
// Enable wake up interrupt on external RF field present
// The process for enabling interrupts is described in section 2.5.1 in the CC2530 datasheet.
// Configure interrupt source: interrupt on falling edge, Port 0, pin 7:0
PICTL |= BIT(0);
// 1. Clear port 0 individual interrupt flag. Read-modify-write is not allowed.
// Writing 1 to a bit in this register has no effect, so 1 should be written to flags that are not to be cleared.
P0IFG = ~BIT(3);
// Clear port 0 interrupt flag. This register is bit-accessible.
P0IF = 0;
// 2. Set pin 3 interrupt-enable
P0IEN |= BIT(3);
// 3. Set port 0 interrupt-enable
IEN1 |= BIT(5);
// 4. Global interrupt enable is set in main()
}
// Interrupt handler: falling edge on signal Wake.
// This interrupt will only occur when device is powered off and NFC field present.
// When device is powered on, VCORE is always asserted.
#pragma vector = 0x6B
__interrupt static void NFC_WAKE_ISR(void)
{
static uint16 cnt = 0;
TracePutUint16(cnt); TracePuts("\r\n");
if (++cnt > 10)
IEN1 &= ~BIT(5);
P0IFG = ~BIT(3); // Clear port 1 individual interrupt flag. Read-modify-write is not allowed.
P0IF = 0; // Clear port 1 CPU interrupt flag. This register is bit-accessible.
return;
Screenshot of software init.
CH1 = External interrupt signal, active low (signal Wake).
CH2 = TP in main.c (setTp / clrTp).
The reset button on CC-debugger seems not to be debounced, so the TP signal turns on and off a few times before stabilizing (should not be an issue). VCC is stable long before the reset. When TP goes low for the last time, all peripherals are initialized.
An external NFC IC is used to wake up the MCU from sleep mode when a NFC field is present. The NFC IC is powered by one of the CC2530 I/O-pins. Normally the IC is powered off to preserve power. In this state, the energy from the NFC field is enough to generate the wake signal (active low). When this signal is detected by the MCU, it wakes up, applies power to the NFC IC, and NFC communication starts.
The NFC IC generates the signal either when powered, or when a NFC field is present.
After reset, all I/O-pins are configured as inputs with pullups. This pulled up input is enough to power the NFC IC, which is why the wake-signal is generated. Immediatly after reset, the I/O is configured (in function PortsInit()), and power to NFC IC is turned off. This makes the wake signal go low. The slow rise- and fall times are probably due to a capacitor, that I will now remove.
Here is where things get weird. Despite the wake signal being low, the external interrupt is configured for falling edge and pending int flag is cleared right before global in enabled, I end up in the ISR a few ms later (not seen in the screen shot). But only with the right number of NOPs, as described above.
If I add a > 15 ms delay before global int enable, all is fine. This coincides with the time measured from TP low to wake high.
One might think that the int is incorrectly configured for active low, but in that case I should get multiple ints, and I don't. Also, that does not explain the magic NOPs...
Compiler generated ISR assembly code:
// 77 // Interrupt handler: falling edge on signal Wake.
// 78 // This interrupt will only occur when device is powered off and NFC field present.
// 79 // When device is powered on, VCORE is always asserted.
// 80 #pragma vector = 0x6B
RSEG NEAR_CODE:CODE:NOROOT(0)
// 81 __interrupt static void NFC_WAKE_ISR(void)
NFC_WAKE_ISR:
// 82 {
PUSH A
MOV A,#-0xe
LCALL ?INTERRUPT_ENTER_XSP
; Saved register size: 15
; Auto size: 0
// 83 static uint16 cnt = 0;
// 84
// 85 TracePutUint16(cnt); TracePuts("\r\n");
; Setup parameters for call to function PutUint16
MOV R4,#(TPutc & 0xff)
MOV R5,#((TPutc >> 8) & 0xff)
MOV DPTR,#??cnt
MOVX A,#DPTR
MOV R2,A
INC DPTR
MOVX A,#DPTR
MOV R3,A
LCALL PutUint16
; Setup parameters for call to function TPuts
MOV R2,#(`?<Constant "\\r\\n">` & 0xff)
MOV R3,#((`?<Constant "\\r\\n">` >> 8) & 0xff)
LCALL TPuts
// 86
// 87 if (++cnt > 10)
MOV DPTR,#??cnt
MOVX A,#DPTR
ADD A,#0x1
MOV R0,A
INC DPTR
MOVX A,#DPTR
ADDC A,#0x0
MOV R1,A
MOV DPTR,#??cnt
MOV A,R0
MOVX #DPTR,A
INC DPTR
MOV A,R1
MOVX #DPTR,A
CLR C
MOV A,R0
SUBB A,#0xb
MOV A,R1
SUBB A,#0x0
JC ??NFC_WAKE_ISR_0
// 88 IEN1 &= ~BIT(5);
CLR 0xb8.5
// 89
// 90
// 91 P0IFG = ~BIT(3); // Clear port 1 individual interrupt flag. Read-modify-write is not allowed.
??NFC_WAKE_ISR_0:
MOV 0x89,#-0x9
// 92 P0IF = 0; // Clear port 1 CPU interrupt flag. This register is bit-accessible.
CLR 0xc0.5
// 93
// 94 return;
MOV R7,#0x1
LJMP ?INTERRUPT_LEAVE_XSP
REQUIRE _A_P0
REQUIRE P0IFG
REQUIRE _A_P1
REQUIRE _A_IEN1
REQUIRE _A_IRCON
////////////////////////////////////////////////////////////////////////////////
// lnk51ew_CC2530F64.xcl: linker command file for IAR Embedded Workbench IDE
// Generated: Mon May 24 00:00:01 +0200 2010
//
////////////////////////////////////////////////////////////////////////////////
//
// Segment limits
// ==============
//
// IDATA
// -----
-D_IDATA0_START=0x00
-D_IDATA0_END=0xFF
//
// PDATA
// -----
// We select 256 bytes of (I)XDATA memory that can be used as PDATA (see also "PDATA page setup" below)
-D_PDATA0_START=0x1E00
-D_PDATA0_END=0x1EFF
//
//
// IXDATA
// ------
-D_IXDATA0_START=0x0001 // Skip address 0x0000 (to avoid ambiguities with NULL pointer)
-D_IXDATA0_END=0x1EFF // CC2530F64 has 8 kB RAM (NOTE: 256 bytes are used for IDATA)
//
//
// XDATA
// -----
-D_XDATA0_START=_IXDATA0_START
-D_XDATA0_END=_IXDATA0_END
//
// NEAR CODE
// ---------
-D_CODE0_START=0x0000
-D_CODE0_END=0xFFFF // CC2530F64 has 64 kB code (flash)
//
// Special SFRs
// ============
//
// Register bank setup
// -------------------
-D?REGISTER_BANK=0x0 // Sets default register bank (0,1,2,3)
-D_REGISTER_BANK_START=0x0 // Start address for default register bank (0x0, 0x8, 0x10, 0x18)
//
// PDATA page setup
// ----------------
-D?PBANK_NUMBER=0x1E // High byte of 16-bit address to the PDATA area
//
// Virtual register setup
// ----------------------
-D_BREG_START=0x00
-D?VB=0x20
-D?ESP=0x9B //Extended stack pointer register location
////////////////////////////////////////////////////////////////////////////////
//
// IDATA memory
// ============
-Z(BIT)BREG=_BREG_START
-Z(BIT)BIT_N=0-7F
-Z(DATA)REGISTERS+8=_REGISTER_BANK_START
-Z(DATA)BDATA_Z,BDATA_N,BDATA_I=20-2F
-Z(DATA)VREG+_NR_OF_VIRTUAL_REGISTERS=08-7F
-Z(DATA)PSP,XSP=08-7F
-Z(DATA)DOVERLAY=08-7F
-Z(DATA)DATA_I,DATA_Z,DATA_N=08-7F
-U(IDATA)0-7F=(DATA)0-7F
-Z(IDATA)IDATA_I,IDATA_Z,IDATA_N=08-_IDATA0_END
-Z(IDATA)ISTACK+_IDATA_STACK_SIZE#08-_IDATA0_END
-Z(IDATA)IOVERLAY=08-FF
//
// ROM memory
// ==========
//
// Top of memory
// -------------
-Z(CODE)INTVEC=0
-Z(CODE)CSTART=_CODE0_START-_CODE0_END
//
// Initializers
// ------------
-Z(CODE)BIT_ID,BDATA_ID,DATA_ID,IDATA_ID,IXDATA_ID,PDATA_ID,XDATA_ID=_CODE0_START-_CODE0_END
//
// Program memory
// --------------
-Z(CODE)RCODE,DIFUNCT,CODE_C,CODE_N,NEAR_CODE=_CODE0_START-_CODE0_END
//
// Checksum
// --------
-Z(CODE)CHECKSUM#_CODE0_END
//
// XDATA memory
// ============
//
// Stacks located in XDATA
// -----------------------
-Z(XDATA)EXT_STACK+_EXTENDED_STACK_SIZE=_EXTENDED_STACK_START
-Z(XDATA)PSTACK+_PDATA_STACK_SIZE=_PDATA0_START-_PDATA0_END
-Z(XDATA)XSTACK+_XDATA_STACK_SIZE=_XDATA0_START-_XDATA0_END
//
// PDATA - data memory
// -------------------
-Z(XDATA)PDATA_Z,PDATA_I=_PDATA0_START-_PDATA0_END
-P(XDATA)PDATA_N=_PDATA0_START-_PDATA0_END
//
// XDATA - data memory
// -------------------
-Z(XDATA)IXDATA_Z,IXDATA_I=_IXDATA0_START-_IXDATA0_END
-P(XDATA)IXDATA_N=_IXDATA0_START-_IXDATA0_END
-Z(XDATA)XDATA_Z,XDATA_I=_XDATA0_START-_XDATA0_END
-P(XDATA)XDATA_N=_XDATA0_START-_XDATA0_END
-Z(XDATA)XDATA_HEAP+_XDATA_HEAP_SIZE=_XDATA0_START-_XDATA0_END
-Z(CONST)XDATA_ROM_C=_XDATA0_START-_XDATA0_END
//
// Core
// ====
-cx51
////////////////////////////////////////////////////////////////////////////////
//
// Texas Instruments device specific
// =================================
//
// Flash lock bits
// ---------------
//
// The CC2530 has its flash lock bits, one bit for each 2048 B flash page, located in
// the last available flash page, starting 16 bytes from the page end. The number of
// bytes with flash lock bits depends on the flash size configuration of the CC2530
// (maximum 16 bytes, i.e. 128 page lock bits, for the CC2530 with 256 kB flash).
// Note that the bit that controls the debug interface lock is always in the last byte,
// regardless of flash size.
//
-D_FLASH_LOCK_BITS_START=(_CODE0_END-0xF)
-D_FLASH_LOCK_BITS_END=_CODE0_END
//
// Define as segment in case one wants to put something there intentionally (then comment out the trick below)
-Z(CODE)FLASH_LOCK_BITS=_FLASH_LOCK_BITS_START-_FLASH_LOCK_BITS_END
//
// Trick to reserve the FLASH_LOCK_BITS segment from being used as normal CODE, avoiding
// code to be placed on top of the flash lock bits. If code is placed on address 0x0000,
// (INTVEC is by default located at 0x0000) then the flash lock bits will be reserved too.
//
-U(CODE)0x0000=(CODE)_FLASH_LOCK_BITS_START-_FLASH_LOCK_BITS_END
//
////////////////////////////////////////////////////////////////////////////////
According to TI, that part has got an 8051 core. Apart from being dinosaur crap, 8051 is an 8-bitter so alignment does not apply.
When random modifications to the code result in completely unrelated errors or run-away code, it is most often caused by one of these things:
You got a stack overflow, or
You got undefined behavior bugs, such as uninitialized variables, array out of bounds access etc.
Also ensure that all ISRs are registred in the interrupt vector table.
EDIT after question change 6/4:
You should normally not return from interrupts! I don't know how your specific setup works, but with a general embedded systems compiler, the non-standard interrupt keyword means two things:
Ensure that the calling convention upon entering the ISR is correct, by stacking whatever registers the CPU/ABI state are not stacked by hardware, but software.
Ensure that the same registers are restored upon leaving the ISR and that the correct return instruction is used.
On 8051 this means that the disassembled ISR absolutely must end with a RETI instruction and not a RET instruction! Chances are high that return results in RET which will sabotage your stack. Disassemble to see if this is indeed the case.
The user's guide for the CC2530 states:
The instruction that sets the PCON.IDLE bit must be aligned in a
certain way for correct operation. The first byte of the assembly
instruction immediately following this instruction must not be placed
on a 4-byte boundary.
This is likely why the system fails on NOP multiples of four.
Just below the warning, there is an implementation for fixing this alignment specifically targeted at the IAR compiler.
I am starting to believe that this is a hardware issue, related to the connection between CC2530 and the NFC IC.
The power and reset to the NFC IC that sends the external interrupt request is controlled by a CC2530 I/O pin with 20 mA current drive capacity. At reset, before execution of the program starts, all the I/O pins defaults to inputs with internal weak pull-up. It seems like the current through the pull-up resistor is enough to power up the NFC IC. The interrupt signal from the NFC IC is high whenever the NFC is powered or a NFC field is present, and inverted by a FET transistor before reaching CC2530. Hence the ISR is triggered by a falling edge on the input.
So what happens at startup is that the NFC IC is incorrectly powered on (and later off, when the ports are initialized), and the WAKE signal falls and rises very slowly due to the poor drive capacity of a pull-up (to make things worse, a large capacitor of 1 uF is connected in parallel with the gate of the FET, and another 1uF filters the NFC IC power pin).
WAKE is supposed to trigger an interrupt only on falling edge, but staying in the transition region for up to 10 ms as seen in the oscilloscope screenshot above seems to cause CC2530 to fire the interrupt even when WAKE is rising. The ISR starts to communicate with the NFC IC via SPI, but at this time, the NFC IC seems to be messed up due to the spurious transitions on VCC and reset. It refuses to respond, the execution halts in the ISR and the watchdog bites. And the process starts over, forever.
When I insert a delay that ensures WAKE to be stable high before enabling the interrupt, all is well.
If I remove the 1 uF cap on the FET gate, WAKE rises very quickly, and there is no need for the delay anymore. And when I add a 4k7 pulldown to the NFC power, it is no longer powered up at reset.
Problem seems to be solved. The refactoring rearranged the code and changed the startup sequence, which led to a different delay that revealed the issue. With the proper hardware update, no delay will be needed.
But what still disturbes me is that I don't understand the magic NOPs. When CC2530 had the interrupt enabled and encountered a slowly rising WAKE, it wouldn't always end up incorrectly in the ISR. And when it did, I could always make it run by adding 1..3 NOPs. Naturally, whenever I added or removed a line of code, the number of NOPs required changed, which as you can imagine, drove me crazy.
It took me some time to narrow things down, and I am very grateful to all your comments and proposed solutions, especially Clifford that forced me to bring out the oscilloscope.
I'm trying to use SysTick_Handler in SW4STM32 for Linux, but whenever the SysTick interrupt is triggered, execution jumps to somewhere in system memory. By my understanding, it should jump into the void SysTick_Handler(void) that I declared, or failing that, into Default_Handler declared in startup_stm32.s where the interrupt vector table is defined. I set a break point in my SysTick_Handler, but it is never reached. In the code below, it gets through init_systick() and stays in the endless for loop if I don't include SysTick_CTRL_TICKINT_Msk, as expected, but when I do include it, the debugger tells me it ends up somewhere around address 0x1fffda7c.
main.c:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick->LOAD = 43999;
SCB->SHP[1] |= 0x40000000L;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
}
int main(void)
{
init_systick();
for(;;);
}
I verified from the .map file that the linker is using the declared SysTick_Handler instead of Default_Handler.
I also tried the following variation to use the standard peripheral library for setup, along with other interrupt priority values, with the same results:
#include "stm32f0xx.h"
volatile uint32_t ticks = 0;
void SysTick_Handler(void) {
ticks++;
}
void init_systick(void) {
SysTick_Config(44000);
NVIC_EnableIRQ(SysTick_IRQn);
NVIC_SetPriority(SysTick_IRQn, 0);
}
int main(void)
{
init_systick();
for(;;);
}
This shouldn't be relevant, but since the target doesn't have a timing crystal, I also modified void SetSysClock(void) in system_stm32f0xx.c to use the HSI clock and PLL, which appears to be working correctly:
static void SetSysClock(void)
{
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) ;
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
RCC->CR &= ~RCC_CR_PLLON;
while (RCC->CR & RCC_CR_PLLRDY) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PLLMUL & ~RCC_CFGR_PLLSRC) | RCC_CFGR_PLLMUL11; // PLL takes 8 MHz HSI / 2 as input
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY)) ;
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) ;
}
-- EDIT: More info requested in the comments --
It's an M0 core, so it doesn't have vector table relocation. From the reference manual section 2.5 (page 44):
Unlike Cortex ® M3 and M4, the M0 CPU does not support the vector table relocation.
Address 0x00000000 should be mapped either to FLASH memory at 0x08000000, system memory at 0x1fffd800, or to SRAM at 0x20000000. The memory at address 0x00000000 matches system memory at 0x1fffd800, even though SYSCFG_CFGR1 MEM_MODE is set to 00, which should map FLASH memory there. Main FLASH memory at address 0x08000000 contains the correct vector table, but address 0x00000000 is populated with address 0x1fffd99d for the SysTick vector (and all other non-NULL vectors except the reset vector, which is 0x1fffdc41); the vectors shown by the debugger at address 0x00000000 are consistent with the observed behavior. All of this information was collected while paused at a breakpoint at address 0x08000298 (a correct position in FLASH memory where the correct code has been loaded), before executing the interrupt.
The VTOR is referenced in the arm-v6 reference manual. It is a good idea to check this.
https://static.docs.arm.com/ddi0419/d/DDI0419D_armv6m_arm.pdf
0xE000ED08 VTOR RW 0x00000000a Vector Table Offset Register, VTOR on page B3-231
That reference manual is for the arm-v6M architecture which is the architecture for cortex-m0 processors. This is the bible. Most of the generic cortex-m0 features of the stm line will not be mentioned in stm refrenece manuals, just in the arm arm.
I'll reiterate, check the VTOR.
And make sure you are building for the right line of STM32F030!
The STM32F030x4 and STM32F030x6 micros have a different memory map than the STM32F030x8.
This sounds like there might be a problem with the linker file.
Can you verify that your linker file has something that looks like the following? (if it is a default file it will probably be far more complex).
. = 0;
.text 0 :
{
*(.vector);
crt0*(.text*);
main*(.text*);
*(.text*);
} > flash
Basically what this is saying is that the 'text' (code) of the program starts at address 0x0, and the first thing to put there is the vector table, followed by startup code, main, and then other code.
You will also then want to check that you have some file specifying this vector table's contents that also agrees it should be at address 0x0. This example is from an ATSAMD21E18A.
.section .vector, "a", %progbits
.equ stack_base, 0x20004000
.word stack_base
.word reset_handler
.word nmi_handler
.word hardfault_handler
.word 0
// ...
.word 0
.word systick_handler
// ...
The important thing is that it is marked as the section vector which the linker file will try to put at 0x0. For a processor with VTOR (like the M0+), this table might be specified in C without any special markings, and its location won't matter, but since you don't have VTOR, you need to make sure the linker knows to put this section right at 0x0.
jonnconn is correct. VTOR is the issue. I just wanted to add to his post (but cannot because my reputation isn't high enough) that I ran into this issue recently with a new CubeMx project.
VTOR is set to 0 on reset. VTOR is initialized in low level init before main. SystemInit in system_stm32l4xx.c (or similar for your part) seems to only initialize VTOR if USER_VECT_TAB_ADDRESS is defined. But CubeMx had this commented out. I had to uncomment it (it is in the same file), and leave VECT_TAB_OFFSET as 0. Afterwards, VTOR config was correctly set to FLASH_BASE (assuming VECT_TAB_SRAM was left undefined).
According the datasheet, that region is the system memory (for built-in bootloader). You may try two things:
Double check BOOTx pins to make sure the MCU loads FLASH instead of the system memory.
Make sure you assigned SCB->VTOR to the correct address of your own interrupt vector table.
I'm trying to program a PIC12C508A to do a simple LED learning circuit. I've read some examples, the Microchip Datasheet, pic12c508a.h and pic12c508a.inc. I've tried to set the TRIS register using a C program and an ASM program but it does not take. Using MPLAB X, the XC8 compiler, and the built in simulator to check the SFR registers I can see that the TRIS is not updating even when the WREG holds the correct values. If anyone has experience with this please check out my code and see if I am doing something wrong.
#include <xc.h>
// -- CONFIG
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config WDT = OFF // Turn Watchdog Timer Off.
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#pragma config OSC = IntRC // Internal RC Oscillator
// -- Internal Frequency
#define _XTAL_FREQ 400000
int main()
{
TRIS = 0b111010; // 0x3A
//---0-0 Set GP0 and GP2 as outputs
GPIO = 0b000100; // 0x04
//---1-0 Set GP2 as HIGH and GP0 as LOW
for(int countdown = 10; countdown > 0; --countdown) {
__delay_ms(60000); // Delay 1 minute.
}
GPIO = 0b000001; // 0x01
//---0-1 Set GP2 as LOW and GP0 as HIGH
while(1)
NOP();
}
I also tried it an assembly which is pretty much identical to the Gooligum tutorials for baseline PIC models.
list p=12c508a
#include <p12c508a.inc>
__CONFIG _MCLRE_ON & _CP_OFF & _WDT_OFF & _IntRC_OSC
RCCAL CODE 0x0FF ; Processor Reset Vector
res 1 ; Hold internal RC cal value, as a movlw k
RESET CODE 0x000 ; RESET VECTOR
movwf OSCCAL ; Factory Calibration
start
movlw b'111010' ; Configure GP0/GP2 as outputs
tris GPIO ;
movlw b'000100' ; Set GP2 HIGH - GREEN LED
movwf GPIO
goto $ ; loop forever
END
This all seems pretty straight forward but when I use breakpoints and examine the SFR registers in the simulator I can see that the GPIO and TRIS registers never are changed even though the WREG will hold the correct values. I've examined the ASM output that the XC8 compiler generates and it is almost identical to the ASM I wrote when it comes to setting the registers.
I've also tried using HEX values and straight integer values and the results are the same.
The answer is that the crystal frequency defined at the top of the program was way beyond the real actual value
#define _XTAL_FREQ 400000 //that's 400KHz INTOSC, impossible
Instead it should be
#define _XTAL_FREQ 4000000 //That's 4MHz INTOSC
#Justin pointed it out in the comment below it's original post.
First, in order to use GP2 as an output, do you need to clear the T0CS in the OPTION register ?
Second, I observe this in the manual:
Note: A read of the ports reads the pins, not the output data latches.
That is, if an output driver on a pin is enabled and driven high, but
the external system is holding it low, a read of the port will
indicate that the pin is low.
but I guess the simulator will assume the external system is not holding down the pin.
Third, BCF and BSF instructions look like a better way of waggling GP2 and GP0 independent of whatever else is going on in the GPIO.
I'm sorry, but other than that I don't know what to suggest.
You can try different GPIO, because according to the documentation, GP2 may be controlled by the option register.
I am new to the processor STM32F107. I have to read the input value from an external source that is a balance. This balance is external to the board that contains the processor and communicates with it via PA4.
What I have to do to read the analogue input from the balance?
Here is my first attempt to read the input from the balance.
I use this function to setup the ADC:
void ADC_Configuration(void) {
ADC_InitTypeDef ADC_InitStructure;
/* PCLK2 is the APB2 clock */
/* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
/* Enable ADC1 clock so that we can talk to it */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* Put everything back to power-on defaults */
ADC_DeInit(ADC1);
/* ADC1 Configuration ------------------------------------------------------*/
/* ADC1 and ADC2 operate independently */
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/* Disable the scan conversion so we do one at a time */
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
/* Don't do contimuous conversions - do them on demand */
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
/* Start conversin by software, not an external trigger */
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
/* Conversions are 12 bit - put them in the lower 12 bits of the result */
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/* Say how many channels would be used by the sequencer */
ADC_InitStructure.ADC_NbrOfChannel = 1;
/* Now do the setup */ ADC_Init(ADC1, &ADC_InitStructure);
/* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE);
/* Enable ADC1 reset calibaration register */
ADC_ResetCalibration(ADC1);
/* Check the end of ADC1 reset calibration register */
while(ADC_GetResetCalibrationStatus(ADC1));
/* Start ADC1 calibaration */
ADC_StartCalibration(ADC1);
/* Check the end of ADC1 calibration */
while(ADC_GetCalibrationStatus(ADC1));
}
And I use this function to get the input:
u16 readADC1(u8 channel) {
ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5);
// Start the conversion
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// Wait until conversion completion
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
// Get the conversion value
return ADC_GetConversionValue(ADC1);
}
The problem is that in N measurements of the same weight, I get N different results.
For example, the weight is 70kg and the output of the readADC1(ADC_Channel_4) is 715,760,748,711,759 etc.
What am I doing wrong?
The balance has a load cell inside it, which generates an analog voltage. The processor on the balance is some how not utilized (i assume this as not much details are present in your question).
The stm32 f107 controller has an on-chip ADC (analog to digital convertor). Connect the output of load cell (analog signal coming from balance) to the analog input pin of stm32 f107. Configure the ADC to sample and convert the analog signal into digital (use the example code as reference to write the software).
PA4 is multiplexed with ADC12_IN4 (an analogue input that can itself be mapped to channel 4 on either ADC1 or ADC2).
Programming the ADC, selecting the correct peripheral clocking and mapping multiplexed pins on STM32 is somewhat complex, but I strongly suggest that you utilise the STM32F10x Standard Peripheral Library which provides an API for all STM32F10x peripherals as well as numerous examples of how to use the library, including ADC examples.
The ADC itself may be polled, interrupt driven, use DMA, and be software triggered or free-running, self-clocked or clocked from a timer peripheral. The options are numerous, only some combinations are covered by the example code, but it is a good place to start nonetheless. To understand all the options and how to use them with the Standard Peripheral Library, you will need a fairly thorough understanding of the Reference Manual.
Another resource you may find useful is STM's MicroXplorer. This allows visual configuration and allocation of multiplexed pins and generates source code you can use directly.
Furthermore you may need some hardware signal conditioning at the input to ensure that the input is within the valid and tolerable range of the ADC input.