我已经编写/复制/修改了一个用 C 编写的 I2C 客户端,它可以工作。当我将其翻译为汇编语言时,它失败了。我哪里错了?
主/从设计非常简单。主站向从站发送一个整数值。从机闪烁 LED 以显示接收到的值,然后递增该值并将其发送回主机。 当我运行 C 语言从属版本时,它按预期工作。当我运行汇编版本时,不会驱动中断,并且我观察到从属设备上没有任何活动(没有 LED 闪烁)。 我将发布 C 代码和相应的汇编代码,看看是否有人可以看到为什么汇编版本不起作用
这是C代码
/*
* File: slave.c
* Author: mike
*
* Created on 18 March 2024, 2:32 PM
*/
#include <stdio.h>
#include <stdlib.h>
/*
PIC16F1503
+----------:_:----------+
+5V <> 1 : VDD VSS : 14 <> GND
RED LED o/p <> 2 : RA5 RA0 : 13 <>
<> 3 : RA4 RA1 : 12 <>
<> 4 : RA3/MCLR RA2 : 11 <>
<> 5 : RC5 RC0 : 10 <> SCL (input)
<> 6 : RC4 RC1 : 9 <> SDA (input)
<> 7 : RC3 RC2 : 8 <> RED LED o/p
+-----------------------+
DIP-14
*/
// PIC16F1503 Configuration Bit Settings
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection Bits (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
// Set Clock Freq. & Delays
#define _XTAL_FREQ 16000000 // oscillator frequency for _delay()
#include <xc.h>
#define slaveAddress 0x60
// Set Clock Freq. & Delays
#define _XTAL_FREQ 16000000 // oscillator frequency for _delay()
#define testBit(var, bit) (var & (1 << bit))
#define clearBit(var, bit) (var ^= (1 << bit))
#define setBit(var, bit) (var |= (1 << bit))
uint8_t temp, flagWord;
#define dataReceived 0
void __interrupt() ISR(void)
{
if (PIR1bits.SSP1IF || PIR2bits.BCL1IF) {
//Check for SSPIF
if(PIR1bits.SSP1IF) {
if (SSP1STATbits.P) { // Stop bit: 1 = stop detected
//Stop Condition
// do nothing
;
}
else if(SSP1STATbits.R_nW) { // R/W bit: 1 = read, 0 = write
//Host wants to read (client transmit)
// increment value in temp and send to master
temp+=1; // increment temp and return to master
SSP1BUF = temp;
}
else {
// Host wants to write (client receive)
if(SSP1STATbits.D_nA) { // Data/Address: 1 = data, 0 = address
//Last byte was data
temp = SSP1BUF;
setBit(flagWord, dataReceived);
}
else {
//Last byte was an address
//Clear the Buffer Full (BF) flag
temp = SSP1BUF;
}
}
}
if(PIR2bits.BCL1IF) {
//Clear the Buffer Full (BF) flag
temp = SSP1BUF;
// Clear BCLIF
PIR2bits.BCL1IF = 0;
}
//Release Clock Stretch
SSP1CON1bits.CKP = 1;
//Clear SSP1IF
PIR1bits.SSP1IF = 0;
}
}
void flashLEDMultiple(uint8_t count) {
for (int i=0; i<count; i++) {
// turn LED on
LATA5 = 1;
__delay_ms(200);
LATA5 = 0;
if (i+1<count) {
__delay_ms(200);
}
}
}
void main(void) {
OSCCON = 0B01111000; // set oscillator to 16MHz
//Init the I2C Pins on the Device
// RA0 = SCL, RA1 = SDA
// RC0 = SCL, RC1 = SDA
//Disable analog mode
ANSELA = 0; // all PORTA pins digital
ANSELC = 0; // all PORTC pins digital
TRISCbits.TRISC0 = 0b1;
TRISCbits.TRISC1 = 0b1;
TRISAbits.TRISA5 = 0; // output for LED
LATAbits.LATA5 = 0; // LED off
//Initialize the I2C Driver
//Reset Registers
SSP1CON1 = 0x00;
SSP1CON2 = 0x00;
SSP1CON3 = 0x00;
SSP1STAT = 0x00;
SSP1STATbits.SMP = 1; //Disable slew control for Standard mode
SSP1CON1bits.SSPM = 0b0110; //Set MSSP Operating Mode (7-bit Client)
SSP1CON2bits.SEN = 1; //Enable clock stretching
SSP1CON3bits.SBCDE = 1; //Enable bus collision interrupts
SSP1ADD = (unsigned char)(slaveAddress << 1); //Load slave address
PIR2bits.BCL1IF = 0; //Clear Bus Collision interrupt flag
PIR1bits.SSP1IF = 0; //Clear the SSP interrupt flag
PIE2bits.BCL1IE = 1; //Enable BCLIF
PIE1bits.SSP1IE = 1; //Enable SSPIF
SSP1CON1bits.SSPEN = 1; //Enable the module
INTCONbits.PEIE = 1; // Enable peripheral interrupts
INTCONbits.GIE = 1; // Enable global interrupts
while (1)
{
if (testBit(flagWord,dataReceived)) {
clearBit(flagWord,dataReceived);
flashLEDMultiple(temp); // flash the received count value
}
}
return;
}
这是“相同的?”汇编代码(不起作用)
; Slave: Sample code to demonstrate use of the MSSP module to drive
; slave device via I2C
; Author: Mike Brady
; Company: Java Point Pty Ltd
;
; Pin summary
; 1 VDD +3.3V
; 8 RC2 Red LED
; 14 VSS Ground
;
; Assembled with pic-as (v2.32) under MPLAB X IDE (v6.15) 4 Mar 2024
;
; Add this line in the project properties box, pic-as Global Options -> Additional options:
; -Wa,-a -Wl,-pPOR_Vec=0h,-pISR_Vec=4h
;
;
; PIC16F1503
; +----------:_:----------+
; +5V <> 1 : VDD VSS : 14 <> GND
; RED LED o/p <> 2 : RA5 RA0 : 13 <>
; <> 3 : RA4 RA1 : 12 <>
; <> 4 : RA3/MCLR RA2 : 11 <>
; <> 5 : RC5 RC0 : 10 <> SCL (input)
; <> 6 : RC4 RC1 : 9 <> SDA (input)
; <> 7 : RC3 RC2 : 8 <>
; +-----------------------+
; DIP-14
;
PAGEWIDTH 132
RADIX DEC
#include <xc.inc>
; See respective data sheet for additional information on configuration word.
config FOSC = INTOSC ; Oscillator Selection bits (HS oscillator)
config WDTE = OFF ; Watchdog Timer (WDT disabled)
config PWRTE = OFF ; Power-up Timer Enable bit (Power-up Timer is disabled)
config CP = OFF ; Code Protection bit (Code protection disabled)
config MCLRE = ON
config BOREN = ON
config CLKOUTEN = OFF
config WRT = OFF
config STVREN = OFF
config LVP = OFF
config LPBOR = OFF
config BORV = LO
; vars used by TimerLib library
global d1,d2,d3
extrn delay5us
#define FOSC 16000 ; Oscillator Clock in kHz
#define dataReceived 0
//I2C Test Properties
#define SLAVE_ADDRESS 0x60 ; unique address for this slave
;**********************************************************************
; Power-On-Reset entry point
;**********************************************************************
PSECT POR_Vec,global,class=CODE,delta=2
global resetVec
resetVec:
goto main
;objects in Common RAM - address 70h
psect udata_shr,global,class=COMMON,space=1,delta=1,noexec
d1: DS 1
d2: DS 1
d3: DS 1
flagWord: DS 1
bufferValue: DS 1
flashCounter: DS 1
;**********************************************************************
; Interrupt vector and handler
;**********************************************************************
PSECT ISR_Vec,global,class=CODE,delta=2
global ISR_Vec
ISR_Vec:
banksel PIR1
btfsc PIR1, PIR1_SSP1IF_POSN
goto SSP_or_BCL_set
btfss PIR2, PIR2_BCL1IF_POSN
retfie
SSP_or_BCL_set:
btfss PIR1, PIR1_SSP1IF_POSN
goto checkBusCollision
banksel SSP1STAT
btfss SSP1STAT, SSP1STAT_R_nW_POSN
goto masterSending
incf bufferValue,f
movf bufferValue,w
movwf SSP1BUF
goto checkBusCollision
masterSending:
btfss SSP1STAT, SSP1STAT_D_nA_POSN
goto processAddress
movf SSP1BUF ; read value from buffer
movwf bufferValue ; store received value
bsf flagWord, dataReceived
goto checkBusCollision
processAddress:
movf SSP1BUF ; clear the BF flag
checkBusCollision:
banksel PIR2
btfss PIR2, PIR2_BCL1IF_POSN
goto clockRelease
banksel SSP1BUF
movf SSP1BUF,w ; clear the BF flag
banksel PIR2
bcf PIR2, PIR2_BCL1IF_POSN
clockRelease:
banksel SSP1CON1
bsf SSP1CON1, SSP1CON1_CKP_POSN
banksel PIR1
bcf PIR1, PIR1_SSP1IF_POSN
retfie
END_ISR_Vec:
;PSECT MainCode,global,class=CODE,delta=2
psect code,global,class=CODE,delta=2
initialisation: ; setup peripherals, start timer
call setupOscillator
call setupIOPins
call I2C_init
return
setupOscillator:
; initialise internal oscillator to 16MHz
banksel OSCCON
movlw 01111000B ; Int. osc. 16 MHz
movwf OSCCON
btfss HFIOFR ; Int. osc. running?
goto $-1 ; No, loop back
btfss HFIOFS ; Osc. stable?
goto $-1 ; No, loop back.
return
setupIOPins: ; RA0 - SCL, RA1 = SDA
banksel ANSELA
clrf ANSELA ; all PORTA pins digital
clrf ANSELC ; all PORTC pins digital
; set all PORTA pins as output
banksel TRISA
clrf TRISA ; set all PORTA as output
; set RC0 and RC1 as input, the rest as output
clrf TRISC
bsf TRISC, TRISC_TRISC0_POSN
bsf TRISC, TRISC_TRISC1_POSN
; turn off LED
bcf LATA, LATA_LATA5_POSN
return
I2C_init:
;Configure MSSP module for Slave Mode
banksel SSP1CON1
clrf SSP1CON1
clrf SSP1CON2
clrf SSP1CON3
clrf SSP1STAT
bsf SSP1STAT, SSP1STAT_SMP_POSN ; Disable slew control for Standard mode
movlw 00000110B ; Set MSSP Operating Mode (7-bit Client)
iorwf SSP1CON1,f
bsf SSP1CON2, SSP1CON2_SEN_POSN ; Enable clock stretching
bsf SSP1CON3, SSP1CON3_SBCDE_POSN ; Enable bus collision interrupts
movlw SLAVE_ADDRESS<<1
movwf SSP1ADD ; Load slave address
banksel PIR2
bcf PIR2, PIR2_BCL1IF_POSN ; Clear Bus Collision interrupt flag
bcf PIR1, PIR1_SSP1IF_POSN ; Clear the SSP interrupt flag
banksel PIE2
bsf PIE2, PIE2_BCL1IE_POSN ; Enable bus collision interrupt
bsf PIE1, PIE1_SSP1IE_POSN ; Enable MSSP interrupt
bsf INTCON, INTCON_PEIE_POSN
bsf INTCON, INTCON_GIE_POSN
return
;**********************************************************************
; main program
;**********************************************************************
main:
call initialisation
loop:
btfss flagWord, dataReceived
goto loop
bcf flagWord, dataReceived
movf bufferValue, w
call flashLEDMultiple
goto loop
flashLEDMultiple:
movwf flashCounter
call flashLED
call delay200ms
decfsz flashCounter, f
goto flashLEDMultiple
return
flashLED:
banksel LATA
bsf LATA, LATA_LATA5_POSN
call delay200ms
bcf LATA, LATA_LATA5_POSN
return
delay200ms:
movlw 0x6D
movwf d1
movlw 0xBF
movwf d2
movlw 0x02
movwf d3
delay200ms_0:
decfsz d1, f
goto $+2
decfsz d2, f
goto $+2
decfsz d3, f
goto delay200ms_0
return
显然这两个程序在功能上并不等同,但我很难看出它们有何不同。 希望有眼力的人能指出他们的不同之处。
@Lundin 中断未被驱动/调用。我没有可用的调试器,但我使用 LED 闪烁来显示代码的哪些部分正在执行。我只是没有在已发布的代码中显示它们。
@Erik Eidt 反汇编代码看起来非常标准。除了存储体选择之外,它还直接进入 SSP1IF 和 BCL1IF 的标志测试。我附上了屏幕片段
@Frankie_C 的目的是总是在退出时当SSP1IF中断发生时设置CKP标志,我相信代码会这样做。 ISR 地址在链接器中使用指令 -pISR_Vec=4h 设置。
汇编版本有几个错误。