完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
电子发烧友论坛|
大家好,我正在尝试设计一个程序,它通过I2C每秒轮询一个实时时钟(DS1307+)。使用的微控制器是dsPIC33EP64GS506。我使用一个主中断标志等待一个任务(启动或停止条件,字节发送等)完成。但是,似乎没有设置主中断标志。这里是一个最小的(非)工作示例(在文章的末尾)。我浏览了几个应用程序说明、数据表等,不幸的是,我没有设法解决这个问题。程序在第一次调用函数I2C1_Wait_Whit_Busy()时就卡住了,它使用了一个主中断标志。在发送启动条件后,首先使用此函数。但是,还有另一种检查START条件是否完成的方法:因为在START条件完成后,SEN位被硬件清除。如果我使用这个命令,那么程序跳到下一行,这表示不管主中断标志如何,开始条件都已完成。换句话说,程序被卡在I2C1_Wait_._Busy()函数的下一个调用上。
以上来自于百度翻译 以下为原文 Hello everyone, I'm trying to design a program that polls a real-time clock (DS1307+) every second via I2C. The microcontroller used is dsPIC33EP64GS506. I use a master interrupt flag to wait until a task (start or stop condition, byte send etc.) is finished. However, it seems that the master interrupt flag is never set. Here is a minimum (non)working example (at the end of the post). I went through several application notes, datasheets etc., unfortunately, I didn't manage to solve the problem. The program is stuck on the very first call of the function I2C1_Wait_While_Busy(), which uses a master interrupt flag. This function is first used after sending a START condition. However, there is another way to check if START condition finished: while (I2C1CON1bits.SEN==1); since SEN bit is cleared by hardware after START condition finishes. If I use this, then the program skips to the next line, which indicates that start condition finished regardless to the master interrupt flag. In other words, the program is stuck on the next call of I2C1_Wait_While_Busy() function. The minimum (non)working example: #include // CPU operates at 120 MHz (60 MIPS) #define FCY 60000000UL #include #include /* INITIALIZATION ROUTINES */ // Primary oscillator configuration _FOSCSEL(FNOSC_FRC & IESO_OFF); _FOSC(PLLKEN_ON & FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); // ALTI2C1_ON - I2C1 mapped to ASDA1/ASCL1 instead of SDA1/SCL1 // ALTI2C2_ON - I2C2 mapped to ASDA2/ASCL2 instead of SDA2/SCL2 // DBCC (not used) _FDEVOPT(ALTI2C1_ON & ALTI2C2_ON); void Init_Oscillator(void) { /* Configure primary oscillator */ // FOSC = FIN/N1*M/N2 // FPLLI = FIN/N1 // N1 = PLLPRE+2, N1 in [2,33] short N1 = 2; CLKDIVbits.PLLPRE = N1-2; // N1=PLLPRE+2=2 // FVCO = FPLLI*M // M = PLLDIV+2, M in [2,513] short M = 48; PLLFBD = M-2; // FPLLO = FVCO/N2 (FPLLO=FOSC, FCY=FOSC/2) // N2 = 2*(PLLPOST+1), N2 is {2,4,8} short N2 = 2; // 2, 4 or 8 CLKDIVbits.PLLPOST = N2/2-1; /* Initiate Clock Switch */ // Primary Oscillator with PLL (XTPLL, HSPLL, ECPLL) __builtin_write_OSCCONH(0x03); // 0b011 -> NOSC<2:0> (OSCCON<10:8>) // Request oscillator switch to selection specified by the NOSC<2:0> bits __builtin_write_OSCCONL(OSCCON | 0x01); // 0b1 -> OSWEN<0> (OSCCON<0>) // Wait for Clock switch to occur // Current Oscillator Selection bits (read-only): COSC<2:0> (OSCCON<15:13>) while (OSCCONbits.COSC!=0b011); // Wait for PLL to lock // PLL Lock Status bit (read-only): LOCK<0> (OSCCON<5>) while (OSCCONbits.LOCK!=1); /* Configure auxiliary oscillator */ // 120 MHz for proper PWM and ADC operation // 7.37 MHz * 16 = 117.92 MHz // Configure source to fast RC with APLL ACLKCONbits.ASRCSEL = 0; // Don't use primary oscillator ACLKCONbits.FRCSEL = 1; // Use fast RC oscillator as source ACLKCONbits.SELACLK = 1; // Don't use primary PLL (FVCO) ACLKCONbits.APSTSCLR = 0b111; // Divide-by-1 for PWM ACLKCONbits.ENAPLL = 1; // Enable 16x APLL // Wait for auxiliary PLL to lock while (ACLKCONbits.APLLCK!=1); } void Init_Ports(void) { // Set all ports to digital ANSELA = 0x00; ANSELB = 0x00; ANSELC = 0x00; ANSELD = 0x00; // Set all ports to low LATA = 0x00; LATB = 0x00; LATC = 0x00; LATD = 0x00; } void Init_I2C(void) { // Set baud rate (100 kHz) I2C1BRG = 291; // FSCL=100kHz, Tdelay=250ns, FP/2=30MHz // // Enable master interrupts // IPC4bits.MI2C1IP = 1; // interrupt priority // IEC1bits.MI2C1IE = 1; // enable interrupt // IFS1bits.MI2C1IF = 0; // clear interrupt flag // Enable I2C1 I2C1CON1bits.I2CEN = 1; } /* I2C ROUTINES */ void I2C1_Send_Byte(char byte) { // Load I2C1 transmit register I2C1TRN = byte; } void I2C1_Wait_While_Busy() { // Check if I2C1 operation is done while(IFS1bits.MI2C1IF==0); // I2C1 is ready, clear interrupt flag IFS1bits.MI2C1IF = 0; } void I2C1_Check_Ack_Status() { if (I2C1STATbits.ACKSTAT==1) { // NACK, do nothing } else { // ACK, do nothing } } void I2C_RTC_Get_Date_and_Time() { int b; char data[7]; // Reset master interrupt flag IFS1bits.MI2C1IF = 0; // Wait for idle state while(I2C1STATbits.TRSTAT); /* MASTER TRANSMITTER / SLAVE RECEIVER */ // Send START condition I2C1CON1bits.SEN = 1; I2C1_Wait_While_Busy(); // Call the RTC in receive mode I2C1_Send_Byte(0xD0); // 1101000|0 I2C1_Wait_While_Busy(); I2C1_Check_Ack_Status(); // Send the first RTC register address I2C1_Send_Byte(0x00); I2C1_Wait_While_Busy(); I2C1_Check_Ack_Status(); /* MASTER RECEIVER / SLAVE TRANSMITTER */ do { // Send RESTART condition I2C1CON1bits.RSEN = 1; I2C1_Wait_While_Busy(); // Reverse direction (RTC is transmitter) I2C1_Send_Byte(0xD1); // 1101000|1 I2C1_Wait_While_Busy(); } while (I2C1STATbits.ACKSTAT==1); // Set to ACK bit I2C1CON1bits.ACKDT = 0; // Receive 7 bytes of data from RTC for (b=0; b<7; b++) { // Enable receive mode I2C1CON1bits.RCEN = 1; I2C1_Wait_While_Busy(); // Get byte from buffer data = I2C1RCV; // Set to NACK bit after last received byte if (b==6) I2C1CON1bits.ACKDT = 1; // Master acknowledge I2C1CON1bits.ACKEN = 1; } // Send STOP condition I2C1CON1bits.PEN = 1; I2C1_Wait_While_Busy(); } /* MAIN ROUTINE */ int main(void) { Init_Oscillator(); Init_Ports(); Init_I2C(); while(1) { // Poll RTC every second I2C_RTC_Get_Date_and_Time(); __delay_ms(1000); } } |
|
相关推荐
11个回答
|
|
|
嗨,I2C是通话协议前的侦听。在执行开始信令序列之前,I2C硬件将检查SDA和SCL信号线是否都是高逻辑电平。如果SDA或SCL是低,硬件将立即拒绝操作,清除SEN位并设置BCL状态位。输入启动条件,代码应该测试S位不是1,BCL位不是1。在设置SEN位之后,代码应该再次检查BCL位没有被触发。原因可能是SDA或SCL线路缺少上拉电阻,PIC在从机进行读取操作时被重置,或与配置中的其他外设冲突。在64引脚封装的GS506上,SDA1和SCL1与模拟输入AN21和AN20以及TDI共享。请确保禁用JTAG以使SCL1工作!有一个配置寄存器位JTAGEN,必须被编程=0或JTAGEN=OFF,以便I2C1工作。可以读取IO端口输入寄存器,也可以在启用I2C外围设备时读取PORTB寄存器,也可以在尝试设置SEN位之前使用调试器读取PORTB寄存器,检查位RB7和RB6,oR RC7和RC8如果使用备用引脚。问候,Mysil
以上来自于百度翻译 以下为原文 Hi, I2C is a listen before you talk protocol. I2C hardware will check that both SDA and SCL signal lines are High logic level before Start signalling sequence is performed. If either SDA or SCL is Low, hardware will immediately refuse the operation, clear the SEN bit and set BCL status bit. Before attempting a Start condition, code should test that S bit is Not 1, and that BCL bit is Not 1. After setting SEN bit, code should again check that BCL bit have not been triggered. Reason may be missing pull-up resistors for SDA or SCL lines, PIC having been reset while doing a read operation from a Slave, or conflict with other peripherals in configuration. On GS506 in 64 pin package, SDA1 and SCL1 is shared with Analog inputs AN21 and AN20, and with TDI. Make sure that JTAG is disabled for SCL1 to work! There is a Configuration register bit JTAGEN that must be programmed = 0 or JTAGEN = OFF, for I2C1 to work. It is possible to read IO Port Input register, also when I2C peripheral is enabled, You may read PORTB register in code, or using debugger before trying to set SEN bit, check bits RB7 and RB6, or RC7 and RC8 if using alternate pins. Regards, Mysil |
|
|
|
|
|
亲爱的Mysil,我有2K2上拉电阻到3.3伏电源。我使用I2C1在另一组引脚上(ASDA1/ASCL1)。在启动条件之前,我把下面的代码放在这里:程序通过了这一点,但是它又被卡在中断标志上。
以上来自于百度翻译 以下为原文 Dear Mysil, I have 2k2 pull-up resistors to 3.3 V supply. I'm using I2C1 on the alternate set of pins (ASDA1/ASCL1). I've put the following code before the start condition: while(I2C1STATbits.S); while(I2C1STATbits.BCL); The program passed this point, however, it is stuck again on the interrupt flag. |
|
|
|
|
|
PIN模拟能力吗?如果是,它们是数字模式吗?
以上来自于百度翻译 以下为原文 Are the pins analog capable? If so, are they in digital mode? |
|
|
|
|
|
不,它们不是模拟能力。以防万一,我把所有的引脚都设置成数字,如下:
以上来自于百度翻译 以下为原文 No, they're not analog capable. Just in case, I've set all pins to digital, as follow: ANSELA=0x00; ANSELB=0x00; ANSELC=0x00; ANSELD=0x00; |
|
|
|
|
|
不过,在调试器中使用监视窗口来验证I2C引脚的POTC位都是真的1,在尝试开始之前,它们在启动序列完成后都变成0。迈西尔
以上来自于百度翻译 以下为原文 Still, Use Watch window in Debugger to verify that PORTC bits for the I2C pins are both really 1, before attempting the Start, and that they both become 0 after the Start sequence is completed. Mysil |
|
|
|
|
|
这里是一个波形(见附件)的SDA(黄色)和SCL(蓝色)线,当我试图强加一个开始条件。我不明白这个频率(CCA 500千赫)。我试着改变I2C1BRG寄存器的值,但是我没有改变时钟频率。我现在也改为SDA1/SCL1引脚(而不是ASDA1/ASCL1)。我可以不知何故单独测试I2C,而不需要从设备?
以上来自于百度翻译 以下为原文 Here is a waveform (see attached) for SDA (yellow) and SCL (blue) lines, when I attempt to impose a START condition. I don't understand this frequency (cca 500 kHz). I tried changing the value of I2C1BRG register, but I didn't manage to change the clock frequency. I also changed to SDA1/SCL1 pins now (instead of ASDA1/ASCL1). Can I somehow test the I2C alone, without the slave device? Attached Image(s) |
|
|
|
|
|
你用的是什么代码?如果你只做一个开始,没有别的,那么这两个引脚都应该保持低。你不需要一个连接的设备来测试,只是上拉电阻。
以上来自于百度翻译 以下为原文 What exact code are you using there? If you just do a START, and nothing else, then the two pins should both stay low. You don't need a slave device connected to test that, just the pullup resistors. |
|
|
|
|
|
这里是我的代码,在原始帖子中的代码的简化版本。这个程序(一切都在主要功能)会尝试:1。发送启动条件2。幸运的是,发送的波形与上一篇文章中的波形相同。我把奴隶从这次考试中除掉了。
以上来自于百度翻译 以下为原文 Here is my code again, simplified version of the code in the original post. This program (everything is in main function) will try to: 1. Send a START condition 2. Send an address Unfortunately, the waveforms are the same as in the previous post. I've removed the slave for this test. #include // CPU operates at 120 MHz (60 MIPS) #define FCY 60000000UL #include /* INITIALIZATION ROUTINES */ // Primary oscillator configuration _FOSCSEL(FNOSC_FRC & IESO_OFF); _FOSC(PLLKEN_ON & FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_XT); //// ALTI2C1_ON - I2C1 mapped to ASDA1/ASCL1 instead of SDA1/SCL1 //// ALTI2C2_ON - I2C2 mapped to ASDA2/ASCL2 instead of SDA2/SCL2 //_FDEVOPT(ALTI2C1_ON & ALTI2C2_ON); // ICS_PGD3 - Communicate on PGEC3 and PGED3 // JTAGEN_OFF - JTAG is disabled _FICD(ICS_PGD3 & JTAGEN_OFF); void Init_Oscillator(void) { /* Configure primary oscillator */ // FOSC = FIN/N1*M/N2 // FPLLI = FIN/N1 // N1 = PLLPRE+2, N1 in [2,33] short N1 = 2; CLKDIVbits.PLLPRE = N1-2; // N1=PLLPRE+2=2 // FVCO = FPLLI*M // M = PLLDIV+2, M in [2,513] short M = 48; PLLFBD = M-2; // FPLLO = FVCO/N2 (FPLLO=FOSC, FCY=FOSC/2) // N2 = 2*(PLLPOST+1), N2 is {2,4,8} short N2 = 2; // 2, 4 or 8 CLKDIVbits.PLLPOST = N2/2-1; /* Initiate Clock Switch */ // Primary Oscillator with PLL (XTPLL, HSPLL, ECPLL) __builtin_write_OSCCONH(0x03); // 0b011 -> NOSC<2:0> (OSCCON<10:8>) // Request oscillator switch to selection specified by the NOSC<2:0> bits __builtin_write_OSCCONL(OSCCON | 0x01); // 0b1 -> OSWEN<0> (OSCCON<0>) // Wait for Clock switch to occur // Current Oscillator Selection bits (read-only): COSC<2:0> (OSCCON<15:13>) while (OSCCONbits.COSC!=0b011); // Wait for PLL to lock // PLL Lock Status bit (read-only): LOCK<0> (OSCCON<5>) while (OSCCONbits.LOCK!=1); /* Configure auxiliary oscillator */ // 120 MHz for proper PWM and ADC operation // 7.37 MHz * 16 = 117.92 MHz // Configure source to fast RC with APLL ACLKCONbits.ASRCSEL = 0; // Don't use primary oscillator ACLKCONbits.FRCSEL = 1; // Use fast RC oscillator as source ACLKCONbits.SELACLK = 1; // Don't use primary PLL (FVCO) ACLKCONbits.APSTSCLR = 0b111; // Divide-by-1 for PWM ACLKCONbits.ENAPLL = 1; // Enable 16x APLL // Wait for auxiliary PLL to lock while (ACLKCONbits.APLLCK!=1); } void Init_Ports(void) { // Set all ports to digital ANSELA = 0x00; ANSELB = 0x00; ANSELC = 0x00; ANSELD = 0x00; // Set all ports to low LATA = 0x00; LATB = 0x00; LATC = 0x00; LATD = 0x00; // Set ports to digital input TRISBbits.TRISB6 = 0; TRISBbits.TRISB7 = 0; } void Init_I2C(void) { // Set baud rate (100 kHz) I2C1CON1 = 0; I2C1BRG = 291; // FSCL=100kHz, Tdelay=250ns, FP/2=30MHz // I2C1BRG = 5810; I2C1CON1bits.DISSLW = 1; // Slew rate control disabled for 100 kHz // // Enable master interrupts // IPC4bits.MI2C1IP = 1; // interrupt priority // IEC1bits.MI2C1IE = 1; // enable interrupt // IFS1bits.MI2C1IF = 0; // clear interrupt flag // Enable I2C1 I2C1CON1bits.I2CEN = 1; } /* MAIN ROUTINE */ int main(void) { Init_Oscillator(); Init_Ports(); /* I2C ROUTINE */ Init_I2C(); __delay_us(1000); IFS1bits.MI2C1IF = 0; while(I2C1STATbits.TRSTAT); while(I2C1STATbits.S); while(I2C1STATbits.BCL); I2C1CON1bits.SEN = 1; while(IFS1bits.MI2C1IF==0); IFS1bits.MI2C1IF = 0; // clear I2C1TRN = 0xD0; while(IFS1bits.MI2C1IF==0); IFS1bits.MI2C1IF = 0; // clear while(1) ; } |
|
|
|
|
|
您确定在Init_Ports()中向LAT寄存器写入零时,并不只是看到引脚变低,而在启用I2C外围设备时返回高位吗?
以上来自于百度翻译 以下为原文 Are you sure you aren't just seeing the pins go low when you write zero to the LAT register in Init_Ports(), then return high when you enable the I2C peripheral? |
|
|
|
|
|
你说得对,我找错了方向。用SDA1/SCL1做的一切都很好。但是,当我切换到ASDA1/ASCL1时,程序被粘贴在中断标志上,正如我在第一篇文章中所描述的。我用这段代码切换到另一对引脚:
以上来自于百度翻译 以下为原文 You're right, I was looking to the wrong thing. Everything works just fine with SDA1/SCL1. However, when I switch to ASDA1/ASCL1, the program is stuck on the interrupt flag, as I described in my first post. I use this piece of code to switch to the alternate pair of pins: _FDEVOPT(ALTI2C1_ON & ALTI2C2_ON); |
|
|
|
|
|
我终于找到了困扰我的问题。当您在与从机进行I2Cx通信的中间停止处理器时,从机可能在下一次启动(或重新启动)时将SDAx线保持在低位。这正是DS1307+实时时钟在我身上发生的情况。这可以通过在启用I2Cx之前生成10个时钟周期(在这种情况下,SCLx被配置为数字输出)来解决。这样:之后,从属将释放SDAx线。现在一切正常,谢谢大家的帮助!
以上来自于百度翻译 以下为原文 I finally found the problem that was bugging me. When you halt the processor in the middle of the I2Cx communication with a slave, the slave might hold the SDAx line low on next start (or restart). This is exactly what happened to me with DS1307+ real-time clock. This can be solved by generating 10 clock periods (in that case SCLx is configured as a digital output) before enabling I2Cx. Something like this: void Generate_10_Clock_Pulses_I2C1(void) { char i; // Set initial clock level to HIGH I2C1_SCL = 1; __delay_us(5); // Generate 10 clock periods // Frequency: 100 kHz (10 us) for (i=0; i<20; i++) { I2C1_SCL = ~I2C1_SCL; // Macro for LATCbits.LATC8 __delay_us(5); // 50%, 100 kHz } } After that, the slave will release the SDAx line. Everything works just fine now, thank you all for your help! |
|
|
|
|
只有小组成员才能发言,加入小组>>
MPLAB X IDE V6.25版本怎么对bootloader和应用程序进行烧录
473 浏览 0 评论
5793 浏览 9 评论
2334 浏览 8 评论
2224 浏览 10 评论
请问是否能把一个ADC值转换成两个字节用来设置PWM占空比?
3530 浏览 3 评论
1123浏览 1评论
有偿咨询,关于MPLAB X IPE烧录PIC32MX所遇到的问题
1095浏览 1评论
我是Microchip 的代理商,有PIC16F1829T-I/SS 技术问题可以咨询我,微信:A-chip-Ti
873浏览 1评论
MPLAB X IDE V6.25版本怎么对bootloader和应用程序进行烧录
475浏览 0评论
/9
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-12-2 01:57 , Processed in 2.948277 second(s), Total 92, Slave 75 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191

淘帖
1387