在HC32F4A0开发板上有3个LED灯是无法直接通过GPIO口来控制的,因为它们被接到了芯片TCA9539之下,见图1所示。
而该芯片是一款以I2C方式工作的芯片,它可控制扩展出的8个I/O口,3个LED灯就连接在它的I/O口上。
图1 TCA9539电路
图2 LED电路
此外,该芯片又是通过I2C接口来共享该总线的,它配分配的地址为1110100,并由如下语句来定义:
#define BSP_TCA9539_DEV_ADDR (0x74U)
图3 I2C接口电路
在该I2C接口上,除连接TCA9539来进行I/O扩展外,还连接着AUDIO音频电路、EEPROM存储器、CAMERA摄像头电路等,并分配了相应的地址以便区分和控制。
图4 EEPROM电路
TCA9539芯片与HC32F4A0的连接关系为:
SCL---PD3
SDA---PF10
RST---PC13
INT---PI1
为此,对所用引脚的语句定义为:
#define EIO_RST_PORT (GPIO_PORT_C)
#define EIO_RST_PIN (GPIO_PIN_13)
#define BSP_TCA9539_I2C_SCL_PORT (GPIO_PORT_D)
#define BSP_TCA9539_I2C_SCL_PIN (GPIO_PIN_03)
#define BSP_TCA9539_I2C_SDA_PORT (GPIO_PORT_F)
#define BSP_TCA9539_I2C_SDA_PIN (GPIO_PIN_10)
#define BSP_TCA9539_I2C_SCL_FUNC (GPIO_FUNC_49)
#define BSP_TCA9539_I2C_SDA_FUNC (GPIO_FUNC_48)
相应的其初始化函数为:
- static void BSP_TCA9539_I2C_Init(void)
- {
- stc_gpio_init_t stcGpioInit;
-
- (void)GPIO_StructInit(&stcGpioInit);
- (void)GPIO_Init(BSP_TCA9539_I2C_SCL_PORT, BSP_TCA9539_I2C_SCL_PIN, &stcGpioInit);
- (void)GPIO_Init(BSP_TCA9539_I2C_SDA_PORT, BSP_TCA9539_I2C_SDA_PIN, &stcGpioInit);
- GPIO_SetFunc(BSP_TCA9539_I2C_SCL_PORT, BSP_TCA9539_I2C_SCL_PIN, BSP_TCA9539_I2C_SCL_FUNC);
- GPIO_SetFunc(BSP_TCA9539_I2C_SDA_PORT, BSP_TCA9539_I2C_SDA_PIN, BSP_TCA9539_I2C_SDA_FUNC);
-
- FCG_Fcg1PeriphClockCmd(BSP_TCA9539_I2C_FCG, ENABLE);
- (void)BSP_I2C_Init(BSP_TCA9539_I2C_UNIT);
- }
-
TCA9539对输出引脚进行写操作的函数为:
- int32_t TCA9539_WritePin(const stc_tca9539_ll_t *pstcTca9539LL, uint8_t u8Port, uint8_t u8Pin, uint8_t u8PinState)
- {
- int32_t i32Ret = LL_OK;
- uint8_t u8TempData[2];
- if (pstcTca9539LL == NULL) {
- i32Ret = LL_ERR_INVD_PARAM;
- } else {
- u8TempData[0] = u8Port + TCA9539_REG_OUTPUT_PORT0;
- pstcTca9539LL->Read(&u8TempData[0], &u8TempData[1], 1U);
- if (0U == u8PinState) {
- u8TempData[1] &= (uint8_t)(~u8Pin);
- } else {
- u8TempData[1] |= u8Pin;
- }
- pstcTca9539LL->Write(&u8TempData[0], &u8TempData[1], 1U);
- }
- return i32Ret;
- }
-
那TCA9539又是如何来控制扩展I/O口的呢?
它是通过先定义各I/O口的作用,然后再进行使用的,它对各I/O口的定义如下:
#define EIO_USBFS_OC (TCA9539_IO_PIN0) /* USBFS over-current, input */
#define EIO_USBHS_OC (TCA9539_IO_PIN1) /* USBHS over-current, input */
#define EIO_SDIC1_CD (TCA9539_IO_PIN2) /* SDIC1 card detect, input */
#define EIO_SCI_CD (TCA9539_IO_PIN3) /* Smart card detect, input */
#define EIO_TOUCH_INT (TCA9539_IO_PIN4) /* Touch screen interrupt, input */
#define EIO_LIN_SLEEP (TCA9539_IO_PIN5) /* LIN PHY sleep, output */
#define EIO_RTCS_CTRST (TCA9539_IO_PIN6)
/* 'CS' for Resistor touch panel or 'Reset' for Cap touch panel, output */
#define EIO_LCD_RST (TCA9539_IO_PIN7) /* LCD panel reset, output */
#define EIO_CAM_RST (TCA9539_IO_PIN0) /* Camera module reset, output */
#define EIO_CAM_STB (TCA9539_IO_PIN1) /* Camera module standby, output */
#define EIO_USB3300_RST (TCA9539_IO_PIN2) /* USBHS PHY USB3300 reset, output */
#define EIO_ETH_RST (TCA9539_IO_PIN3) /* ETH PHY reset, output */
#define EIO_CAN_STB (TCA9539_IO_PIN4) /* CAN PHY standby, output */
#define EIO_LED_RED (TCA9539_IO_PIN5) /* Red LED, output */
#define EIO_LED_YELLOW (TCA9539_IO_PIN6) /* Yellow LED, output */
#define EIO_LED_BLUE (TCA9539_IO_PIN7) /* Blue LED, output */
这其中的EIO_LED_RED、EIO_LED_YELLOW及EIO_LED_BLUE就是那3个不能直接进行控制的LED。
而这些I/O口,又具体对应着控制字的某一位,这由下面的定义可以看出:
#define TCA9539_IO_PIN0 (0x01U)
#define TCA9539_IO_PIN1 (0x02U)
#define TCA9539_IO_PIN2 (0x04U)
#define TCA9539_IO_PIN3 (0x08U)
#define TCA9539_IO_PIN4 (0x10U)
#define TCA9539_IO_PIN5 (0x20U)
#define TCA9539_IO_PIN6 (0x40U)
#define TCA9539_IO_PIN7 (0x80U)
#define TCA9539_IO_PIN_ALL (0xFFU)
在建立了以上控制关系后,就可以对相应的I/O口进行功能配置了,所用的初始化函数为:
- void BSP_IO_Init(void)
- {
-
- stcTca9539Config.Init = BSP_TCA9539_I2C_Init;
- stcTca9539Config.Write = BSP_TCA9539_I2C_Write;
- stcTca9539Config.Read = BSP_TCA9539_I2C_Read;
- stcTca9539Config.Reset = BSP_TCA9539_Reset;
- stcTca9539Config.IntInit = NULL;
-
- (void)TCA9539_Init(&stcTca9539Config);
- }
-
以TCA9539实现位写入操作的函数为:
void BSP_IO_WritePortPin(uint8_t u8Port, uint8_t u8Pin, uint8_t u8PinState)
{
(void)TCA9539_WritePin(&stcTca9539Config, u8Port, u8Pin, u8PinState);
}
以TCA9539实现位异或操作的函数为:
void BSP_IO_TogglePortPin(uint8_t u8Port, uint8_t u8Pin)
{
(void)TCA9539_TogglePin(&stcTca9539Config, u8Port, u8Pin);
}
通过上面这2个函数,就为后续的LED控制提供了基础性的支持。
为控制这3个LED灯,所做的端口和引脚定义为:
#define EIO_LED_RED (TCA9539_IO_PIN5) /* Red LED, output */
#define EIO_LED_YELLOW (TCA9539_IO_PIN6) /* Yellow LED, output */
#define EIO_LED_BLUE (TCA9539_IO_PIN7) /* Blue LED, output */
#define LED_PORT (EIO_PORT1)
#define LED_RED_PORT (EIO_PORT1)
#define LED_RED_PIN (EIO_LED_RED)
#define LED_YELLOW_PORT (EIO_PORT1)
#define LED_YELLOW_PIN (EIO_LED_YELLOW)
#define LED_BLUE_PORT (EIO_PORT1)
#define LED_BLUE_PIN (EIO_LED_BLUE)
在该定义的情况下,实现LED灯初始化的函数为:
- void BSP_LED_Init(void)
- {
-
- BSP_IO_WritePortPin(LED_PORT, (LED_RED_PIN | LED_YELLOW_PIN | LED_BLUE_PIN), LED_OFF);
-
- BSP_IO_ConfigPortPin(LED_PORT, (LED_RED_PIN | LED_YELLOW_PIN | LED_BLUE_PIN), EIO_DIR_OUT);
- }
-
其中的EIO_DIR_OUT是指定所用的I/O口做输出口使用,LED_OFF则指定对应的I/O口输出低电平。
具体的LED控制可通过下面3个函数的调用来实现,其内容如下:
- void BSP_LED_On(uint8_t u8Led)
- {
- BSP_IO_WritePortPin(LED_PORT, u8Led, LED_ON);
- }
-
- void BSP_LED_Off(uint8_t u8Led)
- {
- BSP_IO_WritePortPin(LED_PORT, u8Led, LED_OFF);
- }
-
- void BSP_LED_Toggle(uint8_t u8Led)
- {
- BSP_IO_TogglePortPin(LED_PORT, u8Led);
- }
-
由此可见使用I2C接口的芯片进行I/O口扩展还是有代价的,会在一定程度上增加使用的复杂度。