Open source interpretation register sys.c internal source code

#include "./SYSTEM/sys/sys.h"

void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset) // (set interrupt vector table)

{

    // The VTOR register of the Cortex-M processor only cares about the 0~8 bits of the address, and the values ​​of the 9~31 bits (higher 23 bits) are ignored

    // Bitwise AND operation reserves the 0~8 binary bits of the offset address,

    // In this way, the address offset of the start position of the interrupt vector table can be set to the VTOR register, thus completing the offset operation of the interrupt vector table.

    SCB->VTOR = baseaddr | (offset & (uint32_t)0xFFFFFE00);

    // Implementation method: Bitwise AND operation between the offset address offset of the specified interrupt vector table and the lowest 9 bits (that is, the 0~8 bits of the offset address),

    // Then perform a bitwise OR operation with the variable baseaddr, and then assign the result to the VTOR register.

}

static void sys_nvic_priority_group_config(uint8_t group) // (--local function-- configure NVIC interrupt priority grouping,)

{

    /*--Supplementary knowledge--

    NVIC_PRIORITYGROUP_4 is a scheme based on interrupt controller (NVIC) to implement priority grouping.

    It is a grouping method based on hardware priority in the Cortex-M processor.

    In NVIC_PRIORITYGROUP_4, there are 4 priority groups, from 0 to 3 (priority from high to low).

    Within each priority group, there are 16 priority levels. Among them, priority 0 is the highest level, and priority 15 is the lowest level.

    Within the same priority group, different sub-priorities can be set for each interrupt

    Both preemption priority and response priority are given 16 levels. The preemption priority is used for preemption between interrupts, and the response priority is used for priority control inside the interrupt service function.

    Interrupt vector table ----------> reference manual p130 page table 54

    */

    uint32_t temp, temp1;

    temp1 = (~group) & 0x07; // perform bitwise inversion (~) on variable group and bitwise AND operation with binary number 0x07

    temp1 <<= 8; // shift temp1 left by 8 bits. The purpose is to put the value of the priority group into the 15th~13th bits of AIRCR

    temp = SCB->AIRCR; // Read the value of AIRCR (application interrupt and reset control register) in the System Control Block (SCB) register to temp

    temp &= 0X0000F8FF; // bitwise AND operation between temp and 0x0000F8FF, the purpose is to set the (interrupt priority group) and (sub-priority) of AIRCR to 0 for reconfiguration

    temp |= 0X05FA0000; // Operate the value of temp with 0x05FA0000 bit by bit, the number means to use the NVIC_PRIORITYGROUP_4 scheme for priority encoding. In this scenario, the priority grouping is 0b10

    temp |= temp1; // Perform a bitwise OR operation on temp1 and temp, and place the grouped value into the corresponding bit. Finally, the first three bits are set as the priority grouping value.

    SCB->AIRCR = temp; // Write temp to (AIRCR) in the (SCB) register, thus completing the whole process of configuring NVCI interrupt priority grouping.

}

void sys_nvic_init(uint8_t pprio, uint8_t sprio, uint8_t ch, uint8_t group) // (NVIC interrupt controller initialization)

{

    sys_nvic_priority_group_config(group); // Call the sys_nvic_priority_group_config function, the parameter is group, so as to complete the configuration of NVCI interrupt priority grouping

    uint32_t temp;

    temp = pprio << (4 - group); // Shift the value of pprio to the left (4-group) and store the result in temp. The purpose is to calculate the master priority

    temp |= sprio & (0x0f >> group); // Store the lower 4 bits of sprio in temp, use 0x0f to shift right (group) bits and perform bitwise AND operation, and keep the lower 4 bits in temp. The purpose is to calculate from the priority

    temp &= 0xf; // Operate temp and 0xf bitwise and (&) to ensure that the value in temp is not greater than 15.

    NVIC->ISER[ch / 32] |= 1 << (ch % 32); // Set the position of the corresponding interrupt line in the NVIC->ISER[ch/32] register to 1, and enable the corresponding interrupt Wire;

    NVIC->IP[ch] |= temp << 4; // Shift the calculated priority temp by 4 bits to the left and write it into the NVIC->IP[ch] register to specify the priority of the corresponding interrupt.

}

void sys_nvic_ex_config(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint8_t tmode) //(NVIC external interrupt configuration)

{

    uint8_t offset;

    uint32_t gpio_num = 0;

    uint32_t pinpos = 0, curpin = 0, pos = 0;

    /*Parameter Description

    offset is used to store the bit offset of the pin number in the EXTICR register.

    gpio_num stores the corresponding GPIO number,

    pinpos and curpin are used to traverse and check each bit in the pin number respectively,

    pos is used to generate the bitmask,

    */

    gpio_num = ((uint32_t)p_gpiox - (uint32_t)GPIOA) / 0X400; // Calculate the difference between the address of the GPIO port GPIOA in the AHB bus and the address of the input parameter p_gpiox, and then divide it by 0x400 to get the corresponding GPIO number .

    RCC->APB2ENR |= 1 << 0; // Enable the AFIO clock, set the AFIO clock bit in the RCC->APB2ENR register to access the AFIO register

    for (pinpos = 0; pinpos < 16; pinpos++) // traverse each bit in the pin number

    {

        pos = 1 << pinpos; // use pos to generate a bitmask,

        curpin = pinx & pos; // Use the bitwise AND (&) operation to get the current bit curpin of the pin number

        if (curpin == pos) // If the current bit is 1, it means that the pin needs to be configured with the EXTI function.

        {

            offset = (pinpos % 4) * 4; // Calculate its corresponding bit offset offset in the EXTICR register,

            AFIO->EXTICR[pinpos / 4] &= ~(0x000F << offset); // Then clear the value at the specified position in the AFIO->EXTICR[pinpos / 4] register

            AFIO->EXTICR[pinpos / 4] |= gpio_num << offset; // Then store gpio_num in this position according to the bit offset value of offset offset

            EXTI->IMR |= 1 << pinpos; // Set the position of the corresponding pin in the EXTI->IMR register to 1, and enable the EXTI interrupt corresponding to the pin (that is, the line BITx interrupt can be enabled).

            /*In addition, configure the trigger mode of the EXTI corresponding line according to the incoming trigger mode tmode*/

            if (tmode & 0x01)

                EXTI->FTSR |= 1 << pinpos; // If the lowest bit of tmode is 1, enable falling edge trigger;

            if (tmode & 0x02)

                EXTI->RTSR |= 1 << pinpos; // If the second low bit of tmode is 1, enable the rising edge trigger.

        }

    }

}

void sys_gpio_remap_set(uint8_t pos, uint8_t bit, uint8_t val) //(GPIO pin remapping)

{

    uint32_t temp = 0; // temp is used to save the generated bitmask, and i is used to traverse the bits that need to be configured.

    uint8_t i = 0;

    RCC->APB2ENR |= 1 << 0; // Enable the AFIO clock, temp is used to save the generated bit mask, and i is used to traverse the bits that need to be configured.

    for (i = 0; i < bit; i++) // Calculate the bit mask temp that needs to be configured according to the bit parameter passed in

    {

        temp <<= 1; // The parameter bit indicates how many bits need to be configured, so it is necessary to generate a mask whose lower bits are all 1,

        temp += 1; // Generated by using temp bitwise left shift and bitwise OR operation.

    }

    AFIO->MAPR &= ~(temp << pos); // Clear the position to be configured in the AFIO->MAPR register. This can be done by shifting the pos bit to the left of temp and then inverting (~) and then taking the original value Bitwise AND (&) operation implementation

    AFIO->MAPR |= (uint32_t)val << pos; // Finally, set the bits to be configured to new values ​​in the AFIO->MAPR register. You can move the new value to the left by pos bits and then press or (|) to the original value to complete the setting.

}

//(GPIO pin setting)

// Structure definition:

// typedef struct

// {

// volatile uint32_t MODER; // GPIO port mode register

// volatile uint16_t OTYPER; // GPIO port output type register

// uint16_t RESERVED0; // Reserved, the value is 0x02

// volatile uint32_t OSPEEDR; // GPIO port output speed register

// volatile uint32_t PUPDR; // GPIO port pull-up/pull-down register

// volatile uint16_t IDR; // GPIO port input data register

// uint16_t RESERVED1; // Reserved, the value is 0x0A

// volatile uint16_t ODR; // GPIO port output data register

// uint16_t RESERVED2; // Reserved, the value is 0x0E

// volatile uint16_t BSRR; // GPIO port bit set/reset register

// uint16_t RESERVED3; // Reserved, the value is 0x12

// volatile uint16_t LCKR; // GPIO port configuration lock register

// uint16_t RESERVED4; // Reserved, the value is 0x16

// volatile uint16_t AFR[2]; // GPIO port alternative function register

// uint16_t RESERVED5[10]; // Reserved, the value is 0x1C-0x28

// volatile uint32_t BRR; // GPIO bit reset register

// } GPIO_TypeDef;

void sys_gpio_set(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint32_t mode, uint32_t otype, uint32_t ospeed, uint32_t pupd)

{

    /*parameter introduction

    p_gpiox points to the register address of GPIOx (x is A, B, C, etc.),

    pinx represents the pin to be configured (may be multiple, represented by binary bits)

    , mode indicates the pin working mode (input, output, multiplexing, etc.),

    otype indicates the output type (open drain or push-pull),

    ospeed indicates the output rate,

    pupd means pull-up and pull-down mode. */

    uint32_t pinpos = 0, pos = 0, curpin = 0;

    // pinpos is used to cyclically check each pin of the GPIO port, pos is used to check whether the pin needs to be configured, and curpin is the result of the check.

    uint32_t config = 0;

    // config variable, the initial value is 0, used to save a certain IO setting

    for (pinpos = 0; pinpos < 16; pinpos++) // Use the for loop to process the pins with pinpos 0~15 in turn.

    {

        pos = 1 << pinpos; // Shift 1 to the left of pinpos and assign it to pos, that is, pos is used to check whether the corresponding pin needs to be configured

        curpin = pinx & pos; // Pin configuration information is stored in pinx, use bit operation & to get curpin, that is, whether the pin needs to be configured.

        if (curpin == pos) // If curpin is equal to pos, it means the pin needs to be configured.

        {

            config = 0; // If configuration is required, first clear config to indicate that the pin is configured as the default analog input mode.

            if ((mode == 0X01) || (mode == 0X02)) // if the mode is normal output mode or alternate function mode

            {

                config = ospeed & 0X03; // OR the lower 2 bits of the speed parameter ospeed with the lower 2 bits of config to get bit0/1, indicating the setting of MODE[1:0].

                config |= (otype & 0X01) << 2; // OR the low bit of otype with the second bit of config to get the setting of CNF[0].

                config |= (mode - 1) << 3; // Shift the value of mode minus 1 to the left by 3 bits, and then OR with the third bit of config to get the setting of CNF[1].

            }

            else if (mode == 0) // If mode is equal to 0, it is normal input mode.

            {

                if (pupd == 0) // If there is no pull-up and pull-down, it means floating input mode.

                {

                    config = 1 << 2; // Set the 2nd and 3rd bits of config to 0 1, which means floating input mode.

                }

                else // Otherwise (that is, with pull-up and pull-down input modes).

                {

                    config = 1 << 3; // Set the 2nd 3rd bits of config to 1 0, indicating the input mode of up and down

                    /*The following is to pull up or pull down the pin of the GPIO port according to the low bit of the pupd parameter. */

                    p_gpiox->ODR &= ~(1 << pinpos); // Clear the corresponding pin bit of ODR,

                    p_gpiox->ODR |= (pupd & 0X01) << pinpos; // Then according to the low bit of pupd, move the corresponding pin bit to the left, perform OR operation, and set it as pull-up or pull-down.

                }

            }

            /*The following judges the CRH or CRL register where the pin is located according to pinpos. */

            if (pinpos <= 7) // If the currently processed pin pinpos is in the lower 8 bits, the corresponding register is the CRL register.

            {

                p_gpiox->CRL &= ~(0X0F << (pinpos * 4)); // First shift pinpos left by 2 bits (equivalent to multiplying by 4) to get the (lower 8 bits) offset, then shift 0X0F to the left shift bit,

                                                         // Get the mask to be written to the register, and then use the inverse code to clear the bit specified by the mask

                p_gpiox->CRL |= config << (pinpos * 4); // Shift config to the left by the offset bit, and write the result to the CRL register.

            }

            else // If the position of the currently processed pin pinpos is in the upper 8 bits, the corresponding register is the CRH register.

            {

                p_gpiox->CRH &= ~(0X0F << ((pinpos - 8) * 4)); // Same as above, except that the offset should be subtracted by 8 (high 8 bits) before calculation

                p_gpiox->CRH |= config << ((pinpos - 8) * 4); // Shift the config to the left by the offset bit, and write the result to the CRH register.

            }

        }

    }

}

void sys_gpio_pin_set(GPIO_TypeDef *p_gpiox, uint16_t pinx, uint8_t status) //(GPIO single pin output status)

{

    /*parameter introduction

    p_gpiox points to the register address of GPIOx (x is A, B, C, etc.), pinx represents the pin to be controlled, and status represents the pin status (0 or 1).

    */

    if (status & 0X01) // judge the lowest bit of status (that is, 0 or 1), and determine the pin operation to be performed

    {

        // At this time, perform a bitwise OR operation on the value of pinx and the mask 0xFFFF (that is, all bits are 1), set the lower 16 bits of the BSRR register to 1, and set the level of the corresponding pin to high level .

        p_gpiox->BSRR |= pinx;

    }

    else

    {

        // At this time, perform a bitwise OR operation on the value of pinx and the mask 0xFFFF (that is, all bits are 1), set the lower 16 bits of the BSRR register to 1, and set the level of the corresponding pin to high level .

        p_gpiox->BSRR |= (uint32_t)pinx << 16;

    }

}

uint8_t sys_gpio_pin_get(GPIO_TypeDef *p_gpiox, uint16_t pinx) // (read GPIO single pin status)

{

    /*parameter introduction

    p_gpiox points to the register address of GPIOx (x is A, B, C, etc.)

    pinx represents the pin to get status

    */

    if (p_gpiox->IDR & pinx) // The if judgment statement judges whether a certain bit of IDR (that is, the pin level data) is 1, and determines the actual level state of the pin

    {

        return 1; // means the pin level is high, return 1

    }

    else

    {

        return 0; // If the bit is 0, it means the pin level is low, return 0.

    }

}

void sys_wfi_set(void) // (enter low power consumption state)

{

    __ASM volatile("wfi"); // The code uses the assembly instruction wfi, which puts the processor in WFI mode and waits for an interrupt signal to be triggered.

}

void sys_intx_disable(void) // (turn off all interrupts)

{

    __ASM volatile("cpsid i"); // The code uses the assembly instruction cpsid i, which will clear the interrupt enable bit of the processor and disable all interrupt triggers.

}

void sys_intx_enable(void) // (enable all interrupts)

{

    __ASM volatile("cpsie i"); // The code uses the assembly instruction cpsie i, which will set the interrupt enable bit of the processor to allow the triggering of interrupt requests.

}

void sys_msr_msp(uint32_t addr) // (set the stack top address)

{

    // (Cortex Microcontroller Software Interface Standard)

    __set_MSP(addr); // The code uses the CMSIS library function __set_MSP(), which is used to set the value of the MSP register to the incoming addr parameter.

}

void sys_standby(void) // (enter standby mode)

{

    RCC->APB1ENR |= 1 << 28; // Enable the PWR (Power Control) module clock on the APB1 bus in the RCC module, (shift 1 << 28 to the left by 28 bits, and get 0x10000000 to indicate PWR)

    PWR->CSR |= 1 << 8; // Set the 8th bit (PDDS) of the CSR register of the PWR module to 1, which means entering the standby mode.

    PWR->CR |= 1 << 2; // Set bit 2 (ULP) of the CR register of the PWR module to 1, indicating that the ULP (Ultra Low Power) mode is selected, and the operation of entering the standby mode is enabled.

    PWR->CR |= 1 << 1; // Set bit 1 (PDDS) of the CR register of the PWR module to 1, PDDS (Power Down Deep Sleep) mode, and start the operation of entering standby mode.

    SCB->SCR |= 1 << 2; // Set bit 2 (SLEEPDEEP) of the SCR register of the SCB (System Control Block) module to 1, which means entering the deep sleep state.

    sys_wfi_set(); // (enter low power state)

}

void sys_soft_reset(void) // (system software reset)

{

    // The "or" operation of 0X05FA0000 and (uint32_t)0x04 can get a specific value 0x05FA0004, indicating that the system software reset is executed.

    // Assigning this value to the SCB's AIRCR register will cause the processor to perform a software reset.

    SCB->AIRCR = 0X05FA0000 | (uint32_t)0x04;

}

uint8_t sys_clock_set(uint32_t plln) // (clock setting function)

{

    uint32_t retry = 0;

    uint8_t retval = 0;

    // The plln parameter indicates the clock multiplication coefficient that needs to be set. retry and retval are used to record the number of retries and the return value respectively.

    // RCC is a register controller module

    RCC->CR |= 0x00010000; // Use the "|=" operation to set bit 16 (HSEON) of this register to 1, indicating that the external high-speed clock (HSE) is to be turned on.

    while (retry < 0XFFF0)

    {

        __nop(); // Empty instruction statement __nop(), to wait for the external high-speed clock to be ready.

        if (RCC->CR & (1 << 17) && retry > 0X8000)

        {

            break; // If the external high-speed clock is stable and ready, bit 17 (HSERDY) of the clock control register will be set to 1, and the loop can be exited at this time.

        }

        retry++;

    }

    if (retry >= 0XFFF0) // It takes a certain amount of time for the external high-speed clock to stabilize, so a maximum number of retries 0XFFF0 is set.

    {

        retval = 1; // retval is 1 means clock setting failed.

    }

    else // If the external high-speed clock is stable and ready, enter the core part of the clock configuration.

    {

        RCC->CFGR = 0X00000400;

        // Set the value of the clock divider register CFGR to 0X00000400, indicating AHB (Advanced High-performance Bus) and APB

        // (Advanced Peripheral Bus) does not divide the frequency respectively, that is, the AHB and APB clock frequencies are the same.

        plln -= 2; // Subtract 2 from the incoming clock multiplication factor plln,

        RCC->CFGR |= plln << 18; // and shift it to the left by 18 bits, and then use the "|=" operation to set it as the corresponding bit [29:24] of the CFGR register, which means setting the PLL clock times frequency factor.

        RCC->CFGR |= 1 << 16; // Set it to the corresponding bits [29:24] of the CFGR register, which means setting the multiplication factor of the PLL clock.

        /* Configure the delay period of the Flash memory */

        FLASH->ACR = 1 << 4; // Use "or" operation to set bit 4 (LATENCY) of the ACR register to 1, indicating that the delay period of the Flash memory is set to 1 clock cycle.

        FLASH->ACR |= 2 << 0; // Use "or" operation to set bits [2:0] of ACR register to 2, which means using HCLK (AHB clock) delay.

        /*The purpose of the above configuration is to enable the Flash memory to support the system clock at 48MHz. */

        RCC->CR |= 1 << 24; // Use "or" operation to set bit 24 (PLLON) of CR register to 1 to enable PLL clock.

        while (!(RCC->CR >> 25)) // Use the while loop to wait for the clock to stabilize, that is, wait for the PLL lock bit (PLLRDY) of the CR register to become 1, indicating that the PLL clock is stable.

            ;

        RCC->CFGR |= 2 << 0; // Use the while loop to wait for the clock to stabilize, that is, wait for the PLL lock bit (PLLRDY) of the CR register to become 1, indicating that the PLL clock is stable.

        while (((RCC->CFGR >> 2) & 0X03) != 2) // Use the while loop to wait for bits [3:2] of the CFGR register to become 2, indicating that the PLL clock has stabilized as the system clock.

            ;

    }

    return retval; // Finally, return the status of the clock setting, if the return value is 0, the clock setting is successful, if the return value is 1, the setting is failed.

}

void sys_stm32_clock_init(uint32_t plln) // (clock initialization function)

{

    RCC->APB1RSTR = 0x00000000; // Clear the reset register of APB1, that is, set all its bits to 0, so as to ensure that all devices on this bus are in reset state when the clock is initialized.

    RCC->APB2RSTR = 0x00000000; // Clear the reset register of APB2, that is, set all its bits to 0, so as to ensure that all devices on this bus are in reset state when the clock is initialized.

    RCC->AHBENR = 0x00000014; // Set the clock enable register on the AHB bus to enable DMA, GPIOA and GPIOB peripherals.

    RCC->APB2ENR = 0x00000000; // Clear the clock enable register of APB2, that is, set all its bits to 0, which can ensure that all devices on this bus are not enabled when the clock is initialized.

    RCC->APB1ENR = 0x00000000; // Clear the clock enable register of APB1, that is, set all its bits to 0, which can ensure that all devices on this bus are not enabled when the clock is initialized.

    RCC->CR |= 0x00000001; // Set the enable bit of the RCC clock control register to enable the external high-speed clock (HSE).

    RCC->CFGR &= 0xF8FF0000; // Clear the PLL, frequency divider and clock option bits of the RCC clock configuration register.

    RCC->CR &= 0xFEF6FFFF; // Clear the PLL, frequency divider and clock option bits of the RCC clock configuration register.

    RCC->CR &= 0xFFFBFFFF; // Clear the HSE disable bit of the RCC clock control register

    RCC->CFGR &= 0xFF80FFFF; // Clear the PLL multiplier bit of the RCC clock configuration register

    RCC->CIR = 0x009F0000; // Clear the RCC clock interrupt register.

    sys_clock_set(plln); // Use the sys_clock_set function to set the PLL multiplication factor and choose whether to use the PLL as the system clock.

#ifdef VECT_TAB_RAM

    sys_nvic_set_vector_table(SRAM_BASE, 0x0); // Select the address of the vector table in RAM for user program jump and interrupt processing.

#else

    sys_nvic_set_vector_table(FLASH_BASE, 0x0); // Select the address of the vector table in FLASH for user program jump and interrupt processing.

#endif

}

Guess you like

Origin blog.csdn.net/JohnJill/article/details/130546178