HC32F4A0-Keil环境搭建与点灯 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
HC32F4A0-串口中断接收与中断发送 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
上篇讲了串口及其中断的使用,本篇主要是使用芯片内部I2C控制器对TCA9539这个IO扩展芯片进行控制,使LED点亮。也方便下篇点亮LCD液晶和触摸屏做准备,液晶屏的复位管脚和触摸的中断管脚使用了TCA9539扩展出来的IO。
以下是板载TCA9539的硬件原理图和TCA9539地址确认图:
从上图可知,TCA9539的A0、A1管脚接地,TCA9539的第3至第7位是固定值,第0位位读写控制位,只有第1、2位可有硬件设置,由此可知TCA9539的从机地址位0x74。且TCA9539在一条I2C总线上最多只能由4片。
以下是HC32F4A0的时序图:
从该时序图中可知,通过开始条件、地址传送、数据传送、停止条件这几个步骤,即可完成一次正常通信,TCA9539的驱动代码也是严格按照这个顺序进行的。以下是TCA9539的读写时序图;对比代码和时序图,可以帮我们更好的理解HC32F4A0的I2C的使用。
以下是I2C的配置代码:
#define TCA9539_I2C_FCG_ENABLE() FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_I2C1, ENABLE )
#define TCA9539_I2C_SCL_PIN GPIO_PIN_03
#define TCA9539_I2C_SCL_PORT GPIO_PORT_D
#define TCA9539_I2C_SDA_PIN GPIO_PIN_10
#define TCA9539_I2C_SDA_PORT GPIO_PORT_F
#define TCA9539_INT_PIN GPIO_PIN_01
#define TCA9539_INT_PORT GPIO_PORT_I
#define TCA9539_RST_PIN GPIO_PIN_13
#define TCA9539_RST_PORT GPIO_PORT_C
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] bsp_TCA9539_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-02-19
**************************************************************/
void bsp_TCA9539_Init( void )
{
float32_t fErr;
int32_t i32Ret = LL_ERR;
stc_gpio_init_t GPIO_InitStruct = { 0 };
stc_i2c_init_t I2C_InitStruct = { 0 };
LL_PERIPH_WE( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
GPIO_ResetPins( TCA9539_RST_PORT, TCA9539_RST_PIN );
GPIO_StructInit( &GPIO_InitStruct );
GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
GPIO_Init( TCA9539_RST_PORT, TCA9539_RST_PIN, &GPIO_InitStruct );
delay_ms( 5 );
GPIO_SetPins( TCA9539_RST_PORT, TCA9539_RST_PIN );
GPIO_StructInit( &GPIO_InitStruct );
GPIO_Init( TCA9539_I2C_SCL_PORT, TCA9539_I2C_SCL_PIN, &GPIO_InitStruct );
GPIO_Init( TCA9539_I2C_SDA_PORT, TCA9539_I2C_SDA_PIN, &GPIO_InitStruct );
GPIO_SetFunc( TCA9539_I2C_SCL_PORT, TCA9539_I2C_SCL_PIN, GPIO_FUNC_49 );
GPIO_SetFunc( TCA9539_I2C_SDA_PORT, TCA9539_I2C_SDA_PIN, GPIO_FUNC_48 );
TCA9539_I2C_FCG_ENABLE();
I2C_InitStruct.u32Baudrate = 100000UL;
I2C_InitStruct.u32ClockDiv = I2C_CLK_DIV16;
I2C_InitStruct.u32SclTime = 0UL;
for ( uint8_t i = 0U; i < 5U; i++ )
{
i32Ret = I2C_Init( TCA9539_I2Cx, &I2C_InitStruct, &fErr );
if ( LL_OK != i32Ret )
{
I2C_InitStruct.u32ClockDiv--;
}
else
{
break;
}
}
if ( LL_OK == i32Ret )
{
I2C_BusWaitCmd( TCA9539_I2Cx, ENABLE );
}
I2C_Cmd( TCA9539_I2Cx, ENABLE );
LL_PERIPH_WP( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
}
以下是使用I2C读写TCA9539的代码,根据代码联系上述读写时序图,可以清楚的理解该外设的使用,代码均有注释,可以看出是一步步按照时序图走的。
/**************************************************************
* @Name TCA9539_I2C_Write
* @brief
* @param u16DevAddr: [输入/出]
** pu8Reg: [输入/出]
** u8RegLen: [输入/出]
** pu8Buf: [输入/出]
** u32Len: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_I2C_Write( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, const uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( TCA9539_I2Cx, ENABLE );
I2C_SWResetCmd( TCA9539_I2Cx, DISABLE );
i32Ret = I2C_Start( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---起始信号--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT ); /* ---TCA9539寄存器地址传送--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT ); /* ---数据传送--- */
}
}
}
I2C_Stop( TCA9539_I2Cx, I2C_TIMEOUT );
}
/**************************************************************
* @Name TCA9539_I2C_Read
* @brief
* @param u16DevAddr: [输入/出]
** pu8Reg: [输入/出]
** u8RegLen: [输入/出]
** pu8Buf: [输入/出]
** u32Len: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_I2C_Read( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( TCA9539_I2Cx, ENABLE );
I2C_SWResetCmd( TCA9539_I2Cx, DISABLE );
i32Ret = I2C_Start( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---起始信号--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT ); /* ---TCA9539寄存器地址传送--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_Restart( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---重新开始--- */
if ( LL_OK == i32Ret )
{
if ( 1UL == u32Len )
{
I2C_AckConfig( TCA9539_I2Cx, I2C_NACK );
}
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_RX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_MasterReceiveDataAndStop( TCA9539_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT ); /* ---开始数据接收--- */
}
I2C_AckConfig( TCA9539_I2Cx, I2C_ACK );
}
}
}
}
if ( LL_OK != i32Ret )
{
( void )I2C_Stop( TCA9539_I2Cx, I2C_TIMEOUT );
}
}
以下是TCA9539的IO配置代码和IO口读写代码,可以将IO口配置位输出或输入:
/**************************************************************
* @Name TCA9539_WritePin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** u8PinState: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_WritePin( uint8_t u8Port, uint8_t u8Pin, uint8_t u8PinState )
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_OUTPUT_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if ( 0U == u8PinState )
{
u8TempData[1] &= ( uint8_t )( ~u8Pin );
}
else
{
u8TempData[1] |= u8Pin;
}
TCA9539_I2C_Write( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
}
/**************************************************************
* @Name TCA9539_ReadPin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** pu8PinState: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_ReadPin( uint8_t u8Port, uint8_t u8Pin, uint8_t *pu8PinState )
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_INPUT_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if (0U != (u8TempData[1] & u8Pin))
{
*pu8PinState = 0x01U;
}
else
{
*pu8PinState = 0x00U;
}
}
/**************************************************************
* @Name TCA9539_ConfigPin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** u8Dir: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_ConfigPin( uint8_t u8Port, uint8_t u8Pin, uint8_t u8Dir)
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_CONFIG_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if ( TCA9539_DIR_OUT == u8Dir )
{
u8TempData[1] &= (uint8_t)(~u8Pin);
}
else
{
u8TempData[1] |= u8Pin;
}
TCA9539_I2C_Write( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
}
以下是main.c测试代码,我们将板载LED2所对应的管脚配置为输出,并且在主循环中每500ms反转一次。
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
}
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-02-13
**************************************************************/
int32_t main( void )
{
SysInit();
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
以下是测试视频:
a95b6c7e9ff3fe2f14261b318f0e9f7d
以下是代码附件:
|