462 lines
11 KiB
C++
462 lines
11 KiB
C++
#include "Sys.h"
|
||
#include "Kernel\Interrupt.h"
|
||
#include "Device\Port.h"
|
||
|
||
#include "Platform\stm32.h"
|
||
|
||
#if defined(STM32F1) || defined(STM32F4)
|
||
static const byte PORT_IRQns[] = {
|
||
EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn, // 5个基础的
|
||
EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, EXTI9_5_IRQn, // EXTI9_5
|
||
EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn, EXTI15_10_IRQn // EXTI15_10
|
||
};
|
||
#elif defined(STM32F0) || defined(GD32F150)
|
||
static const byte PORT_IRQns[] = {
|
||
EXTI0_1_IRQn, EXTI0_1_IRQn, // 基础
|
||
EXTI2_3_IRQn, EXTI2_3_IRQn, // 基础
|
||
EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn,
|
||
EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn, EXTI4_15_IRQn // EXTI15_10
|
||
};
|
||
#endif
|
||
|
||
//_force_inline GPIO_TypeDef* IndexToGroup(byte index) { return ((GPIO_TypeDef *) (GPIOA_BASE + (index << 10))); }
|
||
//_force_inline byte GroupToIndex(GPIO_TypeDef* group) { return (byte)(((int)group - GPIOA_BASE) >> 10); }
|
||
|
||
/******************************** Port ********************************/
|
||
|
||
// 端口基本功能
|
||
#define REGION_Port 1
|
||
#ifdef REGION_Port
|
||
|
||
static GPIO_TypeDef* IndexToGroup(byte index) { return ((GPIO_TypeDef *) (GPIOA_BASE + (index << 10))); }
|
||
|
||
// 分组时钟
|
||
static byte _GroupClock[10];
|
||
|
||
static void OpenClock(Pin pin, bool flag)
|
||
{
|
||
int gi = pin >> 4;
|
||
|
||
if(flag)
|
||
{
|
||
// 增加计数,首次打开时钟
|
||
if(_GroupClock[gi]++) return;
|
||
}
|
||
else
|
||
{
|
||
// 减少计数,最后一次关闭时钟
|
||
if(_GroupClock[gi]-- > 1) return;
|
||
}
|
||
|
||
FunctionalState fs = flag ? ENABLE : DISABLE;
|
||
#if defined(STM32F0) || defined(GD32F150)
|
||
RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOAEN << gi, fs);
|
||
#elif defined(STM32F1)
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA << gi, fs);
|
||
#elif defined(STM32F4)
|
||
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA << gi, fs);
|
||
#endif
|
||
}
|
||
|
||
// 确定配置,确认用对象内部的参数进行初始化
|
||
void Port::OpenPin()
|
||
{
|
||
// 先打开时钟才能配置
|
||
OpenClock(_Pin, true);
|
||
|
||
GPIO_InitTypeDef gpio;
|
||
// 特别要慎重,有些结构体成员可能因为没有初始化而酿成大错
|
||
GPIO_StructInit(&gpio);
|
||
|
||
OnOpen(&gpio);
|
||
|
||
GPIO_Init(IndexToGroup(_Pin >> 4), &gpio);
|
||
}
|
||
|
||
void Port::OnOpen(void* param)
|
||
{
|
||
auto gpio = (GPIO_InitTypeDef*)param;
|
||
gpio->GPIO_Pin = 1 << (_Pin & 0x0F);
|
||
|
||
#ifdef STM32F1
|
||
// PA15/PB3/PB4 需要关闭JTAG
|
||
switch(_Pin)
|
||
{
|
||
case PA15:
|
||
case PB3:
|
||
case PB4:
|
||
{
|
||
debug_printf("Close JTAG Pin P%c%d \r\n", _PIN_NAME(_Pin));
|
||
|
||
// PA15是jtag接口中的一员 想要使用 必须开启remap
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
|
||
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void Port::OnClose()
|
||
{
|
||
// 不能随便关闭时钟,否则可能会影响别的引脚
|
||
OpenClock(_Pin, false);
|
||
}
|
||
|
||
void Port::RemapConfig(uint param, bool sta)
|
||
{
|
||
#ifdef STM32F1
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
|
||
GPIO_PinRemapConfig(param, sta ? ENABLE : DISABLE);
|
||
#endif
|
||
}
|
||
|
||
void Port::AFConfig(GPIO_AF GPIO_AF) const
|
||
{
|
||
#if defined(STM32F0) || defined(GD32F150) || defined(STM32F4)
|
||
assert(Opened, "打开后才能配置AF");
|
||
|
||
GPIO_PinAFConfig(IndexToGroup(_Pin >> 4), _PIN(_Pin), GPIO_AF);
|
||
#endif
|
||
}
|
||
|
||
bool Port::Read() const
|
||
{
|
||
if(_Pin == P0) return false;
|
||
|
||
auto gp = IndexToGroup(_Pin >> 4);
|
||
ushort ms = 1 << (_Pin & 0x0F);
|
||
return GPIO_ReadInputData(gp) & ms;
|
||
}
|
||
#endif
|
||
|
||
/******************************** OutputPort ********************************/
|
||
|
||
// 输出端口
|
||
#define REGION_Output 1
|
||
#ifdef REGION_Output
|
||
|
||
void OutputPort::OpenPin(void* param)
|
||
{
|
||
#ifndef STM32F4
|
||
assert(Speed == 2 || Speed == 10 || Speed == 50, "Speed");
|
||
#else
|
||
assert(Speed == 2 || Speed == 25 || Speed == 50 || Speed == 100, "Speed");
|
||
#endif
|
||
|
||
auto gpio = (GPIO_InitTypeDef*)param;
|
||
|
||
switch(Speed)
|
||
{
|
||
case 2: gpio->GPIO_Speed = GPIO_Speed_2MHz; break;
|
||
#ifndef STM32F4
|
||
case 10: gpio->GPIO_Speed = GPIO_Speed_10MHz; break;
|
||
#else
|
||
case 25: gpio->GPIO_Speed = GPIO_Speed_25MHz; break;
|
||
case 100: gpio->GPIO_Speed = GPIO_Speed_100MHz;break;
|
||
#endif
|
||
case 50: gpio->GPIO_Speed = GPIO_Speed_50MHz; break;
|
||
}
|
||
|
||
#ifdef STM32F1
|
||
gpio->GPIO_Mode = OpenDrain ? GPIO_Mode_Out_OD : GPIO_Mode_Out_PP;
|
||
#else
|
||
gpio->GPIO_Mode = GPIO_Mode_OUT;
|
||
gpio->GPIO_OType = OpenDrain ? GPIO_OType_OD : GPIO_OType_PP;
|
||
#endif
|
||
}
|
||
|
||
bool OutputPort::Read() const
|
||
{
|
||
if(Empty()) return false;
|
||
|
||
auto gp = IndexToGroup(_Pin >> 4);
|
||
ushort ms = 1 << (_Pin & 0x0F);
|
||
// 转为bool时会转为0/1
|
||
bool rs = GPIO_ReadOutputData(gp) & ms;
|
||
return rs ^ Invert;
|
||
}
|
||
|
||
bool OutputPort::ReadInput() const
|
||
{
|
||
if(Empty()) return false;
|
||
|
||
return Port::Read() ^ Invert;
|
||
}
|
||
|
||
void OutputPort::Write(bool value) const
|
||
{
|
||
if(Empty()) return;
|
||
|
||
auto gi = IndexToGroup(_Pin >> 4);
|
||
ushort ms = 1 << (_Pin & 0x0F);
|
||
if(value ^ Invert)
|
||
GPIO_SetBits(gi, ms);
|
||
else
|
||
GPIO_ResetBits(gi, ms);
|
||
}
|
||
|
||
// 设置端口状态
|
||
void OutputPort::Write(Pin pin, bool value)
|
||
{
|
||
if(pin == P0) return;
|
||
|
||
if(value)
|
||
GPIO_SetBits(_GROUP(pin), _PORT(pin));
|
||
else
|
||
GPIO_ResetBits(_GROUP(pin), _PORT(pin));
|
||
}
|
||
|
||
/******************************** AlternatePort ********************************/
|
||
|
||
void AlternatePort::OpenPin(void* param)
|
||
{
|
||
auto gpio = (GPIO_InitTypeDef*)param;
|
||
|
||
#ifdef STM32F1
|
||
gpio->GPIO_Mode = OpenDrain ? GPIO_Mode_AF_OD : GPIO_Mode_AF_PP;
|
||
#else
|
||
gpio->GPIO_Mode = GPIO_Mode_AF;
|
||
gpio->GPIO_OType = OpenDrain ? GPIO_OType_OD : GPIO_OType_PP;
|
||
#endif
|
||
}
|
||
|
||
#endif
|
||
|
||
/******************************** InputPort ********************************/
|
||
|
||
// 输入端口
|
||
#define REGION_Input 1
|
||
#ifdef REGION_Input
|
||
/* 中断状态结构体 */
|
||
/* 一共16条中断线,意味着同一条线每一组只能有一个引脚使用中断 */
|
||
typedef struct TIntState
|
||
{
|
||
InputPort* Port;
|
||
} IntState;
|
||
|
||
// 16条中断线
|
||
static IntState States[16];
|
||
static bool hasInitState = false;
|
||
|
||
int Bits2Index(ushort value)
|
||
{
|
||
for(int i=0; i<16; i++)
|
||
{
|
||
if(value & 0x01) return i;
|
||
value >>= 1;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
#define IT 1
|
||
#ifdef IT
|
||
void GPIO_ISR(int num) // 0 <= num <= 15
|
||
{
|
||
if(!hasInitState) return;
|
||
|
||
auto st = &States[num];
|
||
// 如果未指定委托,则不处理
|
||
if(!st || !st->Port) return;
|
||
|
||
uint bit = 1 << num;
|
||
bool value;
|
||
do {
|
||
EXTI->PR = bit; // 重置挂起位
|
||
value = st->Port->Read(); // 获取引脚状态
|
||
} while (EXTI->PR & bit); // 如果再次挂起则重复
|
||
// Read的时候已经计算倒置,这里不必重复计算
|
||
st->Port->OnPress(value);
|
||
}
|
||
|
||
void EXTI_IRQHandler(ushort num, void* param)
|
||
{
|
||
#if defined(STM32F1) || defined(STM32F4)
|
||
// EXTI0 - EXTI4
|
||
if(num <= EXTI4_IRQn)
|
||
GPIO_ISR(num - EXTI0_IRQn);
|
||
else
|
||
#endif
|
||
{
|
||
uint pending = EXTI->PR & EXTI->IMR;
|
||
for(int i=0; i < 16 && pending != 0; i++, pending >>= 1)
|
||
{
|
||
if (pending & 1) GPIO_ISR(i);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
void SetEXIT(int pinIndex, bool enable, InputPort::Trigger mode)
|
||
{
|
||
/* 配置EXTI中断线 */
|
||
EXTI_InitTypeDef ext;
|
||
EXTI_StructInit(&ext);
|
||
ext.EXTI_Line = EXTI_Line0 << pinIndex;
|
||
ext.EXTI_Mode = EXTI_Mode_Interrupt;
|
||
if(mode == InputPort::Rising)
|
||
ext.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发
|
||
else if(mode == InputPort::Falling)
|
||
ext.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发
|
||
else
|
||
ext.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 上升沿下降沿触发
|
||
|
||
ext.EXTI_LineCmd = enable ? ENABLE : DISABLE;
|
||
EXTI_Init(&ext);
|
||
}
|
||
|
||
void InputPort::OpenPin(void* param)
|
||
{
|
||
auto gpio = (GPIO_InitTypeDef*)param;
|
||
|
||
#ifdef STM32F1
|
||
if(Floating)
|
||
gpio->GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
||
else if(Pull == UP)
|
||
gpio->GPIO_Mode = GPIO_Mode_IPU;
|
||
else if(Pull == DOWN)
|
||
gpio->GPIO_Mode = GPIO_Mode_IPD; // 这里很不确定,需要根据实际进行调整
|
||
#else
|
||
gpio->GPIO_Mode = GPIO_Mode_IN;
|
||
//gpio->GPIO_OType = !Floating ? GPIO_OType_OD : GPIO_OType_PP;
|
||
#endif
|
||
}
|
||
|
||
// 是否独享中断号
|
||
bool IsOnlyExOfInt(const InputPort* pt, int idx)
|
||
{
|
||
int s=0, e=0;
|
||
#if defined(STM32F1) || defined(STM32F4)
|
||
if(idx <= 4) return true;
|
||
|
||
if(idx <= 9)
|
||
{
|
||
s = 5;
|
||
e = 9;
|
||
}
|
||
else if(idx <= 15)
|
||
{
|
||
s = 10;
|
||
e = 15;
|
||
}
|
||
#elif defined(STM32F0) || defined(GD32F150)
|
||
if(idx <= 1)
|
||
{
|
||
s = 0;
|
||
e = 1;
|
||
}
|
||
else if(idx <= 3)
|
||
{
|
||
s = 2;
|
||
e = 3;
|
||
}
|
||
else if(idx <= 15)
|
||
{
|
||
s = 4;
|
||
e = 15;
|
||
}
|
||
#endif
|
||
for(int i = s; i <= e; i++)
|
||
if(States[i].Port != nullptr && States[i].Port != pt) return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
InputPort::Trigger GetTrigger(InputPort::Trigger mode, bool invert)
|
||
{
|
||
if(invert && mode != InputPort::Both)
|
||
{
|
||
// 把上升沿下降沿换过来
|
||
if(mode == InputPort::Rising)
|
||
mode = InputPort::Falling;
|
||
else if(mode == InputPort::Falling)
|
||
mode = InputPort::Rising;
|
||
}
|
||
|
||
return mode;
|
||
}
|
||
|
||
void InputPort::ClosePin()
|
||
{
|
||
int idx = Bits2Index(1 << (_Pin & 0x0F));
|
||
|
||
auto st = &States[idx];
|
||
if(st->Port == this)
|
||
{
|
||
st->Port = nullptr;
|
||
|
||
SetEXIT(idx, false, GetTrigger(Mode, Invert));
|
||
if(!IsOnlyExOfInt(this, idx))return;
|
||
Interrupt.Deactivate(PORT_IRQns[idx]);
|
||
}
|
||
}
|
||
|
||
// 注册回调 及中断使能
|
||
bool InputPort::OnRegister()
|
||
{
|
||
// 检查并初始化中断线数组
|
||
if(!hasInitState)
|
||
{
|
||
for(int i=0; i<16; i++)
|
||
{
|
||
States[i].Port = nullptr;
|
||
}
|
||
hasInitState = true;
|
||
}
|
||
|
||
byte gi = _Pin >> 4;
|
||
int idx = Bits2Index(1 << (_Pin & 0x0F));
|
||
auto st = &States[idx];
|
||
|
||
auto port = st->Port;
|
||
// 检查是否已经注册到别的引脚上
|
||
if(port != this && port != nullptr)
|
||
{
|
||
#if DEBUG
|
||
debug_printf("中断线EXTI%d 不能注册到 P%c%d, 它已经注册到 P%c%d\r\n", gi, _PIN_NAME(_Pin), _PIN_NAME(port->_Pin));
|
||
#endif
|
||
|
||
// 将来可能修改设计,即使注册失败,也可以开启一个短时间的定时任务,来替代中断输入
|
||
return false;
|
||
}
|
||
st->Port = this;
|
||
|
||
//byte gi = _Pin >> 4;
|
||
//int idx = Bits2Index(1 << (_Pin & 0x0F));
|
||
|
||
// 打开时钟,选择端口作为端口EXTI时钟线
|
||
#if defined(STM32F0) || defined(GD32F150) || defined(STM32F4)
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
|
||
SYSCFG_EXTILineConfig(gi, idx);
|
||
#elif defined(STM32F1)
|
||
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
|
||
GPIO_EXTILineConfig(gi, idx);
|
||
#endif
|
||
|
||
SetEXIT(idx, true, GetTrigger(Mode, Invert));
|
||
|
||
// 打开并设置EXTI中断为低优先级
|
||
Interrupt.SetPriority(PORT_IRQns[idx], 1);
|
||
|
||
Interrupt.Activate(PORT_IRQns[idx], EXTI_IRQHandler, this);
|
||
|
||
return true;
|
||
}
|
||
|
||
#endif
|
||
|
||
/******************************** AnalogInPort ********************************/
|
||
|
||
void AnalogInPort::OpenPin(void* param)
|
||
{
|
||
auto gpio = (GPIO_InitTypeDef*)param;
|
||
|
||
#ifdef STM32F1
|
||
gpio->GPIO_Mode = GPIO_Mode_AIN; //
|
||
#else
|
||
gpio->GPIO_Mode = GPIO_Mode_AN;
|
||
//gpio->GPIO_OType = !Floating ? GPIO_OType_OD : GPIO_OType_PP;
|
||
#endif
|
||
}
|