TCA9548A的用途就是IIC扩展,每个TCA9548A可以扩展出8路IIC。TCA9548A芯片带有地址选择引脚A0/A1/A2,根据高低电平不同,从MCU的一路IIC最多可以接入8个TCA9548A芯片,从而可以达到扩展出8*8=64路IIC的效果。
在什么情况下会使用到TCA9548A芯片来扩展?当一个MCU想要驱动多个器件地址相同的芯片时,如驱动8个OLED时,OLED的IIC器件地址为0x78,要用MCU引出8路IIC的硬件线路?显然得不偿失,这时候用TCA9548A就再合适不过了。
下面是我使用STM32F1通过一个TCA9548A驱动8个OLED的示例描述
STM32和TCA9548A配合使用需要注意的问题:
1)TCA9548A硬件电路的连接
STM32和TCA9548A连线的上拉电阻我使用的4.7K。
2)STM32按照上述电路图后的代码配置
void i2c_CfgGpio(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(OLED_RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Pin = OLED_I2C_SCL_PIN | OLED_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(OLED_GPIO_PORT_I2C, &GPIO_InitStructure);
i2c_Stop();
}
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; 这句代码的配置很重要,模式配置的不对,很有可能不好用。
下面是关键部分测试代码:
bsp_oled.c
#ifndef __BSP_OLED_H
#define __BSP_OLED_H
#include "bsp_oled.h"
#endif
#ifndef __CODETAB_H
#define __CODETAB_H
#include "codetab.h"
#endif
void i2c_CfgGpio(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(OLED_RCC_I2C_PORT, ENABLE);
GPIO_InitStructure.GPIO_Pin = OLED_I2C_SCL_PIN | OLED_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(OLED_GPIO_PORT_I2C, &GPIO_InitStructure);
i2c_Stop();
}
static void i2c_Delay(void)
{
uint8_t i;
for (i = 0; i < 10; i++);
}
void i2c_Start(void)
{
OLED_I2C_SDA_1();
OLED_I2C_SCL_1();
i2c_Delay();
OLED_I2C_SDA_0();
i2c_Delay();
OLED_I2C_SCL_0();
i2c_Delay();
}
void i2c_Stop(void)
{
OLED_I2C_SDA_0();
OLED_I2C_SCL_1();
i2c_Delay();
OLED_I2C_SDA_1();
}
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
OLED_I2C_SDA_1();
}
else
{
OLED_I2C_SDA_0();
}
i2c_Delay();
OLED_I2C_SCL_1();
i2c_Delay();
OLED_I2C_SCL_0();
if (i == 7)
{
OLED_I2C_SDA_1(); // ÊÍ·Å×ÜÏß
}
_ucByte <<= 1;
i2c_Delay();
}
}
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
OLED_I2C_SCL_1();
i2c_Delay();
if (OLED_I2C_SDA_READ())
{
value++;
}
OLED_I2C_SCL_0();
i2c_Delay();
}
return value;
}
uint8_t i2c_WaitAck(void)
{
uint8_t re;
OLED_I2C_SDA_1();
i2c_Delay();
OLED_I2C_SCL_1();
i2c_Delay();
if (OLED_I2C_SDA_READ())
{
re = 1;
}
else
{
re = 0;
}
OLED_I2C_SCL_0();
i2c_Delay();
return re;
}
void i2c_Ack(void)
{
OLED_I2C_SDA_0();
i2c_Delay();
OLED_I2C_SCL_1();
i2c_Delay();
OLED_I2C_SCL_0();
i2c_Delay();
OLED_I2C_SDA_1();
}
void i2c_NAck(void)
{
OLED_I2C_SDA_1(); /* CPUÇý¶¯SDA = 1 */
i2c_Delay();
OLED_I2C_SCL_1(); /* CPU²úÉú1¸öʱÖÓ */
i2c_Delay();
OLED_I2C_SCL_0();
i2c_Delay();
}
void func_tca9548a_select(u8 chn)//0~7
{
u8 ADDR_TCA9548A = 0x70;
i2c_Start();
i2c_SendByte((ADDR_TCA9548A << 1) | OLED_I2C_WR);
// i2c_SendByte((ADDR_TCA9548A) | OLED_I2C_WR);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
i2c_SendByte(1 << chn);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
i2c_Stop();
cmd_fail:
i2c_Stop();
}
u8 func_tca9548a_read_ch(void)
{
u8 res;
u8 ADDR_TCA9548A = 0x70;
i2c_Start();
i2c_SendByte((ADDR_TCA9548A << 1) | OLED_I2C_WR);
// i2c_SendByte((ADDR_TCA9548A) | OLED_I2C_WR);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
res = i2c_ReadByte();
i2c_NAck();
i2c_Stop();
cmd_fail:
i2c_Stop();
return res;
}
uint8_t OLED_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
i2c_Start(); /* ·¢ËÍÆô¶¯ÐźŠ*/
i2c_SendByte(_Address|OLED_I2C_WR);/* ·¢ËÍÉ豸µØÖ· */
ucAck = i2c_WaitAck(); /* ¼ì²âÉ豸µÄACKÓ¦´ð */
i2c_Stop(); /* ·¢ËÍÍ£Ö¹ÐźŠ*/
return ucAck;
}
void I2C_WriteByte(uint8_t addr,uint8_t data){
i2c_Start();
i2c_SendByte(OLED_ADDRESS|OLED_I2C_WR);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
i2c_SendByte(addr);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
i2c_SendByte(data);
if (i2c_WaitAck() != 0)
{
goto cmd_fail;
}
i2c_Stop();
cmd_fail:
i2c_Stop();
}
void WriteCmd(unsigned char I2C_Command)//дÃüÁî
{
I2C_WriteByte(0x00, I2C_Command);
}
void WriteDat(unsigned char I2C_Data)//дÊý¾Ý
{
I2C_WriteByte(0x40, I2C_Data);
}
void OLED_Init(void)
{
delay_ms(10);
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //ÁÁ¶Èµ÷½Ú 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
OLED_CLS();
}
void OLED_SetPos(unsigned char x, unsigned char y)
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
void OLED_Fill(unsigned char fill_Data)//È«ÆÁÌî³ä
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<130;n++)
{
WriteDat(fill_Data);
}
}
}
void OLED_CLS(void)//ÇåÆÁ
{
OLED_Fill(0x00);
}
void OLED_ON(void)
{
WriteCmd(0X8D); //ÉèÖõçºÉ±Ã
WriteCmd(0X14); //¿ªÆôµçºÉ±Ã
WriteCmd(0XAF); //OLED»½ÐÑ
}
void OLED_OFF(void)
{
WriteCmd(0X8D); //ÉèÖõçºÉ±Ã
WriteCmd(0X10); //¹Ø±ÕµçºÉ±Ã
WriteCmd(0XAE); //OLEDÐÝÃß
}
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
unsigned char c = 0,i = 0,j = 0,k = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
case 3:
{
while(ch[j] != '\0')
{
c = ch[j] - 48;
if(x > 120)
{
x = 0;
y++;
}
for(k=0;k<4;k++)
{
OLED_SetPos(x,y+k);
for(i=0;i<8;i++)
WriteDat(F16X32[c*16*4+k*16+i]);
OLED_SetPos(x+8,y+k);
for(i=0;i<8;i++)
WriteDat(F16X32[c*16*4+k*16+8+i]);
}
x += 16;
j++;
}
case 4:
{
while(ch[j] != '\0')
{
c = ch[j];
if(c >= '0')
{
c -= '0';
}
else
{
c = 10;
}
if(x > 120)
{
x = 0;
y++;
}
for(k=0;k<6;k++)
{
OLED_SetPos(x,y+k);
for(i=0;i<8;i++)
WriteDat(F24X48[c*16*9+k*24+i]);
OLED_SetPos(x+8,y+k);
for(i=0;i<8;i++)
WriteDat(F24X48[c*16*9+k*24+8+i]);
OLED_SetPos(x+16,y+k);
for(i=0;i<8;i++)
WriteDat(F24X48[c*16*9+k*24+16+i]);
}
x += 24;
j++;
}
}
}
}
}
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
WriteDat(F16x16[adder]);
adder += 1;
}
}
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}
uint8_t OLED_Test(void)
{
if (OLED_CheckDevice(OLED_ADDRESS) == 1)
{
return 0;
}
else
{
return 1;
}
}
void OLED_ShowTest(void)
{
unsigned char i;
if(OLED_Test()==0)
{
i = 1;
}
else
{
i = 2;
}
// while(1)
// {
OLED_Fill(0xFF);
delay_s(1);
OLED_Fill(0x00);
delay_s(1);
for(i=0;i<4;i++)
{
OLED_ShowCN(22+i*16,0,i);
}
delay_s(1);
OLED_ShowStr(0,3,(unsigned char*)"Wildfire Tech",1);
OLED_ShowStr(0,4,(unsigned char*)"Hello wildfire",2);
delay_s(2);
OLED_CLS();
OLED_OFF();
delay_s(1);
OLED_ON();
OLED_DrawBMP(0,0,128,8,(unsigned char *)BMP1);
delay_s(1);
// }
}
main.c
#ifndef __STM32F10X_H
#define __STM32F10X_H
#include "stm32f10x.h"
#endif
#ifndef __BSP_BEEP_H
#define __BSP_BEEP_H
#include "bsp_beep.h"
#endif
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "bsp_led.h"
#endif
#ifndef __BSP_OLED_H
#define __BSP_OLED_H
#include "bsp_oled.h"
#endif
int main()
{
uint32_t tmp;
init_beep();
init_led();
i2c_CfgGpio();
func_led2_on();
func_led3_on();
func_led4_on();
func_beep_play();
for (tmp = 0; tmp < 3000000; tmp++);
func_beep_stop();
for (tmp = 0; tmp < 3000000; tmp++);
func_tca9548a_select(0);//0~7
OLED_Init();
func_tca9548a_select(1);//0~7
OLED_Init();
func_tca9548a_select(2);//0~7
OLED_Init();
func_tca9548a_select(3);//0~7
OLED_Init();
func_tca9548a_select(4);//0~7
OLED_Init();
func_tca9548a_select(5);//0~7
OLED_Init();
func_tca9548a_select(6);//0~7
OLED_Init();
func_tca9548a_select(7);//0~7
OLED_Init();
for(;;)
{
func_tca9548a_select(0);
OLED_ShowTest();
func_tca9548a_select(1);
OLED_ShowTest();
func_tca9548a_select(2);
OLED_ShowTest();
func_tca9548a_select(3);
OLED_ShowTest();
func_tca9548a_select(4);
OLED_ShowTest();
func_tca9548a_select(5);
OLED_ShowTest();
func_tca9548a_select(6);
OLED_ShowTest();
func_tca9548a_select(7);
OLED_ShowTest();
}
}
注意,当STM32选择了TCA9548A的8个通道中的某一个后,TCA9548A就会保持这个通道,并透传IIC的消息,就可以直接向操作OLED一样发数据了。那么怎么选择TCA9548A的某一个通道呢?TCA9548A内部只有一个8位寄存器,置高的通道被选中。
最后就是效果了