目录
1.背景
IIC作为低速环境下使用非常广泛的协议,还是要学一学滴。TI的IIC是硬件(看什么看,说的就是你ST,这么大一个厂一个硬件IIC做的还有bug)这两年TI还是不错的,以前TI做的IIC还是专属,鬼知道怎么搞,非常难调。不过这两年TI貌似光明正大地说起来了(难道是NXP开发出来I3C后对I2C管控放松了?不应该吧)虽说与正点原子写的软件IIC还是有区别和不理解,但是最终还是调出来了,回过头来看程序还算能理解吧。
2.TI的IIC的特别
如果各位写过模拟IIC再看TI的IIC会有一种特别的感觉。
首先的话就是各个通信模块都有FIFO和中断(ST:?)
其次IIC有占空比33%和55%两种模式,老实说以前写模拟IIC的时候还真没考虑过这个问题
至此TI的IIC的特别点我都介绍过了,下面我们就正式开始写程序吧。
3.程序
//
// initI2C - Function to configure I2C A in FIFO mode.
//
void initI2C1(void)
{
//
// Must put I2C into reset before configuring it
//
I2C_disableModule(I2CA_BASE);
//
// I2C configuration. Use a 400kHz I2CCLK with a 33% duty cycle.
//
I2C_initMaster(I2CA_BASE, DEVICE_SYSCLK_FREQ, 1000, I2C_DUTYCYCLE_33);
I2C_setBitCount(I2CA_BASE, I2C_BITCOUNT_8);
I2C_setSlaveAddress(I2CA_BASE, 0x50);
I2C_setEmulationMode(I2CA_BASE, I2C_EMULATION_FREE_RUN);
//
// Enable stop condition and register-access-ready interrupts
//
I2C_enableInterrupt(I2CA_BASE, I2C_INT_STOP_CONDITION |
I2C_INT_REG_ACCESS_RDY);
//
// FIFO configuration
//
I2C_enableFIFO(I2CA_BASE);
I2C_clearInterruptStatus(I2CA_BASE, I2C_INT_RXFF | I2C_INT_TXFF);
//
// Configuration complete. Enable the module.
//
I2C_enableModule(I2CA_BASE);
GPIO_setPinConfig(GPIO_32_SDAA);
GPIO_setPadConfig(32, GPIO_PIN_TYPE_PULLUP);
GPIO_setQualificationMode(32, GPIO_QUAL_ASYNC);
GPIO_setPinConfig(GPIO_33_SCLA);
GPIO_setPadConfig(33, GPIO_PIN_TYPE_PULLUP);
GPIO_setQualificationMode(33, GPIO_QUAL_ASYNC);
//
// Interrupts that are used in this example are re-mapped to ISR functions
// found within this file.
//
Interrupt_register(INT_I2CA, &i2cAISR);
}
上面的是IIC初始化,其中比较重要的有三句话
I2C_initMaster(I2CA_BASE, DEVICE_SYSCLK_FREQ, 10000, I2C_DUTYCYCLE_50);
I2C_setBitCount(I2CA_BASE, I2C_BITCOUNT_8);
I2C_setSlaveAddress(I2CA_BASE, 0x50);
I2C_setEmulationMode(I2CA_BASE, I2C_EMULATION_FREE_RUN);
第一个函数的第三个参数是通信速率,第四个参数占空比
第二个函数的第二个参数是位数
第三个函数的第二个参数是从机的地址(其实设置没什么影响)
第四个函数的第二个参数是设置仿真模式,我推荐各位使用freerun模式,对应的是我们正常使用的模式,我没试过其他模式
//
// writeData - Function to send the data that is to be written to the EEPROM
//
void I2cWriteData1(unsigned int i2c1data)
{
//
// Wait until the STP bit is cleared from any previous master
// communication. Clearing of this bit by the module is delayed until after
// the SCD bit is set. If this bit is not checked prior to initiating a new
// message, the I2C could get confused.
//
if(I2C_getStopConditionStatus(I2CA_BASE))
{
return;
}
//
// Setup slave address
//
I2C_setSlaveAddress(I2CA_BASE, 0x78);
//
// Check if bus busy
//
if(I2C_isBusBusy(I2CA_BASE))
{
return;
}
//
// Setup number of bytes to send msgBuffer and address
//
I2C_setDataCount(I2CA_BASE, 1);
//
// Setup data to send
//
I2C_putData(I2CA_BASE, i2c1data);
//
// Send start as master transmitter
//
I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);
I2C_sendStartCondition(I2CA_BASE);
I2C_sendStopCondition(I2CA_BASE);
}
上面的程序就是发送一个数据的程序,其中最为注意的是三个地方
I2C_setSlaveAddress(I2CA_BASE, 0x78);
这个函数的第二个参数就是从机的地址,每次发送是依照这里的地址而非初始化的地址
//
// Setup number of bytes to send msgBuffer and address
//
I2C_setDataCount(I2CA_BASE, 1);
//
// Setup data to send
//
I2C_putData(I2CA_BASE, i2c1data);
这是发送一个数据的程序,注意第一个函数的第二个参数因该与下面发送的个数匹配,否则芯片会认为发送任务没有完成而一直等待从而死机。
假如我要一次性发送两个数据呢?比如0.96寸的oled
void OLED4WriteCmd(unsigned char I2C_Command)//写命令
{
//
// Wait until the STP bit is cleared from any previous master
// communication. Clearing of this bit by the module is delayed until after
// the SCD bit is set. If this bit is not checked prior to initiating a new
// message, the I2C could get confused.
//
if(I2C_getStopConditionStatus(I2CA_BASE))
{
return;
}
//
// Setup slave address
//
I2C_setSlaveAddress(I2CA_BASE, 0x78);
//
// Check if bus busy
//
if(I2C_isBusBusy(I2CA_BASE))
{
return;
}
//
// Setup number of bytes to send msgBuffer and address
//
I2C_setDataCount(I2CA_BASE, 2);
//
// Setup data to send
//
I2C_putData(I2CA_BASE, 0x00);
I2C_putData(I2CA_BASE, I2C_Command);
//
// Send start as master transmitter
//
I2C_setConfig(I2CA_BASE, I2C_MASTER_SEND_MODE);
I2C_sendStartCondition(I2CA_BASE);
I2C_sendStopCondition(I2CA_BASE);
}
注意看其中的区别
//
// Setup number of bytes to send msgBuffer and address
//
I2C_setDataCount(I2CA_BASE, 2);
//
// Setup data to send
//
I2C_putData(I2CA_BASE, 0x00);
I2C_putData(I2CA_BASE, I2C_Command);
可以对比发送一个字节,相信大家能理解,如果还要发送更多以此类推就行,TI给的例程中EEPROM就是一次发送10个字节,大家再看看肯定能理解了
//
// i2cAISR - I2C A ISR (non-FIFO)
//
__interrupt void
i2cAISR(void)
{
I2C_InterruptSource intSource;
//
// Read interrupt source
//
intSource = I2C_getInterruptSource(I2CA_BASE);
//
// Interrupt source = stop condition detected
//
if(intSource == I2C_INTSRC_STOP_CONDITION){;}
//
// Interrupt source = Register Access Ready
//
// This interrupt is used to determine when the EEPROM address setup
// portion of the read data communication is complete. Since no stop bit
// is commanded, this flag tells us when the message has been sent
// instead of the SCD flag.
//
else if(intSource == I2C_INTSRC_REG_ACCESS_RDY){;}
//
// Issue ACK to enable future group 8 interrupts
//
Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP8);
}
这是中断服务函数,用来异常处理,我这里没有写,大家如有需要可以对照官方文件,也挺好理解的。
void main(void)
{
init_CPU1(); //初始化CPU1
// CLA_init(); //初始化CLA1
//
// initCPUTimer0(100000); //初始化timer0
// initCPUTimer1(200000); //初始化timer1
// initCPUTimer2(100); //初始化timer2
//
// EPWM_init(); //初始化EPWM模块
// initEPWM1(); //初始化EPWM1
// initEPWM_for_dac(EPWM8_BASE); //初始化EPWM8给DACA
//
// initDMA6_DAC(); //初始化DMA6给DAC
// initADC1(); //初始化ADC1
// start_epwm12_adc1(); //初始化EPWM12给adc1
// initDACA(); //初始化DACA
// initDACC(); //初始化DACC
//
// init_uart1(115200); //初始化串口1
initI2C1(); //初始化IIC1
// init_spi1(); //初始化SPI1
// Init_LEDV3(); //初始化LEDV3管脚
//
ENINT; //启用全局中断和实时中断
OLED4_Init(); //4针IIC的oled初始化
// CLA_runTest(1); //启动CLA任务1
// CLA_runTest(2);
// ESTOP0; //软件断点
//
// for(fftmov=0;fftmov<520;fftmov++)
// {
// fftout[fftmov]=fabsf(IOBuffer[fftmov]);
// __asm(" NOP");
// }
// ESTOP0; //软件断点
while(1)
{
// SPI_writeDataNonBlocking(SPIA_BASE, 257);
;
}
}
这里我就以初始化0.96寸的OLED为例来完成实际效果的演示了。
4.实际效果
可以看到波形和结果非常漂亮
下面我们再看一下占空比
这里设置的是50占空比
这是设置成33占空比
大家除了直观观察还可以看时间