Como os manipuladores de interrupção são implementados no CMSIS do Cortex M0?

9

Eu tenho um kit LPC1114. Nos últimos dias, estive pesquisando a implementação do CMSIS do Cortex M0 para descobrir como as coisas são feitas nele. Até agora, entendi como cada registro é mapeado e como posso acessá-lo. Mas ainda não sei como as interrupções são implementadas nele. Tudo o que sei sobre interrupções no CMSIS é que existem alguns nomes de manipuladores de interrupção mencionados no arquivo de inicialização. E eu posso escrever meus próprios manipuladores simplesmente escrevendo uma função C com os mesmos nomes mencionados no arquivo de inicialização. O que me confunde é que, no guia do usuário, é informado que todo o GPIO pode ser usado como fonte de interrupção externa. Mas há apenas 4 interrupções do PIO mencionadas no arquivo de inicialização. Então me conte:

  1. Como posso implementar manipuladores de interrupção externos para outros GPIOs?
  2. Onde a tabela de interrupções é mapeada no CMSIS?
  3. Quais são as principais diferenças entre o NVIC e a implementação de interrupção nos AVRs / PICs? (exceto que o NVIC pode ser mapeado em qualquer lugar no flash)
0xakhil
fonte

Respostas:

14

As informações a seguir complementam a excelente resposta de Igor.

De uma perspectiva de programação C, os manipuladores de interrupção são definidos no arquivo cr_startup_xxx.c (por exemplo, arquivo cr_startup_lpc13.c para LPC1343). Todos os manipuladores de interrupção possíveis são definidos lá como um alias WEAK. Se você não definir seu próprio XXX_Handler () para uma fonte de interrupção, será usada a função do manipulador de interrupção padrão definida neste arquivo. O vinculador classificará qual função incluir no binário final junto com a tabela de vetores de interrupção de cr_startup_xxx.c

Exemplos de interrupções de GPIO a partir de portas são mostrados nos arquivos de demonstração em gpio.c. Há uma entrada de interrupção na porta NVIC por GPIO. Cada bit individual na porta pode ser ativado / desativado para gerar uma interrupção nessa porta. Se você precisar de interrupções nas portas PIO1_4 e PIO1_5, por exemplo, habilite os bits de interrupção individuais PIO1_4 e PIO1_5 no GPIO0IE. Quando a função do manipulador de interrupções PIOINT0_Handler () é acionada, você decide quais interrupções PIO1_4 ou PIO1_5 (ou ambas) estão pendentes lendo o registro GPIO0RIS e manipulando a interrupção adequadamente.

Austin Phillips
fonte
10

(Observe que os pontos 1 e 2 são detalhes de implementação e não limitações arquitetônicas.)

  1. Nos chips NXP maiores (como o LPC17xx), existem alguns pinos de interrupção dedicados (EINTn) que possuem seu próprio manipulador de interrupções. O restante dos GPIOs precisa usar uma interrupção comum (EINT3). Você pode pesquisar o registro de status de interrupção para ver quais pinos acionaram a interrupção.
  2. Não estou muito familiarizado com o LPC11xx, mas parece que ele tem uma interrupção por porta GPIO. Você novamente terá que verificar o registro de status para descobrir os pinos específicos. Também existem até 12 pinos que podem atuar como fontes de ativação. Não tenho certeza se você pode sequestrá-los como interrupções gerais (ou seja, elas provavelmente só serão acionadas quando estiverem no estado de suspensão).
  3. A tabela do manipulador padrão é colocada no endereço 0 (que está em flash). A primeira entrada é o valor de redefinição para o registro SP, a segunda é o vetor de redefinição e o restante são outras exceções e vetores de interrupção. Alguns dos primeiros (como NMI e HardFault) são corrigidos pelo ARM, o restante é específico de chip. Se você precisar alterar os vetores no tempo de execução, poderá remapear para a RAM (é necessário primeiro copiar a tabela). No LPC11xx, o remapeamento é fixado no início da SRAM (0x10000000), outros chips podem ser mais flexíveis.
  4. O NVIC é otimizado para manipulação eficiente de interrupções:
    • nível de prioridade programável de 0 a 3 para cada interrupção. Uma interrupção de prioridade mais alta antecipa as de prioridade mais baixa (aninhamento). A execução da prioridade mais baixa é retomada quando a interrupção de prioridade mais alta é concluída.
    • empilhamento automático do estado do processador na entrada de interrupção; isso permite gravar manipuladores de interrupção diretamente em C e remove a necessidade de wrappers de montagem.
    • encadeamento: em vez de estourar e empurrar o estado novamente, a próxima interrupção pendente é tratada imediatamente
    • chegada tardia: se uma interrupção de prioridade mais alta chegar ao empilhar o estado do processador, ela será executada imediatamente em vez da anteriormente pendente.

Como você conhece os PICs, consulte esta Nota sobre o aplicativo: Migrando dos microcontroladores PIC para o Cortex-M3

É sobre o M3, mas a maioria dos pontos também se aplica ao M0.

Igor Skochinsky
fonte
8

As respostas de Austin e Igor são detalhadas o suficiente. No entanto, quero responder de outra maneira, talvez você ache útil.

O LPC11xx (Cortex-M0) possui 4 níveis para pinos GPIO, todos os pinos do GPIO0.0 ao GPIO0.n compartilham o mesmo número de interrupção e todos os pinos do GPIO3.0 ao GPIO3.m compartilham o mesmo número de interrupção.

Há seis etapas para inicializar a interrupção do GPIO no LPC11xx

  1. Configure a função do pino modificando os Registros do bloco de conexão do pino.
  2. Configure a direção dos pinos modificando o registro de direção dos dados GPIO (o valor padrão é a entrada).
  3. Configure a interrupção para cada pino individual, você deve ir para o registro da máscara de interrupção GPIO GPIOnIE e definir a lógica do bit (que corresponde ao pino) 1.
  4. Configure a interrupção para a borda ascendente ou descendente ou ambas, modificando os registros de detecção de interrupção do GPIO GPIOnIBE e GPIOnIS.
  5. Habilite a fonte de interrupção PIO_0 / PIO_1 / PIO_2 / PIO_3 no Nested Vectored Interrupt Control usando funções CMSIS.
  6. Defina a prioridade de interrupção usando as funções do CMSIS.

Implementações de código. Você precisa de duas funções: uma inicializa 6 etapas acima e a segunda é o manipulador de interrupções, que deve ter o mesmo nome do manipulador definido no startup_LPC11xx.sarquivo de códigos de inicialização . Os nomes são de PIOINT0_IRQHandlerpara PIOINT3_IRQHandler. Se você usar um nome diferente, precisará alterar os nomes no arquivo de inicialização.

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}
Phuong Pham
fonte