#c #arm #embedded #cortex-m
#c #arm #встроенный #cortex-m
Вопрос:
Я пытаюсь получить фундаментальное представление о программировании для микроконтроллеров STM32. Я пытаюсь использовать внешнее прерывание с кнопки для переключения состояния светодиода, установив все соответствующие регистры (без внешних библиотек / определений). Кажется, я не могу заставить процедуру прерывания работать, управление никогда не передается обработчику.
Вот что я использую, чтобы настроить PC-13 (к которому прикреплена кнопка в соответствии с платой Nucleo-G070RB) в качестве внешнего прерывания:
RCC_APBENR2 |= 0b1 << 0; // Enable SYSCFGEN
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
EXTI_RTSR1 |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
EXTI_EXTICR4 |= 0x2 << 8; // Set PC-13 as GPIO pin for interrupt
EXTI_IMR1 |= 0x1 << 13; // Unmask EXTI line 13
NVIC_ISER |= 0b1 << 7; // Enable interrupts for EXTI4_15
Я не уверен, чего мне не хватает.
Вот полный код:
#include <stdint.h>
#include <stdio.h>
#define RCC_IOPENR *((uint32_t volatile *) 0x40021034)
#define RCC_APBENR2 *((uint32_t volatile *) 0x40021040)
#define GPIOA_MODER *((uint32_t volatile *) 0x50000000)
#define GPIOA_IDR *((uint32_t volatile *) 0x50000010)
#define GPIOA_ODR *((uint32_t volatile *) 0x50000014)
#define GPIOC_MODER *((uint32_t volatile *) 0x50000800)
#define GPIOC_IDR *((uint32_t volatile *) 0x50000810)
#define GPIOC_ODR *((uint32_t volatile *) 0x50000814)
#define NVIC_ISER *((uint32_t volatile *) 0xE000E100)
#define EXTI_RTSR1 *((uint32_t volatile *) 0x40021800)
#define EXTI_EXTICR1 *((uint32_t volatile *) (0x40021800 0x060 0x4 * 0))
#define EXTI_EXTICR2 *((uint32_t volatile *) (0x40021800 0x060 0x4 * 1))
#define EXTI_EXTICR3 *((uint32_t volatile *) (0x40021800 0x060 0x4 * 2))
#define EXTI_EXTICR4 *((uint32_t volatile *) (0x40021800 0x060 0x4 * 3))
#define EXTI_IMR1 *((uint32_t volatile *) (0x40021800 0x080))
#define EXTI_RPR1 *((uint32_t volatile *) (0x40021800 0x00C))
void button_init();
extern void initialise_monitor_handles();
uint8_t volatile g_button_pressed = 0;
int main(void) {
//asm volatile ("cpsie i");
initialise_monitor_handles();
//printf("Hello World!n");
RCC_IOPENR |= 0b1 << 0; // Enable PORTA
GPIOA_MODER amp;= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
GPIOA_MODER |= 0b1 << (2*5); // Set MODE bits for 5th pin (PA-5) to 01 (set output)
GPIOC_MODER amp;= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
button_init();
while(1) {
if (g_button_pressed) {
GPIOA_ODR ^= 0b1 << 5;
printf("Button pressedn");
g_button_pressed = 0;
}
}
}
void button_init() {
RCC_APBENR2 |= 0b1 << 0; // Enable SYSCFGEN
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
EXTI_RTSR1 |= 0b1 << 13; // Enable interrupt on rising edge at EXTI line 13
EXTI_EXTICR4 |= 0x2 << 8; // Set PC-13 as GPIO pin for interrupt
EXTI_IMR1 |= 0x1 << 13; // Unmask EXTI line 13
NVIC_ISER |= 0b1 << 7; // Enable interrupts for EXTI4_15
}
void EXTI4_15_IRQHandler(void) {
g_button_pressed = 1;
printf("Button pressedn");
EXTI_RPR1 |= 0xFFFF << 0;
}
Я использую STM32CubeIDE с компилятором по умолчанию и печатаю через полуохостинг с OpenOCD.
Итак, мой вопрос в том, я пропустил шаг или делаю что-то не так?
Комментарии:
1. внешние должны быть одними из самых сложных, вы сначала пробовали системный таймер, что-то, что не проходит через nvic? обработчик swi / svc. И затем скажите прерывание по таймеру, которое является внешним по отношению к ядру, но не к чипу? чтобы разобраться с основами, а затем перейти на следующий уровень к внешнему выводу?
2. Как и при разработке любого обработчика прерываний, вы проводили опрос? начиная с периферийных устройств и продвигаясь к ядру? Понимание состояния прерывания и очистка прерывания по пути? По одному слою за раз?
3. Где находится адрес обработчика прерываний, загруженный в таблицу vevtor прерываний?
4. не изобретайте велосипед и не используйте заголовки CMSIS stm32. Все регистры там уже определены.
5. @P__J__ , где я могу найти заголовки CMSIS STM32? Я только что скопировал их из проекта, созданного STM32CubeMX — есть ли лучшее место для их получения?
Ответ №1:
Итак, я понял, в чем была моя глупая ошибка. Я был так сосредоточен, пытаясь понять, где я ошибся в настройке внешнего прерывания, что на самом деле не проверил, правильно ли я настроил кнопку.
Я попытался установить PC13 в качестве входных данных:
GPIOC_MODER amp;= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
прежде чем я вызвал свою button_init()
функцию, которая включает синхронизацию для GPIOC через:
RCC_IOPENR |= 0b1 << 2; // Enable PORTC
Поскольку GPIOC не был включен, PC13 также не был установлен в качестве входного сигнала, поэтому обнаруживать было нечего.
Вот исправленный код: (я также обновил его, чтобы использовать заголовки CMSIS stm32, как предложено P__J__
)
#define STM32G070xx
#include "stm32g0xx.h"
uint8_t volatile g_button_pressed = 0;
void button_init();
//extern void initialise_monitor_handles();
int main(void) {
// initialise_monitor_handles();
//printf("Hello World!n");
RCC->IOPENR |= 0b1 << 0; // Enable PORTA
GPIOA->MODER amp;= ~(0b11 << (2*5)); // Clear MODE bits for 5th pin (PA-5)
GPIOA->MODER |= 0b1 << (2*5); // Set MODE bits for 5th pin (PA-5) to 01 (set output)
button_init();
while(1) {
if (g_button_pressed) {
GPIOA->ODR ^= 0b1 << 5;
// printf("Button pressed! Yay!n");
g_button_pressed = 0;
}
}
}
void button_init() {
// RCC->APBENR2 |= (uint32_t) (0b1 << 0); // Enable SYSCFGEN
RCC->IOPENR |= (uint32_t) (0b1 << 2); // Enable PORTC
GPIOC->MODER amp;= ~(0b11 << (2*13)); // Clear MODE bits for 13th pin (PC-13) (set input)
EXTI->RTSR1 |= (uint32_t) (0b1 << 13); // Enable interrupt on rising edge at EXTI line 13
EXTI->EXTICR[3] |= (uint32_t) (0x2 << 8); // Set PC-13 as GPIO pin for interrupt
EXTI->IMR1 |= (uint32_t) (0x1 << 13); // Unmask EXTI line 13
NVIC->ISER[0] |= (uint32_t) (0b1 << 7); // Enable interrupts for EXTI4_15
}
void EXTI4_15_IRQHandler(void) {
g_button_pressed = 1;
// printf("Button pressedn");
EXTI->RPR1 = 0b1 << 13;
}