N32G401单片机驱动配置详解(适用于STM32系列)

N32G401总体上和STM32F4系列差不多,无论是从芯片资源,还是各种寄存器,都有相通之处,所以N32G401的所有驱动,如果使用smt32的话也可以借鉴使用(修改函数名)

文章代码仅限于参考,如果直接CV是肯定用不了的,源代码链接在最后

PS:所有驱动基于N32G401F7S8-1,一共20个引脚,并且没有外接晶振,用的是内部的8MHZ的HSI,倍频到64MHZ,所以接下来的配置都以64MHZ为基准。

在编写驱动的过程中发现STM32单片机有直接写bitband操作,比如:

IO操作函数											   
#define	DS18B20_DQ_OUT PAout(0) //数据端口	PA0
#define	DS18B20_DQ_IN  PAin(0)  //数据端口	PA0 

在N32G401也能进行类似的操作,不过比较复杂,所以不建议使用

本质上这个就是对寄存器进行操作,所以我们可以直接这样来写,效果也是一样的:


// IO操作函数
#define DS18B20_DQ_IN GPIO_Input_Pin_Data_Get(DS18B20_PORT, DS18B20_PIN)

#define DS18B20_DQ_OUT_1 GPIO_Pins_Set(DS18B20_PORT, DS18B20_PIN)
#define DS18B20_DQ_OUT_0 GPIO_Pins_Reset(DS18B20_PORT, DS18B20_PIN)

以上三个是基于ds18b20温度传感器的单总线传输配置的宏定义(下面有详细介绍)

驱动分类

GPIO

N32G401的GPIO挂载在APH总线,所以在移植代码的时候务必看清时钟开启函数

以配置KEY为例:KEY.c

	RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOA); // 按键全部使用GPIOA
	RCC_APB2_Peripheral_Clock_Enable(RCC_APB2_PERIPH_AFIO);
	RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_TIM6);

	GPIO_InitType GPIO_InitStructure;
	GPIO_Structure_Initialize(&GPIO_InitStructure);
	GPIO_InitStructure.Pin = KEY6_GPIO_PIN | KEY7_GPIO_PIN | KEY8_GPIO_PIN | KEY9_GPIO_PIN 
    | KEY10_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT; // 输入
	GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
	GPIO_Peripheral_Initialize(GPIOA, &GPIO_InitStructure);

	GPIO_EXTI_Line_Set(EXTI_LINE_SOURCE5, AFIO_EXTI_PA0);
	GPIO_EXTI_Line_Set(EXTI_LINE_SOURCE6, AFIO_EXTI_PA1);
	GPIO_EXTI_Line_Set(EXTI_LINE_SOURCE7, AFIO_EXTI_PA2);
	GPIO_EXTI_Line_Set(EXTI_LINE_SOURCE8, AFIO_EXTI_PA3);
	GPIO_EXTI_Line_Set(EXTI_LINE_SOURCE9, AFIO_EXTI_PA4);

KEY(按键中断,短按长按)

中断配置:KEY.c

/* Configure key EXTI line */
	EXTI_InitType EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line = EXTI_LINE5;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Peripheral_Initializes(&EXTI_InitStructure);

	EXTI_InitStructure.EXTI_Line = EXTI_LINE6;
	EXTI_Peripheral_Initializes(&EXTI_InitStructure);

	EXTI_InitStructure.EXTI_Line = EXTI_LINE7;
	EXTI_Peripheral_Initializes(&EXTI_InitStructure);

	EXTI_InitStructure.EXTI_Line = EXTI_LINE8;
	EXTI_Peripheral_Initializes(&EXTI_InitStructure);

	EXTI_InitStructure.EXTI_Line = EXTI_LINE9;
	EXTI_Peripheral_Initializes(&EXTI_InitStructure);

	/* Set key input interrupt priority */
	NVIC_InitType NVIC_InitStructure;
	NVIC_Priority_Group_Set(NVIC_PER3_SUB1_PRIORITYGROUP);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SUB_PRIORITY_3;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PER_PRIORITY_3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Initializes(&NVIC_InitStructure);

中断服务函数:N32G401_IT.c


#define it_ms 300 // 中断延迟值

volatile u8 K8_PressCount = 0;  // 设定风速
volatile u8 K9_PressCount = 0;  // 设定风速
volatile u8 K10_PressCount = 1; // 开关机
volatile u8 FLAG = 1;           // 开机标志位


/**
 * @name     EXTI9_5_IRQHandler
 * @function K6温度减少键:设定温度降低,长按快速递减
 * @function K7温度增加键:设定温度升高,长按快速递增
 * @function K8速切换键:高、中、低、自动风速切换
 * @function K9模式切换键:进行冷风、暖风模式切换风
 * @function K10开机关机键:选择开机或关机
 * @param    none
 * @return   none
 */
void EXTI9_5_IRQHandler(void)
{
    if (EXTI_Interrupt_Status_Get(EXTI_LINE5) != RESET) // K6
    {
        if (FLAG)
        {
            while (GPIO_Input_Pin_Data_Get(GPIOA, GPIO_PIN_0) == RESET)
            {
                if (set_temp > 16)
                {
                    set_temp--;
                    LCD_TEMP_SHOW(1, set_temp);
                }
                else
                {
                    set_temp = 16;
                    LCD_TEMP_SHOW(1, set_temp);
                }
                SysTick_Delay_Ms(it_ms);
            }
        }
        EXTI_Interrupt_Status_Clear(EXTI_LINE5);
    }

    else if (EXTI_Interrupt_Status_Get(EXTI_LINE6) != RESET) // K7
    {
        if (FLAG)
        {
            while (GPIO_Input_Pin_Data_Get(GPIOA, GPIO_PIN_1) == RESET)
            {
                if (set_temp < 32)
                {
                    set_temp++;
                    LCD_TEMP_SHOW(1, set_temp);
                }
                else
                {
                    set_temp = 32;
                    LCD_TEMP_SHOW(1, set_temp);
                }
                SysTick_Delay_Ms(it_ms);
            }
        }
        EXTI_Interrupt_Status_Clear(EXTI_LINE6);
    }

    else if (EXTI_Interrupt_Status_Get(EXTI_LINE7) != RESET) // K8
    {
        if (FLAG)
        {
            K8_PressCount++; // 每次按键按下,计数器加一
            // 根据计数器的值切换状态
            switch (K8_PressCount % K8_NUM_STATES)
            {
            case K8_STATE_AUTO:
                // 处理自动状态的逻辑
                HT1621_Set_LCD(5);
                break;
            case K8_STATE_LOW:
                // 处理低速状态的逻辑
                HT1621_Set_LCD(4);
                break;
            case K8_STATE_MEDIUM:
                // 处理中速状态的逻辑
                HT1621_Set_LCD(3);
                break;
            case K8_STATE_HIGH:
                // 处理高速状态的逻辑
                HT1621_Set_LCD(2);
                break;
            }
        }
        EXTI_Interrupt_Status_Clear(EXTI_LINE7);
    }

    else if (EXTI_Interrupt_Status_Get(EXTI_LINE8) != RESET) // K9
    {
        if (FLAG)
        {
            K9_PressCount++; // 每次按键按下,计数器加一
            // 根据计数器的值切换状态
            switch (K9_PressCount % K9_NUM_STATES)
            {
            case K9_STATE_COLD:
                // 处理冷风状态的逻辑
                HT1621_Set_LCD(9);
                break;
            case K9_STATE_WARM:
                // 处理暖风状态的逻辑
                HT1621_Set_LCD(10);
                break;
            case K9_STATE_SLEEP:
                // 处理睡眠状态的逻辑
                HT1621_Set_LCD(11);
                break;
            }
        }
        EXTI_Interrupt_Status_Clear(EXTI_LINE8);
    }

    else if (EXTI_Interrupt_Status_Get(EXTI_LINE9) != RESET) // K10
    {
        K10_PressCount++; // 每次按键按下,计数器加一
        switch (K10_PressCount % K10_NUM_STATES)
        {
        // 关机
        case K10_STATE_OFF:
            LCD_BLK_Value(0);
            LCD_Clear();

            FLAG = 0;
            EXTI_Interrupt_Status_Clear(EXTI_LINE9);
            break;
        // 开机
        case K10_STATE_ON:
            LCD_ON();
            LCD_BLK_Value(LCD_BLK_VALUE);
            FLAG = 1;

            EXTI_Interrupt_Status_Clear(EXTI_LINE9);
            break;
        }
        EXTI_Interrupt_Status_Clear(EXTI_LINE9);
    }
}

这里的中断服务函数,按键使用了枚举,以k8为例:IT_.h

// 定义K8状态
typedef enum
{
    K8_STATE_AUTO,
    K8_STATE_LOW,
    K8_STATE_MEDIUM,
    K8_STATE_HIGH,
    K8_NUM_STATES // 状态的数量
} K8_State;

一共有四个状态,枚举了五个元素,最后一个为状态的数量,至于为什么要多枚举一个状态的数量,从中断服务函数中可以看到

K8_PressCount++; // 每次按键按下,计数器加一
            // 根据计数器的值切换状态
            switch (K8_PressCount % K8_NUM_STATES)
            {
            case K8_STATE_AUTO:
                // 处理自动状态的逻辑
                HT1621_Set_LCD(5);
                break;
            case K8_STATE_LOW:
                // 处理低速状态的逻辑
                HT1621_Set_LCD(4);
                break;
            case K8_STATE_MEDIUM:
                // 处理中速状态的逻辑
                HT1621_Set_LCD(3);
                break;
            case K8_STATE_HIGH:
                // 处理高速状态的逻辑
                HT1621_Set_LCD(2);
                break;
            }
K8_PressCount++; // 每次按键按下,计数器加一
            // 根据计数器的值切换状态
switch (K8_PressCount % K8_NUM_STATES)

这里按键每按下一次,进入中断,在中断中将按键对应的计数值加一,将得到的计数值对按键状态取余,这样就能保证按键能在设定的几种状态中不断循环切换。

如果需要长按,就用while代替if,然后加上一个延迟,建议延迟不易太久,不然一直卡在中断里面,芯片无法做别的事情,最后清除标志位。

 while (GPIO_Input_Pin_Data_Get(GPIOA, GPIO_PIN_0) == RESET)
            {

                SysTick_Delay_Ms(it_ms);
            }
EXTI_Interrupt_Status_Clear(EXTI_LINE5);

LCD(HT1621驱动,用PWM控制背光)

HT1621驱动在网上已经有很多详细的教程,这里只简单的介绍

初始化就按上面GPIO的格式进行配置

首先是背光PWM配置如下:LCD.c

    TIM_TimeBaseInitType TIM_TimeBaseInitStructure;
    TIM_Base_Struct_Initialize(&TIM_TimeBaseInitStructure);
    TIM_TimeBaseInitStructure.Period = 100 - 1;
    TIM_TimeBaseInitStructure.Prescaler = 640 - 1;
    TIM_TimeBaseInitStructure.RepetCnt = 0;
    TIM_TimeBaseInitStructure.CntMode = TIM_CNT_MODE_UP;
    TIM_TimeBaseInitStructure.ClkDiv = TIM_CLK_DIV1;
    TIM_Base_Initialize(TIM1, &TIM_TimeBaseInitStructure);
    TIM_On(TIM1);

    OCInitType TIM_OCInitStructure;
    TIM_Output_Channel_Struct_Initialize(&TIM_OCInitStructure);
    TIM_OCInitStructure.OcMode = TIM_OCMODE_PWM1;
    TIM_OCInitStructure.OcNPolarity = TIM_OCN_POLARITY_LOW;
    TIM_OCInitStructure.OutputState = TIM_OUTPUT_STATE_ENABLE;
    TIM_OCInitStructure.Pulse = 0;
    TIM_OCInitStructure.OcNIdleState = TIM_OCN_IDLE_STATE_SET;
    TIM_Output_Channel2_Initialize(TIM1, &TIM_OCInitStructure);

    TIM_PWM_Output_Enable(TIM1);

除了函数名称以及结构体变量名称不一样之外,名称对应的功能基本相符,所以N32G401和STM32都可以参考这个进行PWM配置。(PULSE是用来设置占空比的,有专门的函数更改)

HT1621驱动函数配置:


/**
 * @name     HT1621_transmit
 * @function 传输数据
 * @param    u8 Data
 * @param    u8 num
 * @return   none
 */
void HT1621_transmit(u8 Data, u8 num)
{
    u8 i;

    for (i = 0; i < num; i++)
    {

        LCD_WR_0();      // 拉低WR脚  Dat a线上的数据在WR脚位上升沿写入
        if (Data & 0x80) // 判断高位是否为1,如果是1就输出高电平,否则低电平
        {
            LCD_DATA_1(); // 拉高DATA(开始写入数据)
        }
        else
        {
            LCD_DATA_0(); // 拉低DATA
        }
        LCD_WR_1(); // 当前这一bit写完,拉高WR脚,为下次WR为上升沿做准备
        Data <<= 1; // 当前位用完了,移到下一位继续上述动作
    }
}

/**
 * @description: HT1621_WriteCommand
 * @param u8 Cmd:命令
 * @return none
 */
void HT1621_WriteCommand(u8 Cmd)
{
    LCD_CS_0();                       // 拉低CS脚  CS拉低时WR有效
    HT1621_transmit(COMMAND_CODE, 4); // 写入命令标志100    0x80 3
    HT1621_transmit(Cmd, 8);          // 写入命令数据
    LCD_CS_1();                       // 拉高CS 结束
}

/**
 * @description: HT1621_WriteData
 * @param u8 Addr:写入初始地址
 * @param u8 Data:写入数据
 * @return none
 */
void HT1621_WriteData(u8 Addr, u8 Data)
{

    LCD_CS_0(); // 拉低CS 开始写数据

    HT1621_transmit(WRITE_DATA_CODE, 3); // 写入0x80(写命令) /0xa0(写数据)
    HT1621_transmit(Addr <<= 2, 6);      // 写入地址
    HT1621_transmit(Data <<= 4, 4);      // 写入数据  这里是对应单个SEG写的 故而为4

    LCD_CS_1(); // 拉高CS 结束
}

在源文件里有详细的代码,代码都有详细的备注

ONE-Wire(DS18B20)

DS18B20注意事项:时序要求,在使用前请确认你的延迟函数精度能到1us,建议使用系统时针。

/*
 * @Author       :王   硕
 * @Date         :2024-01-03 14:47:29
 * @LastEditTime :2024-01-03 15:34:22
 * @Description  :
 */

#include "DS18B20.h"

/**
 * @description: IO口输出初始化
 * @param none
 * @return none
 */
void DS18B20_IO_OUT(void)
{
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStructure.Pin = DS18B20_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
    GPIO_Peripheral_Initialize(DS18B20_PORT, &GPIO_InitStructure);
}

/**
 * @description: IO口输入初始化
 * @param none
 * @return none
 */
void DS18B20_IO_IN(void)
{
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStructure.Pin = DS18B20_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_INPUT;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
    GPIO_Peripheral_Initialize(DS18B20_PORT, &GPIO_InitStructure);
}

/**
 * @description: 复位DS18B20
 * @param none
 * @return none
 */
void DS18B20_Rst(void)
{
    DS18B20_IO_OUT(); // 设置为输出
    DS18B20_DQ_OUT_0; // 拉低DQ
    delay_us(750);    // 拉低750us
    DS18B20_DQ_OUT_1; // DQ=1
    delay_us(15);
}

/**
 * @description: 等待DS18B20的回应
 * @param none
 * @return 返回1:未检测到DS18B20的存在 返回0:存在
 */
u8 DS18B20_Check(void)
{
    u8 retry = 0;
    DS18B20_IO_IN(); // 设置为输入
    while (DS18B20_DQ_IN && retry < 200)
    {
        retry++;
        delay_us(1);
    };
    if (retry >= 200)
    {
        return 1;
    }

    else
    {
        retry = 0;
    }
    while (!DS18B20_DQ_IN && retry < 240)
    {
        retry++;
        delay_us(1);
    };
    if (retry >= 240)
        return 1;
    return 0;
}

/**
 * @description: 从DS18B20读取一个位
 * @param none
 * @return 1/0
 */
u8 DS18B20_Read_Bit(void)
{
    u8 data;
    DS18B20_IO_OUT(); // 设置为输出
    DS18B20_DQ_OUT_0;
    delay_us(2);
    DS18B20_DQ_OUT_1;
    DS18B20_IO_IN(); // 设置为输入
    delay_us(12);
    if (DS18B20_DQ_IN)
    {
        data = 1;
    }
    else
    {
        data = 0;
    }
    delay_us(50);
    return data;
}

/**
 * @description: 从DS18B20读取一个字节
 * @param none
 * @return dat
 */
u8 DS18B20_Read_Byte(void)
{
    u8 i, j, dat;
    dat = 0;
    for (i = 1; i <= 8; i++)
    {
        j = DS18B20_Read_Bit();
        dat = (j << 7) | (dat >> 1);
    }
    return dat;
}

/**
 * @description: 写一个字节到DS18B20
 * @param u8 dat
 * @return dat:要写入的字节
 */
void DS18B20_Write_Byte(u8 dat)
{
    u8 j;
    u8 testb;
    DS18B20_IO_OUT(); // 设置为输出
    for (j = 1; j <= 8; j++)
    {
        testb = dat & 0x01;
        dat = dat >> 1;
        if (testb)
        {
            DS18B20_DQ_OUT_0; // 写1
            delay_us(10);
            DS18B20_DQ_OUT_1;
            delay_us(60);
        }
        else
        {
            DS18B20_DQ_OUT_0; // 写0
            delay_us(60);
            DS18B20_DQ_OUT_1;
            delay_us(10);
        }
    }
}

/**
 * @description: 开始温度转换
 * @param none
 * @return none
 */
void DS18B20_Start(void)
{
    DS18B20_Rst();
    DS18B20_Check();
    DS18B20_Write_Byte(0xcc); // 跳过 ROM
    DS18B20_Write_Byte(0x44); // 转换
}

/**
 * @description: 初始化DS18B20的IO口 DQ 同时检测DS的存在
 * @param none
 * @return 返回1:不存在
 * @return 返回0:存在
 */
u8 DS18B20_Init(void)
{
    RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOA);
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStructure.Pin = DS18B20_PIN; // 推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_OUT_PP;
    GPIO_InitStructure.GPIO_Slew_Rate = GPIO_SLEW_RATE_FAST;
    GPIO_Peripheral_Initialize(DS18B20_PORT, &GPIO_InitStructure);

    GPIO_Pins_Set(DS18B20_PORT, DS18B20_PIN); // 拉高

    DS18B20_Rst();

    return DS18B20_Check();
}

/**
 * @description: 从ds18b20得到温度值
 * @description: 精度:0.1C
 * @param none
 * @return 温度值 (-550~1250)
 */
short DS18B20_Get_Temp(void)
{
    u8 temp;
    u8 TL, TH;
    short tem;
    DS18B20_Start(); // ds1820 start convert
    DS18B20_Rst();
    DS18B20_Check();
    DS18B20_Write_Byte(0xcc); // skip rom
    DS18B20_Write_Byte(0xbe); // convert
    TL = DS18B20_Read_Byte(); // LSB
    TH = DS18B20_Read_Byte(); // MSB

    if (TH > 7)
    {
        TH = ~TH;
        TL = ~TL;
        temp = 0; // 温度为负
    }
    else
    {
        temp = 1; // 温度为正
    }

    tem = TH; // 获得高八位
    tem <<= 8;
    tem += TL;                // 获得底八位
    tem = (float)tem * 0.625; // 转换
    if (temp)
    {
        return tem; // 返回温度值
    }

    else
    {
        return -tem;
    }
}

代码仅限于参考时序图,如果直接CV是肯定用不了的,源代码链接在最后

USART

/*
 * @Author       :王   硕
 * @Date         :2024-01-03 14:47:29
 * @LastEditTime :2024-01-03 15:35:06
 * @Description  :
 */

#include "USART.h"

/**
 * @name     USART2_Init
 * @function USART2初始化  逻辑"0"发送 逻辑"1"接收
 * @param    none
 * @return   none
 */
void USART2_Init(void)
{
    RCC_APB1_Peripheral_Clock_Enable(RCC_APB1_PERIPH_USART2);
    RCC_AHB_Peripheral_Clock_Enable(RCC_AHB_PERIPH_GPIOD);
    RCC_APB2_Peripheral_Clock_Enable(RCC_APB2_PERIPH_AFIO);

    GPIO_InitType GPIO_InitStructure;
    /* 配置USARTy Tx为复用推挽输出 */
    GPIO_InitStructure.Pin = GPIO_PIN_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF4_USART2;
    GPIO_Peripheral_Initialize(GPIOD, &GPIO_InitStructure);

    /* 配置USARTy Rx为复用推挽输入和上拉输入 */
    GPIO_InitStructure.Pin = GPIO_PIN_14;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF4_USART2;
    GPIO_Peripheral_Initialize(GPIOD, &GPIO_InitStructure);

    USART_InitType USART2_InitStructure;
    USART2_InitStructure.BaudRate = 9600;
    USART2_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
    USART2_InitStructure.WordLength = USART_WL_8B;
    USART2_InitStructure.Parity = USART_PE_NO;
    USART2_InitStructure.StopBits = USART_STPB_1;
    USART2_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_Initializes(USART2, &USART2_InitStructure);

    NVIC_InitType NVIC_InitStructure;
    NVIC_Priority_Group_Set(NVIC_PER2_SUB2_PRIORITYGROUP);
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Initializes(&NVIC_InitStructure);

    USART_Interrput_Enable(USART2, USART_INT_RXDNE);
    USART_Interrput_Enable(USART2, USART_INT_TXDE);

    USART_Enable(USART2);
}

IWDG

/*
 * @Author       :王   硕
 * @Date         :2024-01-03 14:47:29
 * @LastEditTime :2024-01-03 15:34:33
 * @Description  :
 */

#include "IWDG.h"

__IO uint32_t LsiFreq = 40000; // 内部低速时钟频率,初始值设定为 40000

void IWDG_Config(IWDG_CONFIG_PRESCALER IWDG_prescaler, uint16_t reload_value)
{
    /* 由于 LSI 频率变化,超时时间可能有所不同 */
    /* 关闭 IWDG_PREDIV 和 IWDG_RELV 寄存器的写保护 */
    IWDG_Write_Protection_Disable();

    /* IWDG 计数器时钟预分频设置 */
    IWDG_Prescaler_Division_Set(IWDG_prescaler);

    /* 设置 IWDG 重装载值 */
    /* 设置计数器重装载值以获取 x 秒的 IWDG 超时。
       计数器重装载值时间 = x(秒) / IWDG 计数器时钟周期
                         = x(秒) / (LSI/IWDG_prescaler)
    */
    IWDG_Counter_Reload(reload_value);

    /* 使用 IWDG_PREDIV 的值重新加载计数器到 IWDG_RELV 寄存器,以防止复位 */
    IWDG_Key_Reload();

    /* 启用 IWDG(LSI 振荡器将由硬件启用) */
    IWDG_Enable();
}

void IWDG_Feed(void)
{
    /* 将重装载寄存器的值写入计数器 */
    IWDG_Key_Reload();
}

DELAY(系统时钟延迟)

/*
 * @Author       :王   硕
 * @Date         :2024-01-03 14:47:29
 * @LastEditTime :2024-01-03 15:34:11
 * @Description  :
 */

#include "DELAY.h"

/**
 * @brief SysTick_Config 函数用于配置 SysTick 定时器
 * @param ticks: SysTick 定时器的计数值
 * @return 0 表示配置成功,1 表示配置失败(计数值过大)
 */
static uint32_t DBG_SysTick_Config(uint32_t ticks)
{
    if (ticks > SysTick_LOAD_RELOAD_Msk)
        return (1); /* 重新加载值不合理 */

    SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* 设置重新加载寄存器 */
    NVIC_SetPriority(SysTick_IRQn, (1 << __NVIC_PRIO_BITS) - 1); /* 设置 Cortex-M0 系统中断的优先级 */
    SysTick->VAL = 0;                                            /* 加载 SysTick 计数器的初始值 */
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                    SysTick_CTRL_ENABLE_Msk; /* 启用 SysTick IRQ 和 SysTick 定时器 */

    SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; /* 禁用 SysTick 中断 */
    SysTick->VAL = 0;
    return (0); /* 配置成功 */
}

/**
 * @brief SysTick_Delay_Us 函数用于提供微秒级延时
 * @param us: 延时的微秒数
 * @return 无
 */
void SysTick_Delay_Us(__IO uint32_t us)
{
    volatile uint32_t i;
    RCC_ClocksType RCC_Clocks;

    RCC_Clocks_Frequencies_Value_Get(&RCC_Clocks);       /* 获取系统时钟频率 */
    DBG_SysTick_Config(RCC_Clocks.SysclkFreq / 1000000); /* 配置 SysTick 定时器为微秒分辨率 */

    for (i = 0; i < us; i++)
    {
        /* 当计数器值减至0时,CRTL 寄存器的第16位将被设置为1 */
        /* 当设置为1时,读取此位将其清零 */
        while (!(SysTick->CTRL & (1 << 16)))
            ;
    }
    /* 关闭 SysTick 定时器 */
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}

/**
 * @brief SysTick_Delay_Ms 函数用于提供毫秒级延时
 * @param ms: 延时的毫秒数
 * @return 无
 */
void SysTick_Delay_Ms(__IO uint32_t ms)
{
    volatile uint32_t i;
    RCC_ClocksType RCC_Clocks;

    RCC_Clocks_Frequencies_Value_Get(&RCC_Clocks);    /* 获取系统时钟频率 */
    DBG_SysTick_Config(RCC_Clocks.SysclkFreq / 1000); /* 配置 SysTick 定时器为毫秒分辨率 */

    for (i = 0; i < ms; i++)
    {
        /* 当计数器值减至0时,CRTL 寄存器的第16位将被设置为1 */
        /* 当设置为1时,读取此位将其清零 */
        while (!(SysTick->CTRL & (1 << 16)))
            ;
    }
    /* 关闭 SysTick 定时器 */
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}


void systick_delay_us(u32 nus)
{
    u32 temp;
    SysTick_Clock_Source_Set(SYSTICK_CLKSOURCE_HCLK);       // select system clock
    SysTick->LOAD = nus * (SystemClockFrequency / 1000000); // time relode
    SysTick->VAL = 0x00;                                    // clear timer value
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;               // Start countdown
    do
    {
        temp = SysTick->CTRL;
    } while (temp & 0x01 && !(temp & (1 << 16))); // wait for the time reach
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;    // close the count
    SysTick->VAL = 0X00;                          // clear timer value
}

/**
 *\*\name    systick_delay_ms.
 *\*\fun     ms delay  program function.
 *\*\param   nms
 *\*\return  none
 **/
void systick_delay_ms(u16 nms)
{
    u32 temp;
    SysTick_Clock_Source_Set(SYSTICK_CLKSOURCE_HCLK);                     // select system clock
    SysTick->LOAD = (u32)nms * ((SystemClockFrequency / 1000000) * 1000); // time relode(SysTick->LOAD is 24bit)
    SysTick->VAL = 0x00;                                                  // clear timer value
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;                             // Start countdown
    do
    {
        temp = SysTick->CTRL;
    } while (temp & 0x01 && !(temp & (1 << 16))); // wait for the time reach
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;    // close the count
    SysTick->VAL = 0X00;                          // clear timer value
}

源码地址:GitHub – wayneso/N32G401_Drive: 国名技术N32G401配置各种驱动国名技术N32G401配置各种驱动. Contribute to wayneso/N32G401_Drive development by creating an account on GitHub.https://github.com/wayneso/N32G401_Drive.git

 (如果觉得内容不错,帮忙点亮星星~)

物联沃分享整理
物联沃-IOTWORD物联网 » N32G401单片机驱动配置详解(适用于STM32系列)

发表评论