#include "./SYSTEM/sys/sys.h"
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset) // (definir tabela de vetores de interrupção)
{
// O registro VTOR do processador Cortex-M se preocupa apenas com os 0~8 bits do endereço, e os valores dos 9~31 bits (23 bits maiores) são ignorados
// A operação AND bit a bit reserva os bits binários de 0 a 8 do endereço de deslocamento,
// Desta forma, o deslocamento do endereço da posição inicial da tabela de vetores de interrupção pode ser definido para o registrador VTOR, completando assim a operação de deslocamento da tabela de vetores de interrupção.
SCB->VTOR = endereço base | (deslocamento & (uint32_t)0xFFFFFE00);
// Método de implementação: operação AND bit a bit entre o deslocamento do endereço de deslocamento da tabela de vetor de interrupção especificada e os 9 bits mais baixos (ou seja, os 0 ~ 8 bits do endereço de deslocamento),
// Em seguida, execute uma operação OR bit a bit com a variável baseaddr e atribua o resultado ao registro VTOR.
}
static void sys_nvic_priority_group_config(uint8_t group) // (--função local-- configurar agrupamento de prioridade de interrupção NVIC,)
{
/*--Conhecimento complementar--
NVIC_PRIORITYGROUP_4 é um esquema baseado em controlador de interrupção (NVIC) para implementar agrupamento de prioridades.
É um método de agrupamento baseado na prioridade de hardware no processador Cortex-M.
Em NVIC_PRIORITYGROUP_4, existem 4 grupos de prioridade, de 0 a 3 (prioridade de alta para baixa).
Dentro de cada grupo prioritário, existem 16 níveis de prioridade. Entre elas, a prioridade 0 é o nível mais alto e a prioridade 15 é o nível mais baixo.
Dentro do mesmo grupo de prioridades, diferentes subprioridades podem ser definidas para cada interrupção
Tanto a prioridade de preempção quanto a prioridade de resposta recebem 16 níveis. A prioridade de preempção é usada para preempção entre interrupções e a prioridade de resposta é usada para controle de prioridade dentro da função de serviço de interrupção.
Tabela de vetores de interrupção ----------> manual de referência p130 tabela de páginas 54
*/
uint32_t temp, temp1;
temp1 = (~group) & 0x07; // realiza inversão bit a bit (~) no grupo de variáveis e operação AND bit a bit com número binário 0x07
temp1 <<= 8; // desloca temp1 para a esquerda em 8 bits. O objetivo é colocar o valor do grupo de prioridade entre os 15º e 13º bits do AIRCR.
temp = SCB->AIRCR; // Lê o valor do AIRCR (registro de controle de interrupção e redefinição da aplicação) no registro do bloco de controle do sistema (SCB) para temp
temp &= 0X0000F8FF; // operação AND bit a bit entre temp e 0x0000F8FF, o objetivo é definir o (grupo de prioridade de interrupção) e (subprioridade) do AIRCR como 0 para reconfiguração
temp |= 0X05FA0000; // Opera o valor de temp com 0x05FA0000 bit a bit, o número significa usar o esquema NVIC_PRIORITYGROUP_4 para codificação de prioridade. Neste cenário, o agrupamento prioritário é 0b10
temp |= temp1; // Executa uma operação OR bit a bit em temp1 e temp e coloca o valor agrupado no bit correspondente. Finalmente, os primeiros três bits são definidos como valor de agrupamento de prioridade.
SCB->AIRCR = temp; // Escreve temp em (AIRCR) no registrador (SCB), completando assim todo o processo de configuração do agrupamento de prioridade de interrupção NVCI.
}
void sys_nvic_init(uint8_t pprio, uint8_t sprio, uint8_t ch, uint8_t group) // (inicialização do controlador de interrupção NVIC)
{
sys_nvic_priority_group_config(group); // Chame a função sys_nvic_priority_group_config, o parâmetro é group, para completar a configuração do agrupamento de prioridade de interrupção NVCI
temperatura uint32_t;
temp = pprio << (4 - group); // Desloca o valor de pprio para a esquerda (4-group) e armazena o resultado em temp. O objetivo é calcular a prioridade mestre
temp |= sprio & (0x0f >> group); // Armazene os 4 bits inferiores do sprio em temp, use 0x0f para deslocar os bits para a direita (agrupar) e execute a operação AND bit a bit e mantenha os 4 bits inferiores em temp. O objetivo é calcular a partir da prioridade
temp &= 0xf; // Opera temp e 0xf bit a bit e (&) para garantir que o valor em temp não seja maior que 15.
NVIC->ISER[ch / 32] |= 1 << (ch % 32); // Define a posição da linha de interrupção correspondente no registro NVIC->ISER[ch/32] como 1 e habilita a interrupção correspondente Arame;
NVIC->IP[ch] |= temp << 4; // Desloca a temperatura de prioridade calculada em 4 bits para a esquerda e escreve-a no registro NVIC->IP[ch] para especificar a prioridade da interrupção correspondente.
}
void sys_nvic_ex_config(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint8_t tmode) //(configuração de interrupção externa NVIC)
{
deslocamento uint8_t;
uint32_t gpio_num = 0;
uint32_t pinos = 0, curpin = 0, pos = 0;
/*Descrição do parâmetro
offset é usado para armazenar o deslocamento de bit do número do pino no registro EXTICR.
gpio_num armazena o número GPIO correspondente,
pinpos e curpin são usados para percorrer e verificar cada bit no número do pino, respectivamente,
pos é usado para gerar a máscara de bits,
*/
gpio_num = ((uint32_t)p_gpiox - (uint32_t)GPIOA) / 0X400; // Calcule a diferença entre o endereço da porta GPIO GPIOA no barramento AHB e o endereço do parâmetro de entrada p_gpiox e depois divida por 0x400 para obter o número GPIO correspondente.
RCC->APB2ENR |= 1 << 0; // Habilita o clock AFIO, configura o bit do clock AFIO no registrador RCC->APB2ENR para acessar o registrador AFIO
for (pinpos = 0; pinpos < 16; pinpos++) // percorre cada bit no número do pino
{
pos = 1 << pinpos; // use pos para gerar uma máscara de bits,
curpin = pinx & pos; // Use a operação bit a bit AND (&) para obter o bit atual do número do pino
if (curpin == pos) // Se o bit atual for 1, significa que o pino precisa ser configurado com a função EXTI.
{
offset = (pinpos % 4) * 4; // Calcula seu deslocamento de bit correspondente no registro EXTICR,
AFIO->EXTICR[pinpos / 4] &= ~(0x000F << offset); // Em seguida, limpa o valor na posição especificada no registro AFIO->EXTICR[pinpos / 4]
AFIO->EXTICR[pinpos / 4] |= gpio_num << offset; // Em seguida, armazene gpio_num nesta posição de acordo com o valor de deslocamento de bit do deslocamento de deslocamento
EXTI->IMR |= 1 << pinpos; // Defina a posição do pino correspondente no registro EXTI->IMR como 1 e habilite a interrupção EXTI correspondente ao pino (ou seja, a interrupção BITx da linha pode ser habilitada ).
/*Além disso, configure o modo de disparo da linha EXTI correspondente de acordo com o modo de disparo de entrada tmode*/
if (tmode & 0x01)
EXTI->FTSR |= 1 << pinpos; // Se o bit mais baixo do tmode for 1, habilita o trigger de borda descendente;
if (tmode & 0x02)
EXTI->RTSR |= 1 << pinpos; // Se o segundo bit baixo do tmode for 1, habilite o disparo da borda ascendente.
}
}
}
void sys_gpio_remap_set(uint8_t pos, uint8_t bit, uint8_t val) //(remapeamento de pinos GPIO)
{
uint32_t temp = 0; // temp é usado para salvar a máscara de bits gerada e i é usado para percorrer os bits que precisam ser configurados.
uint8_t i = 0;
RCC->APB2ENR |= 1 << 0; // Habilita o relógio AFIO, temp é usado para salvar a máscara de bits gerada e i é usado para percorrer os bits que precisam ser configurados.
for (i = 0; i < bit; i++) // Calcula a temperatura da máscara de bits que precisa ser configurada de acordo com o parâmetro bit passado
{
temp <<= 1; // O parâmetro bit indica quantos bits precisam ser configurados, portanto é necessário gerar uma máscara cujos bits inferiores sejam todos 1,
temp += 1; // Gerado usando temp bitwise left shift e operação bit a bit OR.
}
AFIO->MAPR &= ~(temp << pos); // Limpa a posição a ser configurada no registro AFIO->MAPR. Isso pode ser feito deslocando o bit pos para a esquerda de temp e depois invertendo (~) e, em seguida, tomando o valor original implementação da operação bit a bit AND (&)
AFIO->MAPR |= (uint32_t)val << pos; // Por fim, defina os bits a serem configurados para novos valores no registro AFIO->MAPR. Você pode mover o novo valor para a esquerda por bits pos e então pressionar ou (|) para o valor original para completar a configuração.
}
//(configuração do pino GPIO)
//Definição da estrutura:
// estrutura typedef
// {
// volátil uint32_t MODER; // registro do modo de porta GPIO
// volátil uint16_t OTYPER; // registro do tipo de saída da porta GPIO
// uint16_t RESERVED0; // Reservado, o valor é 0x02
// volátil uint32_t OSPEEDR; // registro de velocidade de saída da porta GPIO
// volátil uint32_t PUPDR; // registro pull-up/pull-down da porta GPIO
// volátil uint16_t IDR; // registro de dados de entrada da porta GPIO
// uint16_t RESERVED1; // Reservado, o valor é 0x0A
// volátil uint16_t ODR; // registro de dados de saída da porta GPIO
// uint16_t RESERVED2; // Reservado, o valor é 0x0E
// volátil uint16_t BSRR; // conjunto/redefinição de registro de bits da porta GPIO
// uint16_t RESERVED3; // Reservado, o valor é 0x12
// volátil uint16_t LCKR; // registro de bloqueio de configuração da porta GPIO
// uint16_t RESERVED4; // Reservado, o valor é 0x16
// volátil uint16_t AFR[2]; // registro de função alternativa da porta GPIO
// uint16_t RESERVED5[10]; // Reservado, o valor é 0x1C-0x28
// volátil uint32_t BRR; // registrador de redefinição de bit GPIO
// } GPIO_TypeDef;
void sys_gpio_set(GPIO_TypeDef *p_gpiox, uint16_t pinx, modo uint32_t, uint32_t otype, uint32_t ospeed, uint32_t pupd)
{
/*introdução de parâmetros
p_gpiox aponta para o endereço de registro do GPIOx (x é A, B, C, etc.),
pinx representa o pino a ser configurado (pode ser múltiplo, representado por bits binários)
, mode indica o modo de funcionamento do pino (entrada, saída, multiplexação, etc.),
otype indica o tipo de saída (dreno aberto ou push-pull),
ospeed indica a taxa de saída,
pupd significa modo pull-up e pull-down. */
uint32_t pinos = 0, pos = 0, curpin = 0;
// pinpos é usado para verificar ciclicamente cada pino da porta GPIO, pos é usado para verificar se o pino precisa ser configurado e curpin é o resultado da verificação.
configuração uint32_t = 0;
// variável de configuração, o valor inicial é 0, usado para salvar uma determinada configuração de IO
for (pinpos = 0; pinpos < 16; pinpos++) // Use o loop for para processar os pinos com pinpos 0~15 por vez.
{
pos = 1 << pinpos; // Desloca 1 para a esquerda de pinpos e atribui-o a pos, ou seja, pos é usado para verificar se o pino correspondente precisa ser configurado
curpin = pinx & pos; // As informações de configuração do pino são armazenadas no pinx, use a operação de bit & para obter curpin, ou seja, se o pino precisa ser configurado.
if (curpin == pos) // Se curpin for igual a pos, significa que o pino precisa ser configurado.
{
config = 0; // Se a configuração for necessária, primeiro limpe config para indicar que o pino está configurado como modo de entrada analógica padrão.
if ((mode == 0X01) || (mode == 0X02)) // se o modo for modo de saída normal ou modo de função alternativa
{
config = ospeed & 0X03; // OU os 2 bits inferiores do parâmetro de velocidade ospeed com os 2 bits inferiores da configuração para obter bit0/1, indicando a configuração de MODE[1:0].
config |= (otype & 0X01) << 2; // OU o bit inferior do otype com o segundo bit da configuração para obter a configuração de CNF[0].
config |= (mode - 1) << 3; // Desloca o valor de mode menos 1 para a esquerda em 3 bits e então OR com o terceiro bit de config para obter a configuração de CNF[1].
}
else if (mode == 0) // Se o modo for igual a 0, é o modo de entrada normal.
{
if (pupd == 0) // Se não houver pull-up e pull-down, significa modo de entrada flutuante.
{
config = 1 << 2; // Define o segundo e terceiro bits da configuração para 0 1, o que significa modo de entrada flutuante.
}
else // Caso contrário (isto é, com modos de entrada pull-up e pull-down).
{
config = 1 << 3; // Define o segundo e terceiro bits da configuração como 1 0, indicando o modo de entrada para cima e para baixo
/*O seguinte é puxar para cima ou para baixo o pino da porta GPIO de acordo com o bit baixo do parâmetro pupd. */
p_gpiox->ODR &= ~(1 << pinpos); // Limpa o bit do pino correspondente do ODR,
p_gpiox->ODR |= (pupd & 0X01) << pinpos; // Então, de acordo com o bit inferior do pupd, mova o bit do pino correspondente para a esquerda, execute a operação OR e defina-o como pull-up ou pull-down .
}
}
/*O seguinte julga o registro CRH ou CRL onde o pin está localizado de acordo com os pinpos. */
if (pinpos <= 7) // Se o pino pinpos atualmente processado estiver nos 8 bits inferiores, o registro correspondente é o registro CRL.
{
p_gpiox->CRL &= ~(0X0F << (pinpos * 4)); // Primeiro desloca os pinos para a esquerda em 2 bits (equivalente a multiplicar por 4) para obter o deslocamento (8 bits inferiores), depois desloca 0X0F para a esquerda pouco de mudança,
// Obtém a máscara a ser escrita no registrador e então usa o código inverso para limpar o bit especificado pela máscara
p_gpiox->CRL |= config << (pinpos * 4); // Desloca a configuração para a esquerda pelo bit de deslocamento e escreve o resultado no registrador CRL.
}
else // Se a posição do pino pinpos atualmente processado estiver nos 8 bits superiores, o registro correspondente é o registro CRH.
{
p_gpiox->CRH &= ~(0X0F << ((pinpos - 8) * 4)); // O mesmo que acima, exceto que o deslocamento deve ser subtraído por 8 (8 bits altos) antes do cálculo
p_gpiox->CRH |= config << ((pinpos - 8) * 4); // Desloca a configuração para a esquerda pelo bit de deslocamento e escreve o resultado no registro CRH.
}
}
}
}
void sys_gpio_pin_set(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint8_t status) //(status de saída de pino único GPIO)
{
/*introdução de parâmetros
p_gpiox aponta para o endereço de registro do GPIOx (x é A, B, C, etc.), pinx representa o pino a ser controlado e status representa o status do pino (0 ou 1).
*/
if (status & 0X01) // avalia o bit mais baixo de status (ou seja, 0 ou 1) e determina a operação do pino a ser executada
{
// Neste momento, execute uma operação OR bit a bit no valor de pinx e na máscara 0xFFFF (ou seja, todos os bits são 1), defina os 16 bits inferiores do registro BSRR como 1 e defina o nível do pino correspondente para alto.
p_gpiox->BSRR |= pinx;
}
outro
{
// Neste momento, execute uma operação OR bit a bit no valor de pinx e na máscara 0xFFFF (ou seja, todos os bits são 1), defina os 16 bits inferiores do registro BSRR como 1 e defina o nível do pino correspondente para alto.
p_gpiox->BSRR |= (uint32_t)pinx << 16;
}
}
uint8_t sys_gpio_pin_get(GPIO_TypeDef *p_gpiox, uint16_t pinx) // (lê o status do pino único do GPIO)
{
/*introdução de parâmetros
p_gpiox aponta para o endereço de registro do GPIOx (x é A, B, C, etc.)
pinx representa o pin para obter status
*/
if (p_gpiox->IDR & pinx) // A instrução de julgamento if julga se um determinado bit de IDR (ou seja, os dados do nível do pino) é 1 e determina o estado real do nível do pino
{
return 1; // significa que o nível do pino está alto, return 1
}
outro
{
return 0; // Se o bit for 0, significa que o nível do pino está baixo, return 0.
}
}
void sys_wfi_set(void) // (entra no estado de baixo consumo de energia)
{
__ASM volátil("wfi"); // O código usa a instrução assembly wfi, que coloca o processador no modo WFI e espera que um sinal de interrupção seja acionado.
}
void sys_intx_disable(void) // (desliga todas as interrupções)
{
__ASM volátil("cpsid i"); // O código usa a instrução assembly cpsid i, que limpará o bit de habilitação de interrupção do processador e desabilitará o disparo de todas as interrupções.
}
void sys_intx_enable(void) // (habilita todas as interrupções)
{
__ASM volátil("cpsie i"); // O código utiliza a instrução assembly cpsie i, que definirá o bit de habilitação de interrupção do processador e permitirá o disparo de solicitações de interrupção.
}
void sys_msr_msp(uint32_t addr) // (define o endereço do topo da pilha)
{
// (Padrão de interface de software do microcontrolador Cortex)
__set_MSP(addr); // O código usa a função da biblioteca CMSIS __set_MSP(), que é usada para definir o valor do registro MSP para o parâmetro addr de entrada.
}
void sys_standby(void) // (entra no modo de espera)
{
RCC->APB1ENR |= 1 << 28; // Habilita o clock do módulo PWR (Power Control) no barramento APB1 no módulo RCC, (desloca 1 << 28 para a esquerda em 28 bits, e obtém 0x10000000 para indicar PWR )
PWR->CSR |= 1 << 8; // Define o 8º bit (PDDS) do registro CSR do módulo PWR para 1, o que significa entrar no modo standby.
PWR->CR |= 1 << 2; // Define o bit 2 (ULP) do registro CR do módulo PWR para 1, indicando que o modo ULP (Ultra Low Power) está selecionado, e a operação de entrar no standby modo está ativado.
PWR->CR |= 1 << 1; // Define o bit 1 (PDDS) do registro CR do módulo PWR para 1, modo PDDS (Power Down Deep Sleep) e inicia a operação de entrada no modo de espera.
SCB->SCR |= 1 << 2; // Define o bit 2 (SLEEPDEEP) do registro SCR do módulo SCB (System Control Block) como 1, o que significa entrar no estado de hibernação profunda.
sys_wfi_set(); // (entra no estado de baixo consumo de energia)
}
void sys_soft_reset(void) // (redefinição do software do sistema)
{
// A operação "ou" de 0X05FA0000 e (uint32_t)0x04 pode obter um valor específico 0x05FA0004, indicando que a reinicialização do software do sistema foi executada.
// Atribuir este valor ao registro AIRCR do SCB fará com que o processador execute uma reinicialização de software.
SCB->AIRCR = 0X05FA0000 | (uint32_t)0x04;
}
uint8_t sys_clock_set(uint32_t plln) // (função de configuração do relógio)
{
uint32_t tentar novamente = 0;
uint8_t retval = 0;
// O parâmetro plln indica o coeficiente de multiplicação do clock que precisa ser definido. retry e retval são usados para registrar o número de novas tentativas e o valor de retorno, respectivamente.
// RCC é um módulo controlador de registro
RCC->CR |= 0x00010000; // Use a operação "|=" para definir o bit 16 (HSEON) deste registro para 1, indicando que o relógio externo de alta velocidade (HSE) deve ser ligado.
while (tentar novamente <0XFFF0)
{
__nop(); // Instrução de instrução vazia __nop(), para esperar que o relógio externo de alta velocidade esteja pronto.
if (RCC->CR & (1 << 17) && tentar novamente > 0X8000)
{
break; // Se o clock externo de alta velocidade estiver estável e pronto, o bit 17 (HSERDY) do registro de controle do clock será definido como 1 e o loop poderá ser encerrado neste momento.
}
tente novamente++;
}
if (retry >= 0XFFF0) // Leva um certo tempo para o relógio externo de alta velocidade se estabilizar, então um número máximo de tentativas 0XFFF0 é definido.
{
retval = 1; // retval é 1 significa que a configuração do relógio falhou.
}
else // Se o relógio externo de alta velocidade estiver estável e pronto, insira a parte principal da configuração do relógio.
{
RCC->CFGR = 0X00000400;
// Define o valor do registrador divisor de clock CFGR para 0X00000400, indicando AHB (Advanced High-Performance Bus) e APB
// (Advanced Peripheral Bus) não divide a frequência respectivamente, ou seja, as frequências de clock AHB e APB são iguais.
plln -= 2; // Subtrai 2 do fator de multiplicação do clock de entrada plln,
RCC->CFGR |= plln << 18; // e desloque-o para a esquerda em 18 bits, e então use a operação "|=" para defini-lo como o bit correspondente [29:24] do registrador CFGR, que significa definir o fator de frequência dos tempos do relógio PLL.
RCC->CFGR |= 1 << 16; // Define-o para os bits correspondentes [29:24] do registrador CFGR, o que significa definir o fator de multiplicação do clock PLL.
/* Configura o período de atraso da memória Flash */
FLASH->ACR = 1 << 4; // Use a operação "ou" para definir o bit 4 (LATENCY) do registro ACR para 1, indicando que o período de atraso da memória Flash está definido para 1 ciclo de clock.
FLASH->ACR |= 2 << 0; // Use a operação "ou" para definir os bits [2:0] do registro ACR para 2, o que significa usar o atraso HCLK (relógio AHB).
/*O objetivo da configuração acima é permitir que a memória Flash suporte o clock do sistema a 48MHz. */
RCC->CR |= 1 << 24; // Use a operação "ou" para definir o bit 24 (PLLON) do registro CR como 1 para habilitar o relógio PLL.
while (!(RCC->CR >> 25)) // Utilize o loop while para aguardar a estabilização do clock, ou seja, aguardar que o bit de bloqueio do PLL (PLLRDY) do registrador CR se torne 1, indicando que o O relógio PLL está estável.
;
RCC->CFGR |= 2 << 0; // Utilize o loop while para aguardar a estabilização do clock, ou seja, aguardar que o bit de bloqueio do PLL (PLLRDY) do registrador CR se torne 1, indicando que o clock do PLL é estável.
while (((RCC->CFGR >> 2) & 0X03) != 2) // Use o loop while para esperar que os bits [3:2] do registrador CFGR se tornem 2, indicando que o clock do PLL se estabilizou como o relógio do sistema.
;
}
return retval; // Finalmente, retorna o status da configuração do relógio, se o valor de retorno for 0, a configuração do relógio foi bem-sucedida, se o valor de retorno for 1, a configuração falhou.
}
void sys_stm32_clock_init(uint32_t plln) // (função de inicialização do relógio)
{
RCC->APB1RSTR = 0x00000000; // Limpa o registrador de reset do APB1, ou seja, coloca todos os seus bits em 0, de forma a garantir que todos os dispositivos deste barramento estejam em estado de reset quando o clock for inicializado.
RCC->APB2RSTR = 0x00000000; // Limpa o registrador de reset do APB2, ou seja, coloca todos os seus bits em 0, de forma a garantir que todos os dispositivos deste barramento estejam em estado de reset quando o clock for inicializado.
RCC->AHBENR = 0x00000014; // Define o registro de habilitação do clock no barramento AHB para habilitar periféricos DMA, GPIOA e GPIOB.
RCC->APB2ENR = 0x00000000; // Limpa o registro de habilitação do clock do APB2, ou seja, coloca todos os seus bits em 0, o que pode garantir que todos os dispositivos deste barramento não sejam habilitados quando o clock for inicializado.
RCC->APB1ENR = 0x00000000; // Limpa o registro de habilitação do clock do APB1, ou seja, coloca todos os seus bits em 0, o que pode garantir que todos os dispositivos deste barramento não sejam habilitados quando o clock for inicializado.
RCC->CR |= 0x00000001; // Define o bit de habilitação do registro de controle de clock RCC para habilitar o clock externo de alta velocidade (HSE).
RCC->CFGR &= 0xF8FF0000; // Limpa o PLL, o divisor de frequência e os bits de opção de relógio do registro de configuração de relógio RCC.
RCC->CR &= 0xFEF6FFFF; // Limpa o PLL, o divisor de frequência e os bits de opção de relógio do registro de configuração de relógio RCC.
RCC->CR &= 0xFFFBFFFF; // Limpa o bit de desabilitação HSE do registrador de controle de clock RCC
RCC->CFGR &= 0xFF80FFFF; // Limpa o bit multiplicador PLL do registro de configuração do relógio RCC
RCC->CIR = 0x009F0000; // Limpa o registro de interrupção do relógio RCC.
sys_clock_set(plln); // Use a função sys_clock_set para definir o fator de multiplicação do PLL e escolha se deseja usar o PLL como relógio do sistema.
#ifdef VECT_TAB_RAM
sys_nvic_set_vector_table(SRAM_BASE, 0x0); // Seleciona o endereço da tabela de vetores na RAM para salto do programa do usuário e processamento de interrupção.
#outro
sys_nvic_set_vector_table(FLASH_BASE, 0x0); // Selecione o endereço da tabela de vetores em FLASH para salto do programa do usuário e processamento de interrupção.
#fim se
}