在后续的博客中,我找一些有谢不一样功能模块或者网上不太好找的现成传感器(如VL53L0X)代码,分享出来给各位智能小车爱好者,大家可以少走点弯路。如果你们有好的分享,请留言,谢谢。
为了配合博客,整了一些小车的视频,百度云上传下载太慢,我直接放在QQ2137418554空间里了(无需加好友,直接看就行)
今天我分享下在GPIO上我的用法。
我在智能小车上采用的一种全新的GPIO组织方式,往常很多项目多是把IO配置散列在功能模块中,用到了就初始化,这样做也没多大问题,就是找问题的时候,要把每个模块的引脚分配和初始化都得查一遍,看看是否有用错PORT,或者用错IO的,如下两个API是很常用的:
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, bsp_GetPinSource(GPIO_Pin_1));
前者配置IO属性(输入/输出,速率、是否上下拉等),
typedef struct
{
uint16_t GPIO_Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode_TypeDef GPIO_Mode;
}GPIO_InitTypeDef;
后者配置外部中断引脚映射。
这2个API里面的4个参数及其结构成员基本涵盖了GPIO的各项属性了,除了重映射之外。
为了避免满篇代码找GPIO映射的尴尬,更因为这份代码不用往别处移植,我就把所有用到的GPIO(其实已经用满了)都组织在一张结构数组的表里。
GPIO_CFG_STRU g_astrGpioDef[] =
{
/***********************************************************
前部电机,WHEEL and MOTO,一个电机需要2路
***********************************************************/
{GPIOC, GPIO_Pin_6, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // front left pwm1, TIM8-CH1
{GPIOC, GPIO_Pin_7, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // front left pwm2, TIM8-CH2
{GPIOC, GPIO_Pin_8, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // front right pwm1, TIM8-CH3
{GPIOC, GPIO_Pin_9, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // front right pwm2, TIM8-CH4
/***********************************************************
前部电机,WHEEL and MOTO,一个电机需要2路
***********************************************************/
{GPIOA, GPIO_Pin_8, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // back left pwm1, TIM1-CH1, CH1N脚PA7与红外周期发射共用
{GPIOA, GPIO_Pin_9, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // back left pwm2, TIM1-CH2 , 与USART1_TX复用
{GPIOA, GPIO_Pin_10, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // back right pwm1, TIM1-CH3, 与USART1_RX复用
{GPIOA, GPIO_Pin_11, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // back right pwm2, TIM1-CH4
/***********************************************************
红外测距与避障,一个通道1个输入,进ADC,引脚mode不同
***********************************************************/
{GPIOC, GPIO_Pin_2, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // left, ADC12
{GPIOC, GPIO_Pin_0, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // middle, ADC10
{GPIOC, GPIO_Pin_3, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // right, ADC13
{GPIOA, GPIO_Pin_5, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // backleft, ADC5
{GPIOA, GPIO_Pin_6, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // backmiddle, ADC6
{GPIOA, GPIO_Pin_1, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // backright, ADC1
/***********************************************************
红外循迹,进通用IO,V12去掉后中位置sensor
***********************************************************/
{GPIOB, GPIO_Pin_0, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // LEFT front, ADC8
{GPIOC, GPIO_Pin_4, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // MIDDLE front, ADC14
{GPIOC, GPIO_Pin_5, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // RIGHT front, ADC15
/***********************************************************
MCU电池供电电压采样和电机电池供电电压供电
***********************************************************/
{GPIOA, GPIO_Pin_4, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // MCU供电电压, ADC4
/***********************************************************
光感检测,一个通道1个输入,进ADC或做普通IO输入,引脚mode不同
***********************************************************/
{GPIOA, GPIO_Pin_0, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // front light sensor, ADC0
/***********************************************************
超声波检测,一个通道1个输入,进中断或者进定时器捕获(本例进中断), 1个输出引脚
***********************************************************/
{GPIOB, GPIO_Pin_1, GPIO_Speed_50MHz, GPIO_Mode_IPD, 0 }, // ECHO 输入
{GPIOB, GPIO_Pin_2, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // trig输出
/***********************************************************
舵机控制, 1个输出引脚
***********************************************************/
{GPIOB, GPIO_Pin_9, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // PWM输出, TIM4_CH4
/***********************************************************
I2C2, LCD/MPU6050,ETC
***********************************************************/
{GPIOB, GPIO_Pin_10, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // SCL
{GPIOB, GPIO_Pin_11, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // SDA
/***********************************************************
SPI2, NRF24L01
***********************************************************/
{GPIOB, GPIO_Pin_12, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // NSS0
{GPIOC, GPIO_Pin_1, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // NSS1
{GPIOB, GPIO_Pin_13, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // SCK
{GPIOB, GPIO_Pin_14, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // MISO
{GPIOB, GPIO_Pin_15, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // MOSI
/***********************************************************
SPI1,
***********************************************************/
{GPIOA, GPIO_Pin_15, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // NSS
{GPIOB, GPIO_Pin_3, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, GPIO_Remap_SPI1 }, // SCK
{GPIOB, GPIO_Pin_4, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, GPIO_Remap_SPI1 }, // MISO
{GPIOB, GPIO_Pin_5, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, GPIO_Remap_SPI1 }, // MOSI
/***********************************************************
WIFI CONTROL, V12 ENABLE不在与SPI1_MISO复用,直接上来
***********************************************************/
//{GPIOB, GPIO_Pin_4, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // v12没有该引脚
{GPIOA, GPIO_Pin_12, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // RESET
/***********************************************************
LED, 头部与尾部车灯
***********************************************************/
{GPIOC, GPIO_Pin_10, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // 左前 led
{GPIOC, GPIO_Pin_11, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // 右前 led
{GPIOC, GPIO_Pin_12, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // 左后 led
{GPIOD, GPIO_Pin_2, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // 右后 led
/***********************************************************
BODY FULL COLOR LED
***********************************************************/
{GPIOC, GPIO_Pin_13, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // RED
{GPIOC, GPIO_Pin_14, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // GREEN
{GPIOC, GPIO_Pin_15, GPIO_Speed_50MHz, GPIO_Mode_Out_PP, 0 }, // BLUE
/***********************************************************
红外周期发射发射控制,VCC接CPU_3V3+, GND接IO引脚
***********************************************************/
{GPIOA, GPIO_Pin_7, GPIO_Speed_50MHz, GPIO_Mode_AIN, 0 }, // PA7, TIM3_CH2
/***********************************************************
红外周期接收,分为左、中、右,用于跟随控制, V12没有单独跟随红外sensor
***********************************************************/
/***********************************************************
红外编码器接收,分为左、右,
***********************************************************/
{GPIOB, GPIO_Pin_6, GPIO_Speed_50MHz, GPIO_Mode_IPU, 0 }, // 左编码器, TIM4_CH1
{GPIOB, GPIO_Pin_7, GPIO_Speed_50MHz, GPIO_Mode_IPU, 0 }, // 右编码器, ITM4_CH2
/***********************************************************
红外接收头,与人体感应复用,与舵机复用,注意修改IO输出特性
***********************************************************/
{GPIOB, GPIO_Pin_8, GPIO_Speed_50MHz, GPIO_Mode_AF_PP, 0 }, // 红外接收头,TIM4_CH3(GPIO_Mode_IN_FLOATING)
};
结构体定义了GPIO的配置属性,以及是否要做某种功能的重映射AFIO
typedef struct
{
GPIO_TypeDef* gpioX;
GPIO_InitTypeDef strGpioType;
uint32_t RemapGpioPin; //是否需要重映射
}GPIO_CFG_STRU;
这样在初始化的时候,写一个函数就够了,如下:
void bsp_gpio_init(void)
{
uint8_t i;
GPIO_CFG_STRU * pstGpioTobeInit;
GPIO_TypeDef * GPIOX;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);
pstGpioTobeInit = g_astrGpioDef;
for(i = 0; i < enGpioMax; i++)
{
GPIO_InitStructure.GPIO_Mode = pstGpioTobeInit->strGpioType.GPIO_Mode;
GPIO_InitStructure.GPIO_Speed = pstGpioTobeInit->strGpioType.GPIO_Speed;
GPIO_InitStructure.GPIO_Pin = pstGpioTobeInit->strGpioType.GPIO_Pin;
GPIOX = pstGpioTobeInit->gpioX;
GPIO_Init(GPIOX, &GPIO_InitStructure);
//需要重映射的引脚在此处配置AFIO
if(g_astrGpioDef->RemapGpioPin)
{
GPIO_PinRemapConfig(g_astrGpioDef->RemapGpioPin, ENABLE);
}
if((pstGpioTobeInit->strGpioType.GPIO_Mode == GPIO_Mode_Out_PP)
|| (pstGpioTobeInit->strGpioType.GPIO_Mode == GPIO_Mode_AF_PP))
GPIOX->BRR = pstGpioTobeInit->strGpioType.GPIO_Pin;
pstGpioTobeInit++;
}
HW_Set_Gpio_OutputValue(enGpioWifiReset, ENABLE);
}
有些人看到了里面有这么一句
HW_Set_Gpio_OutputValue(enGpioWifiReset, ENABLE);
这是把输入和输出读取和写入操作又封装了一层。
uint8_t HW_Get_Gpio_InputValue(GPIO_FUCNTIONALITY_ENUM enSeqNum)
{
return(GPIO_ReadInputDataBit(g_astrGpioDef[enSeqNum].gpioX, g_astrGpioDef[enSeqNum].strGpioType.GPIO_Pin));
}
uint8_t HW_Get_Gpio_OutputValue(GPIO_FUCNTIONALITY_ENUM enSeqNum)
{
return(GPIO_ReadOutputDataBit(g_astrGpioDef[enSeqNum].gpioX, g_astrGpioDef[enSeqNum].strGpioType.GPIO_Pin));
}
void HW_Set_Gpio_OutputValue(GPIO_FUCNTIONALITY_ENUM enSeqNum, uint8_t ucEnable)
{
//输出高
if(ucEnable == ENABLE)
{
g_astrGpioDef[enSeqNum].gpioX->BSRR = g_astrGpioDef[enSeqNum].strGpioType.GPIO_Pin;
}
//输出低
else if(ucEnable == DISABLE)
{
g_astrGpioDef[enSeqNum].gpioX->BRR = g_astrGpioDef[enSeqNum].strGpioType.GPIO_Pin;
}
}
有人说,这样做效率有点低,是的,我承认,但是代码里真正需要直接操作GPIO的地方是不多的,我忍了。在需要考虑性能的地方单独换成更高效的处理方式就行,比如I2C驱动,就是从某个开发板的down的吧(具体时间长了,我也记不到了)。
拉回来,说到最后了,上面设置和查询GPIO状态的枚举值,要跟之前定义的结构数组顺便必须保持一致:
typedef enum
{
enGpioFrontMotoLeftIn1,
enGpioFrontMotoLeftIn2,
enGpioFrontMotoRightIn1,
enGpioFrontMotoRightIn2,
enGpioBackMotoLeftIn1,
enGpioBackMotoLeftIn2,
enGpioBackMotoRightIn1,
enGpioBackMotoRightIn2,
enGpioINFObsLeft,
enGpioINFObsMiddle,
enGpioINFObsRight,
enGpioINFObsBackLeft,
enGpioINFObsBackMiddle,
enGpioINFObsBackRight,
enGpioINFTraceLeftFront,
enGpioINFTraceMiddleFront,
enGpioINFTraceRightFront,
//enGpioBuzzOut,
enGpioPwrMonitor,
enGpioLightFront,
enGpioUltraSonicEcho,
enGpioUltraSonicTrig,
enGpioServoPwmOut,//舵机
enGpioI2C2Scl,
enGpioI2C2Sda,
enGpioSPI2Cs0,
enGpioSPI2Cs1,
enGpioSPI2Sck,
enGpioSPI2Miso,
enGpioSPI2Mosi,
enGpioSPI1Cs0,
enGpioSPI1Sck,
enGpioSPI1Miso,
enGpioSPI1Mosi,
enGpioWifiReset,
enGpioLeftFrontLed,
enGpioRightFrontLed,
enGpioLeftBackLed,
enGpioRightBackLed,
//RGB全彩灯
enGpioBodyLedRed,
enGpioBodyLedGreen,
enGpioBodyLedBlue,
enGpioInfPeriodTxOut,
enGpioEncoderLeft,
enGpioEncoderRight,
enGpioInfReceiver,
enGpioMax
}GPIO_FUCNTIONALITY_ENUM;
讲到这里GPIO就结束了,代码比较简单,不需要那么多注释。
有用的大家借鉴下,认为没啥意义或者你已经这么做过了,鼓励我一下就行,砖头咱们就省了:)
谢谢