/**
****************************************************************************************************
* @file sys.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2022-10-15
* @brief 系统初始化代码(包括时钟配置、中断管理、GPIO设置等)
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 APM32F407最小系统板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
****************************************************************************************************
*/
#include "./SYSTEM/sys/sys.h"
/**
* @brief 设置中断向量表偏移地址
* @param baseaddr: 基地址
* @param offset : 偏移量(必须是0x100的0或正整数倍)
* @retval 无
*/
void sys_nvic_set_vector_table(uint32_t baseaddr, uint32_t offset)
{
/* 设置向量表偏移寄存器 */
SCB->VTOR = baseaddr | (offset & (uint32_t)0x1FFFFF80);
}
/**
* @brief 设置中断优先级分组
* @param group: 中断优先级分组
* @arg 0: 组0,0位抢占优先级,4位子优先级
* @arg 1: 组1,1位抢占优先级,3位子优先级
* @arg 2: 组2,2位抢占优先级,2位子优先级
* @arg 3: 组3,3位抢占优先级,1位子优先级
* @arg 4: 组4,4位抢占优先级,0位子优先级
* @retval 无
*/
static void sys_nvic_priority_group_config(uint8_t group)
{
SCB->AIRCR = ((uint32_t)0x05FA0000) | (((~group) & 0x07) << 8);
}
/**
* @brief 设置NVIC(包括中断优先级分组、抢占优先级、子优先级等)
* @param pprio: 抢占优先级
* @param sprio: 子优先级
* @param ch : 中断编号
* @param group: 中断优先级分组
* @arg 0: 组0,0位抢占优先级,4位子优先级
* @arg 1: 组1,1位抢占优先级,3位子优先级
* @arg 2: 组2,2位抢占优先级,2位子优先级
* @arg 3: 组3,3位抢占优先级,1位子优先级
* @arg 4: 组4,4位抢占优先级,0位子优先级
* @retval 无
*/
void sys_nvic_init(uint8_t pprio, uint8_t sprio, uint8_t ch, uint8_t group)
{
uint32_t prio;
sys_nvic_priority_group_config(group); /* 设置中断优先级分组 */
prio = pprio << (4 - group);
prio |= sprio & (0xF >> group);
prio &= 0xF;
prio <<= 4;
NVIC->IP[ch] |= prio; /* 设置抢占优先级和子优先级 */
NVIC->ISER[ch >> 5] |= 1 << (ch & 0x1F); /* 使能中断 */
}
/**
* @brief 配置外部中断
* @param p_gpiox: GPIO端口
* @param pinx : GPIO引脚
* @param tmode : 触发模式
* @arg SYS_GPIO_FTIR: 下降沿触发
* @arg SYS_GPIO_RTIR: 上升沿触发
* @arg SYS_GPIO_BTIR: 双边沿触发
* @retval 无
*/
void sys_nvic_ex_config(GPIO_T *p_gpiox, uint16_t pinx, uint8_t tmode)
{
uint32_t gpio_num;
uint32_t pinpos;
uint32_t pos;
uint32_t curpin;
uint8_t offset;
gpio_num = ((uint32_t)p_gpiox - (uint32_t)GPIOA) >> 10; /* 计算GPIO端口编号,0~10对应GPIOA~GPIOK */
RCM->APB2CLKEN_B.SYSCFGEN = ENABLE; /* 使能SYSCFG时钟 */
for (pinpos=0; pinpos<16; pinpos++)
{
pos = 1 << pinpos;
curpin = pinx & pos;
if (curpin == pos)
{
offset = (pinpos >> 2) << 2;
if ((pinpos >> 2) == 0)
{
SYSCFG->EINTCFG1 &= ~(0x000F << offset); /* 清除原来设置 */
SYSCFG->EINTCFG1 |= gpio_num << offset; /* 设置外部中断源 */
}
else if ((pinpos >> 2) == 1)
{
SYSCFG->EINTCFG2 &= ~(0x000F << offset); /* 清除原来设置 */
SYSCFG->EINTCFG2 |= gpio_num << offset; /* 设置外部中断源 */
}
else if ((pinpos >> 2) == 2)
{
SYSCFG->EINTCFG3 &= ~(0x000F << offset); /* 清除原来设置 */
SYSCFG->EINTCFG3 |= gpio_num << offset; /* 设置外部中断源 */
}
else if ((pinpos >> 2) == 3)
{
SYSCFG->EINTCFG4 &= ~(0x000F << offset); /* 清除原来设置 */
SYSCFG->EINTCFG4 |= gpio_num << offset; /* 设置外部中断源 */
}
EINT->IMASK |= 1 << pinpos; /* 使能中断 */
if (tmode == SYS_GPIO_FTIR) /* 下降沿触发 */
{
EINT->FTEN |= 1 << pinpos;
}
else if (tmode == SYS_GPIO_RTIR) /* 上升沿触发 */
{
EINT->RTEN |= 1 << pinpos;
}
else if (tmode == SYS_GPIO_BTIR) /* 双边沿触发 */
{
EINT->FTEN |= 1 << pinpos;
EINT->RTEN |= 1 << pinpos;
}
}
}
}
/**
* @brief 配置GPIO引脚复用功能
* @param p_gpiox: GPIO端口
* @param pinx : GPIO引脚
* @param afx : 复用功能,0~15对应AF0~AF15
* @arg 请见《APM32F405xG 407xExG数据手册.pdf》表4~12
* @retval 无
*/
void sys_gpio_af_set(GPIO_T *p_gpiox, uint16_t pinx, uint8_t afx)
{
uint32_t pinpos;
uint32_t pos;
uint32_t curpin;
for (pinpos=0; pinpos<16; pinpos++)
{
pos = 1 << pinpos;
curpin = pinx & pos;
if (curpin == pos)
{
if ((pinpos >> 3) == 0)
{
p_gpiox->ALFL &= ~(0x0F << ((pinpos & 0x07) << 2));
p_gpiox->ALFL |= (uint32_t)afx << ((pinpos & 0x07) << 2);
}
else if ((pinpos >> 3) == 1)
{
p_gpiox->ALFH &= ~(0x0F << ((pinpos & 0x07) << 2));
p_gpiox->ALFH |= (uint32_t)afx << ((pinpos & 0x07) << 2);
}
}
}
}
/**
* @brief 配置GPIO
* @param p_gpiox: GPIO端口
* @param pinx : GPIO引脚
* @param mode : 模式
* @arg SYS_GPIO_MODE_IN : 输入模式
* @arg SYS_GPIO_MODE_OUT: 通用输出模式
* @arg SYS_GPIO_MODE_AF : 复用功能模式
* @arg SYS_GPIO_MODE_AIN: 模拟模式
* @param otype : 输出模式
* @arg SYS_GPIO_OTYPE_PP: 推挽输出
* @arg SYS_GPIO_OTYPE_OD: 开漏输出
* @param ospeed : 输出速度
* @arg SYS_GPIO_SPEED_LOW : 低速(2MHz)
* @arg SYS_GPIO_SPEED_MID : 中速(25MHz)
* @arg SYS_GPIO_SPEED_FAST: 快速(50MHz)
* @arg SYS_GPIO_SPEED_HIGH: 高速(100MHz)
* @param pupd : 上拉/下拉
* @arg SYS_GPIO_PUPD_NONE: 禁止上拉、下拉
* @arg SYS_GPIO_PUPD_PU : 上拉
* @arg SYS_GPIO_PUPD_PD : 下拉
* @arg SYS_GPIO_PUPD_RES : 复位
* @retval 无
*/
void sys_gpio_set(GPIO_T *p_gpiox, uint16_t pinx, uint32_t mode, uint32_t otype, uint32_t ospeed, uint32_t pupd)
{
uint32_t pinpos;
uint32_t pos;
uint32_t curpin;
for (pinpos=0; pinpos<16; pinpos++)
{
pos = 1 << pinpos;
curpin = pinx & pos;
if (curpin == pos)
{
p_gpiox->MODE &= ~(3 << (pinpos << 1)); /* 先清除原来的设置 */
p_gpiox->MODE |= mode << (pinpos << 1); /* 设置模式 */
if ((mode == SYS_GPIO_MODE_OUT) || (mode == SYS_GPIO_MODE_AF)) /* 输出速度和输出模式仅对通用输出模式和复用模式有效 */
{
p_gpiox->OSSEL &= ~(3 << (pinpos << 1)); /* 先清除原来的设置 */
p_gpiox->OSSEL |= (ospeed << (pinpos << 1)); /* 设置输出速度 */
p_gpiox->OMODE &= ~(1 << pinpos); /* 先清除原来的设置 */
p_gpiox->OMODE |= otype << pinpos; /* 设置输出模式 */
}
p_gpiox->