Pull to refresh

Comments 9

Вроде бы в cmsis определена функция NVIC_SetVector?
Как часть mbed.
Сам делаю похожим на описанный в статье способ, но все является статическим массивом указателей в классе InterruptManager
А не проще завести указатель на функцию, и в обработчике вызывать функцию по этому указателю? А уже сам указатель менять как вам надо. Или расходы на +1 вызов имеют значение?
Я вот даже использую для этих целей статический член класса. И могу описывать прерывание внутри объекта, и каждый тип «драйвера» имеет свой обработчик. крайне читаемый код получается
просьба выложить пример кода с таким стат. обработчиком в классе
В целом я делаю так.

Тут описание
Это просто идея, которая мне нравится. Я не делают время-критичных устройств и лишний call-другой мне погоду не делает.
Как мне кажется, каждый такой «драйвер» получается крайне читаемым с точки зрения алгоритма работы. Но это лишь мое мнение

К примеру, у нас есть класс для уарт.
  class Uart
  {
  public:
    Uart (short ch, word bd, bool doinit);
    Uart (short ch, word bd, word isrptr = 0);
    ~Uart(void);

    static class Uart *self;
    static void isr (void);
    //тут какой-то код
  protected:
    //тут всякое разное
    USART_TypeDef* Reg;
  };


static class Uart *self;

Данный указатель на себя нужен для того, чтобы получить доступ к самому классу изнутри статической функции — нашего обработчика прерываний. Так как хоть привязки к классу нет, но статический метод имеет доступ к другим статическим членам класса.
USART_TypeDef* Reg
Является указателем на нужный нам регистр нужного порта для автоматизации.
В инициализации он устанавливается через блок switch, к примеру, так:
__disable_irq();
self = this;
....
switch (channel)
.....
Reg = (USART_TypeDef*) USART1_BASE;
IRQ_VECTOR_TABLE[USART1_IRQn + IRQ0_EX] = isrptr;
....
init();
__enable_irq();


Где наш обработчик выглядит так
class Uart *Uart::self = nullptr;

...


void Uart::isr(void)
  {
    if (self->Reg->SR & USART_SR_RXNE) //receive
      {
        self->Reg->SR &= ~USART_SR_RXNE;
      }
    else if (self->Reg->SR & USART_SR_TC) //transfer
      {
        self->Reg->SR &= ~USART_SR_TC;
      }
  }


Это наш базовый placeholder, тут можно выполнять нужные нам действия.
Теперь мы поверх этого класса наследуемся, скажем, в класс GPS

class Gps: public Uart
{
public:
	Gps(short ch, word bd) :
			Uart::Uart(ch, bd, (word) &gpsisr)
	{
		Gps::self = this;
	}
	static void gpsisr(void);
	static class Gps *self;
};


Спокойно пишем свой обработчик внутри GPS класса

void Gps::gpsisr(void)
{
	if (self->Reg->SR & USART_SR_RXNE)
	{
		short a = self->Reg->DR;
		self->Reg->SR &= ~USART_SR_RXNE;
		if (self->ready)
		{
			return;
		}

		if (0 == self->nmeastr_len && '$' != a)
		{
			return;
		}	
.....................И.Т.Д.
	}
}


Каждый объект — устройство, работающий поверх UART при создании ставят свой обработчик в таблицу прерываний, сохраняя предыдущее значение. В деструкторе восстанавливают предыдущее состояние.

Всё то же самое для SPI, I2C, DMA и прочих SDIO
Я писал для ARM7, там все было проще — есть регистры в которых хранятся указатели на обработчики прерываний, просто записывал в такой регистр новое значение и все. Понадобилось такое, когда нужно было обрабатывать четные и нечетные прерывания от таймера или периферии немного по-разному… Оказалось быстрее написать два обработчика и переключаться между ними в конце обработки очередного прерывания, чем писать «if» внутри единого обработчика.
одно не понял, а зачем так мудрено копировать ?!!! или это компилятор Си так поизвращался?

<source lang=«XML»
CopyVectorTable:
ldr r0, =_sivector //Записываем в регистр R0 начальный адрес таблицы прерываний во flash-памяти
ldr r1, =_svector //Записываем в регистр R1 начальный адрес таблицы прерываний в оперативной памяти
MOV R4, R1
ldr r2, =_evector //Записываем в регистр R2 конечный адрес таблицы прерываний в оперативной памяти
loop:
LDR R3, [R0]+4!
STR R3, [R1]+4!
CMP R2, R3
BNE LOOP

ldr r2, =VTOR //Записываем адрес регистра VTOR в регистр r2
str r4, [r2] //Записываем по адреcу, содержащемся в r2 значение r0
ой, правильнее
CMP R1, R2
нужно было сделать в цикле…
Sign up to leave a comment.

Articles