【国名技术】N32G401单片机驱动配置(STM32系列适用)

这篇具有很好参考价值的文章主要介绍了【国名技术】N32G401单片机驱动配置(STM32系列适用)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【国名技术】N32G401单片机驱动配置(STM32系列适用),单片机,嵌入式硬件,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也能进行类似的操作,不过比较复杂,所以不建议使用

【国名技术】N32G401单片机驱动配置(STM32系列适用),单片机,嵌入式硬件,stm32,驱动开发

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


// 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

 (如果觉得内容不错,帮忙点亮星星~)文章来源地址https://www.toymoban.com/news/detail-805516.html

到了这里,关于【国名技术】N32G401单片机驱动配置(STM32系列适用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 国民技术N32G430开发笔记(12)- IAP升级 Settings区域数据初始化

    1、假如,有两个产品,A产品跟B产品,硬件都一样,要求一个软件里的board_name为N32G430C8L7_STB_A,另一个软件里的board_name为N32G430C8L7_STB_B。 那我们如何在不改boot程序跟App程序的基础上,快速的使两个软件看上去不同呢? 这里我们将setting区域的数据提前初始化,通过c语言的文

    2024年02月01日
    浏览(52)
  • 国民技术N32G430开发笔记(18)- I2C1 从机收发数据

    1、将PB6 PB7设置为i2c从机,跟android板卡通讯。 2、Android发送 写命令: 0x05 0x02 0x00 0x00 checksum n32将收到的命令打印出来 读版本命令: 0x01 0x02 0x00 0x00 checksum n32将app_version返回电视端 3、i2c从机配置,采用中断方式收发数据。 主要代码: 从机配置代码: 中断处理函数: 接收到数

    2024年02月03日
    浏览(82)
  • STC32G单片机内置ADC及应用编程

          STC32G单片机内部集成了一个12位高速ADC转换器,ADC的最高时钟频率为系统频率的1/2。其输入通道多达15个(第15通道为专门测量内部1.19V参考信号源的通道),可分时切换使用。       STC15系列单片机内置ADC模块以电源电源作为ADC参考电压,STC32G的ADC模块则与之不同,它

    2024年02月09日
    浏览(77)
  • 【N32G457】 基于RT-Thread和N32G457的迷你键盘

    此文为参加RT-Thread与国民技术联手推出N32G457 RT-Thread设计大赛期间作品。 由于疫情影响加上其他各种原因,本应在3月25日结束的比赛在今日终于完成了一个比较完整的作品,无论是否可以参赛,还是希望对大家有所帮助。 先放个图。 机械键盘在最近几年发展迅猛,功能也逐

    2024年02月09日
    浏览(40)
  • 单片机移植freemodbus主机(STM32、GD32、瑞萨、国民技术等)

    从github下载:https://github.com/armink/FreeModbus_Slave-Master-RTT-STM32 无法下载或者下载太慢可以用资源下载,无需积分。freeModbus主机源码下载 示例代码 参考从机代码创建 将FreeModbus主机源代码拷贝到工程中间件-第三方库-freemodbus源码库:即MiddlewaresThird_PartyFreeModbusMaster文件夹中。

    2024年02月11日
    浏览(47)
  • GD32单片机和STM32单片机的对比分析

    GD32单片机和STM32单片机都是基于Arm Cortex-M3/M4内核的32位通用微控制器,广泛应用于各种嵌入式系统和物联网领域。两者之间有很多相似之处,但也有一些不同之处,本文将从以下几个方面对比分析两者的特点、优势和开发成本。 GD32单片机采用的是二代的M3/M4内核,而STM32单片

    2024年02月16日
    浏览(63)
  • STM32单片机(一)STM32简介

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月10日
    浏览(60)
  • 【单片机】STM32单片机的各个定时器的定时中断程序,标准库,STM32F103

    高级定时器和普通定时器的区别(https://zhuanlan.zhihu.com/p/557896041): TIM1是高级定时器,使用的时钟总线是RCC_APB2Periph_TIM1,和普通定时器不一样。 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用 timer.c timer.h 调用

    2024年02月07日
    浏览(58)
  • STM32单片机(二)STM32环境搭建

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月10日
    浏览(63)
  • STM32单片机开发-01 STM32介绍

    通过野火开发板学习单片机 从内核上分有Cortex-M0、M3、M4 和M7 F1 代表了基础型,基于Cortex-M3 内核,主频为72MHZ F4 代表了高性能,基于Cortex-M4 内核,主频180M。 数据手册:用于芯片选型和设计原理图 参考手册:用于编程时查阅 Icode总线 – 该总线讲M3内核的指令总线与闪存指令

    2024年01月21日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包