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文章来源:https://www.toymoban.com/news/detail-805516.html
(如果觉得内容不错,帮忙点亮星星~)文章来源地址https://www.toymoban.com/news/detail-805516.html
到了这里,关于【国名技术】N32G401单片机驱动配置(STM32系列适用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!