Полный пример, без заголовочных файлов, без библиотек. Плата NUCLEO-F411RE. Некоторые STMF4 практически одинаковы, cortex-m4 будет таким же. На любом системном микроконтроллере или в другом случае вы должны работать над прерыванием процессора как можно медленнее, по одному слою / шагу за раз. Так гораздо проще.
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang @ NMI
.word hang @ HardFault
.word hang @ MemManage
.word hang @ BusFault
.word hang @ UsageFault
.word hang @ 7
.word hang @ 8
.word hang @ 9
.word hang @ 10
.word hang @ SVCall
.word hang @ DebugMonitor
.word hang @ Reserved
.word hang @ PendSV
.word hang @ SysTick
.word hang @ External interrupt 0
.word hang @ External interrupt 1
.word hang @ External interrupt 2
.word hang @ External interrupt 3
.word hang @ External interrupt 4
.word hang @ External interrupt 5
.word hang @ External interrupt 6
.word hang @ External interrupt 7
.word hang @ External interrupt 8
.word hang @ External interrupt 9
.word hang @ External interrupt 10
.word hang @ External interrupt 11
.word hang @ External interrupt 12
.word hang @ External interrupt 13
.word hang @ External interrupt 14
.word hang @ External interrupt 15
.word hang @ External interrupt 16
.word hang @ External interrupt 17
.word hang @ External interrupt 18
.word hang @ External interrupt 19
.word hang @ External interrupt 20
.word hang @ External interrupt 21
.word hang @ External interrupt 22
.word hang @ External interrupt 23
.word hang @ External interrupt 24
.word hang @ External interrupt 25
.word hang @ External interrupt 26
.word hang @ External interrupt 27
.word hang @ External interrupt 28
.word hang @ External interrupt 29
.word hang @ External interrupt 30
.word hang @ External interrupt 31
.word hang @ External interrupt 32
.word hang @ External interrupt 33
.word hang @ External interrupt 34
.word hang @ External interrupt 35
.word hang @ External interrupt 36
.word hang @ External interrupt 37
.word hang @ External interrupt 38
.word hang @ External interrupt 39
.word hang @ External interrupt 40
.word hang @ External interrupt 41
.word hang @ External interrupt 42
.word hang @ External interrupt 43
.word hang @ External interrupt 44
.word hang @ External interrupt 45
.word hang @ External interrupt 46
.word hang @ External interrupt 47
.word hang @ External interrupt 48
.word hang @ External interrupt 49
.word tim5_handler @ External interrupt 50
.word hang @ External interrupt 51
.word hang @ External interrupt 52
.word hang @ External interrupt 53
.word hang @ External interrupt 54
.word hang @ External interrupt 55
.word hang @ External interrupt 56
.word hang @ External interrupt 57
.word hang @ External interrupt 58
.word hang @ External interrupt 59
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl DOWFI
DOWFI:
wfi
bx lr
notmain.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void DOWFI ( void );
#define RCCBASE 0x40023800
#define RCC_CR (RCCBASE+0x00)
#define RCC_CFGR (RCCBASE+0x08)
#define RCC_APB1RSTR (RCCBASE+0x20)
#define RCC_AHB1ENR (RCCBASE+0x30)
#define RCC_APB1ENR (RCCBASE+0x40)
#define RCC_BDCR (RCCBASE+0x70)
#define GPIOABASE 0x40020000
#define GPIOA_MODER (GPIOABASE+0x00)
#define GPIOA_OTYPER (GPIOABASE+0x04)
#define GPIOA_OSPEEDR (GPIOABASE+0x08)
#define GPIOA_PUPDR (GPIOABASE+0x0C)
#define GPIOA_BSRR (GPIOABASE+0x18)
#define GPIOA_AFRL (GPIOABASE+0x20)
#define USART2BASE 0x40004400
#define USART2_SR (USART2BASE+0x00)
#define USART2_DR (USART2BASE+0x04)
#define USART2_BRR (USART2BASE+0x08)
#define USART2_CR1 (USART2BASE+0x0C)
#define TIM5BASE 0x40000C00
#define TIM5_CR1 (TIM5BASE+0x00)
#define TIM5_DIER (TIM5BASE+0x0C)
#define TIM5_SR (TIM5BASE+0x10)
#define TIM5_CNT (TIM5BASE+0x24)
#define TIM5_PSC (TIM5BASE+0x24)
#define TIM5_ARR (TIM5BASE+0x2C)
#define NVIC_ISER1 0xE000E104
#define NVIC_ICPR1 0xE000E284
//PA2 is USART2_TX alternate function 1
//PA3 is USART2_RX alternate function 1
static int clock_init ( void )
{
unsigned int ra;
//switch to external clock.
ra=GET32(RCC_CR);
ra|=1<<16;
PUT32(RCC_CR,ra);
while(1) if(GET32(RCC_CR)&(1<<17)) break;
ra=GET32(RCC_CFGR);
ra&=~3;
ra|=1;
PUT32(RCC_CFGR,ra);
while(1) if(((GET32(RCC_CFGR)>>2)&3)==1) break;
return(0);
}
static int uart2_init ( void )
{
unsigned int ra;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0; //enable port A
PUT32(RCC_AHB1ENR,ra);
ra=GET32(RCC_APB1ENR);
ra|=1<<17; //enable USART2
PUT32(RCC_APB1ENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<4); //PA2
ra&=~(3<<6); //PA3
ra|=2<<4; //PA2
ra|=2<<6; //PA3
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<2); //PA2
ra&=~(1<<3); //PA3
PUT32(GPIOA_OTYPER,ra);
ra=GET32(GPIOA_OSPEEDR);
ra|=3<<4; //PA2
ra|=3<<6; //PA3
PUT32(GPIOA_OSPEEDR,ra);
ra=GET32(GPIOA_PUPDR);
ra&=~(3<<4); //PA2
ra&=~(3<<6); //PA3
PUT32(GPIOA_PUPDR,ra);
ra=GET32(GPIOA_AFRL);
ra&=~(0xF<<8); //PA2
ra&=~(0xF<<12); //PA3
ra|=0x7<<8; //PA2
ra|=0x7<<12; //PA3
PUT32(GPIOA_AFRL,ra);
ra=GET32(RCC_APB1RSTR);
ra|=1<<17; //reset USART2
PUT32(RCC_APB1RSTR,ra);
ra&=~(1<<17);
PUT32(RCC_APB1RSTR,ra);
//PUT32(USART2_CR1,(1<<13));
//8000000/(16*115200) = 4.34 4+5/16
PUT32(USART2_BRR,0x45);
PUT32(USART2_CR1,(1<<3)|(1<<2)|(1<<13));
return(0);
}
static void uart2_send ( unsigned int x )
{
while(1) if(GET32(USART2_SR)&(1<<7)) break;
PUT32(USART2_DR,x);
}
static void hexstrings ( unsigned int d )
{
//unsigned int ra;
unsigned int rb;
unsigned int rc;
rb=32;
while(1)
{
rb-=4;
rc=(d>>rb)&0xF;
if(rc>9) rc+=0x37; else rc+=0x30;
uart2_send(rc);
if(rb==0) break;
}
uart2_send(0x20);
}
static void hexstring ( unsigned int d )
{
hexstrings(d);
uart2_send(0x0D);
uart2_send(0x0A);
}
void tim5_handler ( void )
{
uart2_send(0x55);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,0x00040000);
}
int notmain ( void )
{
unsigned int ra;
unsigned int rb;
clock_init();
uart2_init();
hexstring(0x12345678);
ra=GET32(RCC_APB1ENR);
ra|=1<<3; //enable TIM5
PUT32(RCC_APB1ENR,ra);
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0000);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
ra=GET32(TIM5_SR);
hexstring(ra);
while(1)
{
rb=GET32(TIM5_SR);
if(rb!=ra)
{
ra=rb;
hexstring(ra);
PUT32(TIM5_SR,0);
}
}
}
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
while(1)
{
ra=GET32(NVIC_ICPR1);
if(ra)
{
hexstring(ra);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,ra);
}
}
}
if(0)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
while(1)
{
ra=GET32(NVIC_ICPR1);
if(ra)
{
hexstring(ra);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,ra);
}
}
}
if(1)
{
PUT32(TIM5_CR1,0x0000);
PUT32(TIM5_DIER,0x0001);
PUT32(TIM5_PSC,0x0000);
PUT32(TIM5_ARR,16000000-1);
PUT32(TIM5_CNT,16000000-1);
PUT32(TIM5_CR1,0x0001);
PUT32(TIM5_SR,0);
PUT32(NVIC_ICPR1,0x00040000);
PUT32(NVIC_ISER1,0x00040000);
while(1)
{
DOWFI();
uart2_send(0x56);
}
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
строить
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
может изменить cortex-m0s на cortex-m4s.
В справочном руководстве по архитектуре cortex-m4 показаны адреса регистров NVIC. Как только вы поймете, как периферийное устройство устанавливает свой статус прерывания, вы можете включить и опросить различные регистры ожидания прерывания NVIC, пока не увидите один набор. Затем выясните, какой это номер прерывания, и посмотрите документацию ST, и он должен совпадать, в данном случае бит 18 во втором регистре (бит 50, если считать от начала до конца всех регистров) - это таймер 5, глядя в документации ST прерывание 50 - это таймер 5, так что совпадает. Документация st также говорит нам, что это адрес 0x108, который совпадает с моим подсчетом вручную.
80000fc: 08000137
8000100: 08000137
8000104: 08000137
8000108: 08000169
800010c: 08000137
8000110: 08000137
8000114: 08000137
Как только я увидел изменение регистра ожидания и подтверждение документацией того, что это было правильное прерывание, вы можете установить тот же бит в соответствующем регистре включения установки, чтобы, наконец, прерывание попало в процессор.
Создайте и скопируйте notmain.bin на виртуальный ядерный диск, и он будет печатать UV каждую секунду, когда срабатывает прерывание и просыпается wfi. Естественно, вы обычно не хотите распечатывать содержимое uart в процедуре обслуживания прерывания, но в этом случае мы знаем, что это происходит раз в секунду, ничто другое не мешает периферийному устройству, поэтому в этом конкретном случае это безопасно.
/dev/ttyACM0 в Linux или что-то подобное в Windows — это то место, где вывод uart находится на отладочной плате NUCLEO. Вы можете легко изменить это, чтобы мигать светодиодами. Заметьте я убрал защиту круглосуточного init, возня с часами может очень быстро замуровать чип. Семейство STM32 имеет внутренний загрузчик и ремешок, поэтому вы можете разблокировать его с помощью этого, но прежде чем углубляться в код инициализации часов, будьте очень осторожны и делайте это медленно, шаг за шагом, в идеале запускайте UART с помощью часов RC. чтобы вы могли видеть, что происходит, точно так же, как выше, наблюдая за тем, что происходит с прерываниями.
Вам изначально не нужно возиться с приоритетами на NVIC. Существуют установленные регистры включения и чистые регистры включения, каждый из которых при чтении скажет, что вы хотите, чтобы он был включен. Есть установленные ожидающие и четкие ожидающие, либо сообщит вам, что находится на рассмотрении, когда будет прочитано. С прерываниями в целом в любой системе вы в идеале хотите знать, как сбросить ожидающее прерывание в источнике, а затем продвигаться к процессору, некоторые конструкции микросхем, когда вы очищаете его в источнике, он очищается полностью, некоторые как этот защелка, так что вы должны очистить его в обоих местах.
Существует 16 регистров NVIC каждого типа, поэтому 512 возможных отдельных прерываний, как я уже сказал, сводят с ума кору — почти тривиально, у вас нет ни одной линии прерывания, и тогда вам нужно пробираться, чтобы увидеть, кто вызвал это, и разобраться с другими. в то время как вы очищаете первый в очереди. У вас может быть одно периферийное устройство с более чем одним прерыванием, но это одно периферийное устройство, а не все из них в системе. Они также разработали логику исключения cortex-m таким образом, что вы можете поместить функцию C (компилятор, совместимый с eabi) непосредственно в таблицу векторов, вам не нужно обертывать этот код сохранением состояния в стеке и очисткой, и вы не используйте специальный возврат из инструкции прерывания. Логика cortex-m делает все это за вас, так что поймите, что вы немного избалованы этим чипом/семейством, но ничего страшного, вы можете намочить здесь ноги, а затем работать над, возможно, более сложными конструкциями MCU. Выполняйте те же шаги, хотя и опрашивайте свой путь пошагово, где это возможно, выполняйте столько шагов, сколько вам нужно, чтобы понять периферийное устройство, прежде чем вы действительно прервете ЦП, а затем даже там, где это необходимо, в зависимости от конструкции ЦП, работайте над тем, как чтобы определить, что было ожидающим и как это очистить, и проверить наличие других прерываний перед возвратом и т. д.
person
old_timer
schedule
28.03.2017