Страница 1 из 4

STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 09:44:48
settz0r
Добрый день, написал самый обычный код в KEIL UVISION с миганием диода и установкой прерывания через EXTI. Хочу запрограммировать плату через бутлодер по адресу 0х8004000(начальный адрес 0х8000000). Для этого пишу такие строчки:
__set primask(0); // глобальное запрещение прерываний
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8004000); // непосредственно перенос таблицы прерываний
__set_PRIMASK(0); // разрешение прерываний
В настройках проекта нужный адрес выставил в Target и Linker.
Достаточно ли этих строчек для переноса или нужно что то еще? Код без EXTI или SysTick без проблем переноситься, а с прерыванием никак не получается.
Сразу говорю, с HAL'ом и STM32CubeMX не работаю. Работаю с SPL.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 10:31:37
VladislavS
Всё гораздо проще. Просто записываешь в SCB->VTOR адрес таблицы векторов прерываний и всё.

Я это обычно вообще в приложении всегда делаю. В GCC и IAR так

Код: Выделить всё

#ifdef __GNUC__
  extern void *__vtor;
  SCB->VTOR = (uint32_t)&__vtor;
#endif
#ifdef __ICCARM__
  #pragma section = ".intvec"
  SCB->VTOR = (uint32_t) __section_begin(".intvec");
#endif
Приложение само знает где у него таблица прерываний. Мало ли, сегодня с одного адреса его скомпилируешь, завтра с другого. Или вообще в SRAM для отладки будешь грузить. Главное грузить в тот адрес с которого приложение скомпилировано.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 12:15:26
Myp3ik
Нужно указать смещение

Код: Выделить всё

NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);
И при вызове из бутлодера

Код: Выделить всё

#define APPLICATION_ADDRESS			((uint32_t)0x08004000)

/* Адрес стека */
uint32_t stackAddr = (*(__IO uint32_t*)APPLICATION_ADDRESS);
	
/* Проверка сигнатуры */
if ((stackAddr & 0x2FFE0000) == 0x20000000)
{ 
    /* Инициализация указателя стека */
    __set_MSP(stackAddr);
		
    /* Запуск программы */
    ((void(*)(void))(*(__IO uint32_t*)(APPLICATION_ADDRESS + sizeof(uint32_t))))();
}

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 12:32:03
settz0r
[uquote="VladislavS",url="/forum/viewtopic.php?p=3488650#p3488650"]Всё гораздо проще. Просто записываешь в SCB->VTOR адрес таблицы векторов прерываний и всё.

Я это обычно вообще в приложении всегда делаю. В GCC и IAR так

Код: Выделить всё

#ifdef __GNUC__
  extern void *__vtor;
  SCB->VTOR = (uint32_t)&__vtor;
#endif
#ifdef __ICCARM__
  #pragma section = ".intvec"
  SCB->VTOR = (uint32_t) __section_begin(".intvec");
#endif
Приложение само знает где у него таблица прерываний. Мало ли, сегодня с одного адреса его скомпилируешь, завтра с другого. Или вообще в SRAM для отладки будешь грузить. Главное грузить в тот адрес с которого приложение скомпилировано.[/uquote]

Все равно не работает

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 12:48:12
dosikus
[uquote="settz0r",url="/forum/viewtopic.php?p=3488576#p3488576"]__set primask(0); // глобальное запрещение прерываний
....
__set_PRIMASK(0); // разрешение прерываний.[/uquote]


Ну и где оно запрещает а где разрешает? :)))
__enable_irq() эквивалент __set_PRIMASK(0)


__disable_irq() эквивалент __set_PRIMASK(1)

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 12:59:10
VladislavS
В CMSIS есть __enable_irq() и __disable_irq(). Куда уж проще?

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 13:08:09
settz0r

Код: Выделить всё

 #include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_exti.h"
#include "misc.h"
#include "Uart_lib.h"

GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; 
EXTI_InitTypeDef EXTI_InitStructure;
USART_InitTypeDef USART_InitStructure;
uint16_t a = 0, b = 0;



	void Init_led(void)
	{
		
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
		
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);
		
	}
	
//----------------------------------------------------------
//
//----------------------------------------------------------
	
	void USART1_Init(void)
	{
		
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
		
		/*NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);*/
    /* Configure USART1 Tx (PA.09) as alternate function push-pull */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure USART1 Rx (PA.10) as input floating */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    /* Configure the USART1 */
 
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
    USART_Init(USART1, &USART_InitStructure);
		USART_ITConfig(USART1, USART_IT_TXE, ENABLE);
  
	}
	
//----------------------------------------------------------
//
//----------------------------------------------------------
	
/*	void USART1_Handler(void)
	{
	
		if (USART_GetFlagStatus(USART1, USART_IT_TXE) == SET)
		{
			
		USART_ClearITPendingBit(USART1, USART_IT_TXE);
		
	}
	}*/
//----------------------------------------------------------
//
//----------------------------------------------------------
/*void Send_Uart(USART_TypeDef* USARTx, unsigned char c)
	{
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET){}
 USART_SendData(USARTx, c);
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET){}	
	} */
	//uint32_t flash_read(uint32_t address) {
//	return (*(__IO uint32_t*) address);
//	} 
		
	
//----------------------------------------------------------
//
//----------------------------------------------------------
	
	void EXTI_NVIC_Init ()
	{
		
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
		
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
		GPIO_Init(GPIOA, &GPIO_InitStructure);
		
		GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource0);
		EXTI_InitStructure.EXTI_Line = EXTI_Line0;
		EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
		EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
		EXTI_InitStructure.EXTI_LineCmd = ENABLE;
		EXTI_Init(&EXTI_InitStructure);
		
		NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStructure);
		
	}

//----------------------------------------------------------
//
//----------------------------------------------------------
	
	void EXTI0_IRQHandler()
	{
		
		EXTI_ClearITPendingBit(EXTI_Line0);
		GPIO_ResetBits(GPIOA, GPIO_Pin_1);
		//a++;
		
//----------------------------------------------------------
//
//----------------------------------------------------------
		
}
	void Led(void)
	{
			if(a==1) {
				while((b<10000)&(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==1)) 
					{
					b++;
				}
				if(b==10000)
					{
				GPIO_ResetBits(GPIOA, GPIO_Pin_1);
		}
	}
	if (a>1) {
		a=0;
		while((b<10000)&(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)==1)) {
					b++;
				}
				if(b==10000) 
					{
		GPIO_SetBits(GPIOA, GPIO_Pin_1);
	}
	}
}
//----------------------------------------------------------
//
//----------------------------------------------------------
	
		int main(void)
		{
			NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x4000);
			Init_led();
			EXTI_NVIC_Init();
			USART1_Init();
			GPIO_SetBits(GPIOA, GPIO_Pin_1);
			__enable_irq();
			
			while(1)
			{
			//Led();
			}
			
		}


выше код программы, здесь код бутлодера. Что не так?
[/code] void Go_to_App(void){
uint32_t app_jmp_address;

typedef void(*pFunction)(void);
pFunction Jamp_To_Application;

__disable_irq();

app_jmp_address = *(uint32_t*)(Boot_end +4);
Jamp_To_Application = (pFunction)app_jmp_address;
__set_MSP(*(__IO uint32_t*)Boot_end);

Jamp_To_Application();
}

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 13:45:38
VladislavS
settz0r ,

1. Имей уважение. Портянки в спойлер [спойлер] и [код] оформляй, если хочешь чтобы их читали.

2. Сначала программу отладь без бутлоадера в том месте в котором она должна работать.

3. Отлаживай бутлоадер.

2ALL:, вы внутрь NVIC_SetVectorTable заглядывали? Пишите SCB->VTOR = 0x8004000; и не сношайте себе мозги.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Вт окт 23, 2018 18:00:30
AlanDrakes
Попробую ответить на самое первое сообщение.
У меня в проекте, загрузчик располагается в начальных адресах и занимает не более 16кБ кода (я ему выделил такую секцию, оставшихся 48кБ от 64кБ общей памяти, для основной прошивки достаточно).

Алгоритм действий (исключая кучу проверок на наличие и корректность прошивки на месте):
1. Отключить ВСЕ задействованные загрузчиком прерывания.
2. Сбросить задействованную загрузчиком периферию.
3. Считать начальные адреса прошивки (AppSect + 0, AppSect + 4)
4. Выполнить:

Код: Выделить всё

jump_app (0x80004000);

void jump_app( uint32_t address ) {
   __asm ("LDR SP, [R0]\n"); // Load new stack pointer address
   __asm ("LDR PC, [R0, #4]\n");  //Load new program counter address;
}; 
При выполнении данной процедуры, безвозвратно теряются данные процесса работы и ядро, фактически переходит на вектор запуска.
Опять же, в моём случае, процедура переноса векторов прерывания, происходит прямо в самом приложении (а так же в загрузчике) самостоятельно, потому изначально загрузчику за этим следить не нужно.

Делаю я это так:

Код: Выделить всё

uint32_t i;
i = (uint32_t)(&main);	// Получаем адрес main()
SCB->VTOR = (i & 0xFF030000);
Всё. Две инструкции в коде.
Естественно, необходимо следить за тем, чтобы адрес main попадал в секцию.
Как уже говорил выше, таблица прерываний располагается (у меня) в начале секции приложения. Соответсвенно, адрес main будет указывать на нужный диапазон, затем накладывается маска и получается адрес. Это случай с тремя независимыми версиями прошивки во Flash памяти.
Либо же, приложение можно собрать с опцией VECT_TAB_SRAM и тогда начальный код позаботится о том, чтобы таблица векторов прерываний оказалась в начале RAM.
И уже не нужно будет переносить самостоятельно что-то ещё.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Ср окт 24, 2018 09:11:51
VladislavS
Вот скажите, в keil хоть что-то можно не пер анус сделать? Нормальные линкеры экспортируют адреса секций и их можно в приложении как константу использовать.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Ср окт 24, 2018 12:43:09
settz0r
AlanDrakes, при компиляции выдает ошибку, SP и PC не идентифицированы. Вопрос, где описаны данные регистры в keil? С ассемблером не особо в ладах.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Ср окт 24, 2018 14:41:32
VladislavS
Да не лезь ты в ассемблер. Стек устанавливается командой __set_MSP(adr), переход к приложению Jamp_To_Application(); на Си. Осталось только правильные адреса подставить.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Ср окт 24, 2018 18:58:46
AlanDrakes
[uquote="settz0r",url="/forum/viewtopic.php?p=3489532#p3489532"]AlanDrakes, при компиляции выдает ошибку, SP и PC не идентифицированы. Вопрос, где описаны данные регистры в keil? С ассемблером не особо в ладах.[/uquote]

Точно, в Keil свои дефайны. Уже отвык после gcc
Попробуйте так:

Код: Выделить всё

void jump_app( uint32_t address ) {
   __asm ("LDR msp, [R0]\n"); // Load new stack pointer address
   __asm ("LDR R15, [R0, #4]\n");  //Load new program counter address;
// Либо "r15" (в нижнем регистре), если так же не будет дефайна
// Можно так же выполнить
   __asm ("BX [R0, #4]\n"); // Аналогичный переход на адрес инструкцией перехода
// Не уверен, что так можно делать
}; 

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Ср окт 24, 2018 20:53:44
VladislavS
AlanDrakes, вот скажи, ЗАЧЕМ? Зачем тут асм? Единственное что ты поимеешь это геморр при переносе на другой компилятор. Я больше чем уверен, что Си код скомпилируется не хуже, чем эта вставка. К тому же, ТС и так запутался, а ты ему асмом по фэйсу.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Чт окт 25, 2018 10:26:09
Alex-Elektron
В настройках проекта нужный адрес выставил в Target и Linker.
Достаточно ли этих строчек для переноса или нужно что то еще?
Птицу в Use Memory Layout from Target Dialog ставил?

П. С. Также надо закомментить в SystemInit()

Код: Выделить всё

#ifdef VECT_TAB_SRAM
  SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else
  SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif 

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Пт окт 26, 2018 08:53:07
settz0r
VladislavS , не получается писать через SCB->VTOR, компилятор не понимает команду __vtor* , так как используется не GCC компил, а стандартный кейловский. А через ассемблеровские команды, так там вообще полный игнор половины команд.


[uquote="VladislavS",url="/forum/viewtopic.php?p=3488650#p3488650"]Всё гораздо проще. Просто записываешь в SCB->VTOR адрес таблицы векторов прерываний и всё.

Я это обычно вообще в приложении всегда делаю. В GCC и IAR так

Код: Выделить всё

#ifdef __GNUC__
  extern void *__vtor;
  SCB->VTOR = (uint32_t)&__vtor;
#endif
#ifdef __ICCARM__
  #pragma section = ".intvec"
  SCB->VTOR = (uint32_t) __section_begin(".intvec");
#endif
Приложение само знает где у него таблица прерываний. Мало ли, сегодня с одного адреса его скомпилируешь, завтра с другого. Или вообще в SRAM для отладки будешь грузить. Главное грузить в тот адрес с которого приложение скомпилировано.[/uquote]

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Пт окт 26, 2018 09:45:02
VladislavS
[uquote="settz0r",url="/forum/viewtopic.php?p=3491656#p3491656"]VladislavS , не получается писать через SCB->VTOR, компилятор не понимает команду __vtor* , так как используется не GCC компил, а стандартный кейловский.[/uquote]Да чтож за люди то пошли без толики фантазии. Ну напиши ты SCB->VTOR = 0x08004000; отладься, а потом будешь думать как кеил заставить самому эту константу подставить. Или вали ты с этого кейла, пока не подсел.

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Пн окт 29, 2018 09:20:09
Alex-Elektron
VladislavS, что ж ты так на Кейл-то взъелся? Не существует идеального эмбеддерского софта, всё определяется прямизной рук и желанием/умением разобраться в проблеме. Сам сталкивался с тем, что после кейла не хотела прошивка со смещённым адресом стартовать. ТС пробовал мои рекомендации по настройкам линкера и комментированием строк в System_Init()?

Добавлено after 27 minutes 2 seconds:
1)Прошиваем сам загрузчик с ремапом таблицы векторов!
Стопудоворабочийкод:

Код: Выделить всё

#define FLASH_DISK_START_ADDRESS    0x08002800              /* Flash start address */

void GoToUserApp(void){
	u32 appJumpAddress;
	void (*GoToApp)(void);
    
	appJumpAddress = *((volatile u32*)(FLASH_DISK_START_ADDRESS + 4));
	GoToApp = (void (*)(void))appJumpAddress;
    
    __disable_irq();
	SCB->VTOR = FLASH_DISK_START_ADDRESS;
    __enable_irq();
    
    __set_MSP(*((volatile u32*) FLASH_DISK_START_ADDRESS)); //stack pointer (to RAM) for USER app in this address
        
	GoToApp();
}
Далее, настройки основной программы:
2)Изображение
3)Изображение
4)Изображение
5)Изображение

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Пн окт 29, 2018 10:04:50
settz0r
Alex-Elektron,Линкер настроен уже давно, бутлодер такой же, закомменитить код со скрина не могу, т.к. просто напросто не могу его нигде найти.
2)Изображение

Re: STM32F103C8 перенос таблицы прерываний

Добавлено: Пн окт 29, 2018 10:10:12
VladislavS
Это я то взъёлся? :) Взъёлся это когда вот так делают[uquote="AlanDrakes",url="/forum/viewtopic.php?p=3489046#p3489046"]

Код: Выделить всё

uint32_t i;
i = (uint32_t)(&main);	// Получаем адрес main()
SCB->VTOR = (i & 0xFF030000);
Всё. Две инструкции в коде.
Естественно, необходимо следить за тем, чтобы адрес main попадал в секцию.[/uquote]вместо

Код: Выделить всё

	extern uint32_t __Vectors;
	SCB->VTOR = (uint32_t) &__Vectors;
VTOR лучше прямо в приложении перенастраивать. Вы же его до загрузки лоадером всё равно будете отлаживать.