Need help using ENC28J60 and PIC16F1939 SPI - Page 1
Forum section: electronics / general

author: Moondeck
author_rank: Regular Contributor
timestamp: November 22, 2015, 10:27:58 am
content: Hi everyone,so, i have this board with a PIC16F1939 and an ENC28J60 chip on it, and i have tried to write some code for it, but it does not seem to work. Its supposed to make the 2 ethernet LEDs blink slowly. Here is the code:spicom.cCode:#include <xc.h>#define _XTAL_FREQ 32000000void SPI_init(void){LATA = 0x00; //Used Microchip Code Configurator to figure out the pins (for now)TRISA = 0xFF;ANSELA = 0x3F;LATB = 0x00;TRISB = 0xFF;ANSELB = 0x3F;WPUB = 0x00;LATC = 0x00;TRISC = 0xD7;LATD = 0x00;TRISD = 0xFF;ANSELD = 0xFF;LATE = 0x00;TRISE = 0x0F;ANSELE = 0x07;WPUE = 0x00;OPTION_REGbits.nWPUEN = 0x01;APFCON = 0x00;SSPSTAT = 0x00;SSPCON1 = 0x20;SSPADD = 0x00;}void SPI_writechar(char writechar){SSPBUF = writechar;}void ENC_WCR(char adr,char value){SSPBUF = (adr + 0b01000000);__delay_ms(1);SSPBUF = value;}main.cCode:#include <xc.h>#include "spicom.h"// CONFIG1#pragma config FOSC = INTOSC // Oscillator Selection (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 = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)#pragma config CPD = OFF // Data Memory Code Protection (Data 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)#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)// CONFIG2#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)#pragma config VCAPEN = OFF // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)#pragma config PLLEN = ON // PLL Enable (4x PLL enabled)#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 LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)b#define _XTAL_FREQ 32000000void main(void){OSCCONbits.IRCF = 0b1110; //8 MHz, 32MHz with x4 PLLTRISA = 0;TRISB = 0;TRISC = 0;SPI_init();ENC_WCR(0x1F,0b11010010); //set Bank 3 in the ENC28J60ENC_WCR(0x14,0x14); //set MIREGADR to PHLCON addressENC_WCR(0x16,0b10111000);ENC_WCR(0x17,0b00001011);while(1);}spicom.hCode:#include <xc.h>void SPI_init(void);void SPI_writechar(char writechar);void ENC_WCR(char adr,char value);

author: Wilksey
author_rank: Super Contributor
timestamp: November 22, 2015, 11:58:39 pm
content: Do you have a schematic for the board?

author: Moondeck
author_rank: Regular Contributor
timestamp: November 23, 2015, 06:02:49 am
content: No, but its the diagram from the datasheet. No level shifting.

author: Wilksey
author_rank: Super Contributor
timestamp: November 23, 2015, 01:23:37 pm
content: So /INT from the ENC goes to the INT0 pin on the PIC (RB0)?The /CS goes to /SS on the PIC?Reset is doing what on the ENC? Floating, tied to VCC/GND or connected to a I/O?Have you confirmed that the hardware works, by using Microchip example code for instance?No level shifting, so I assume that you are running both PIC and ENC from 3.3V?

author: Moondeck
author_rank: Regular Contributor
timestamp: November 23, 2015, 02:08:10 pm
content: 1 no, it goes to RD02 Goes to RC6, handled in software3 Reset is floating, but has an internal pullup4 no, but i have noticed i am missing the cap on Vcap pin, gotta solder that and will update5 yes

author: Moondeck
author_rank: Regular Contributor
timestamp: November 23, 2015, 02:47:08 pm
content: Still nothing. i will try to replace the ENC28J60 chip.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 23, 2015, 03:20:39 pm
content: Still the same.

author: hans
author_rank: Super Contributor
timestamp: November 23, 2015, 03:36:18 pm
content: I would highly recommended looking at existing code for the ENC28J60 chip. Ethernet is a relatively software-heavy task.What I can see:Instead of using delay_ms(1) try polling the hardware bit for transmit busy. You still have got a chance that ENC_WCR will cause a SPI overrun because when the function exists, the main will call a new ENC_WCR and write another value to SSPBUF. This can happen relatively quickly.Most importantly you need to assert SPI Chip Select in order to have the chip listen/respond to commands.Additionally, review your choice of MCU. The PIC16F1939 only has 1kB of RAM, which is not large enough to hold 1 ethernet frame (1518 bytes). Most TCP/IP stacks will work with a local copy of ethernet frames before transmitting them to the ENC28J60. Techncially you can make it work, but you would have to do all buffer manipulations on the ENC28J60 SRAM buffer, which has the SPI bus as a bottleneck between it.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 23, 2015, 03:40:47 pm
content: Quote from: hans on November 23, 2015, 03:36:18 pmI will have a look, but now its about getting the thing to communicateI will try to have it looking for the hardware bit, and i have added the !CS signal to the code by now. I will also soon be trying this with another micro, that has 2K of RAM

author: Moondeck
author_rank: Regular Contributor
timestamp: November 24, 2015, 02:11:35 pm
content: Bump

author: Wilksey
author_rank: Super Contributor
timestamp: November 24, 2015, 03:59:05 pm
content: I don't have a ETH board to hand but you could try the following:Code:void ENC_WCR(char adr,char value){SSPBUF = (0x80 | (adr & 0x1F));while (!SSPIF);SSPBUF = value;while (!SSPIF);}In SPI_init:Code:APFCON = 0x00;SSPADD = 0x00;SSPSTAT=0x40;SSPCON1=0x30;And under main:Code:ENC_WCR(0x1F,0b00000010); //set Bank 2 in the ENC28J60ENC_WCR(0x14,0x14); //set MIREGADR to PHLCON addressENC_WCR(0x16,0b10111000);ENC_WCR(0x17,0b00111011);I haven't tested it.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 24, 2015, 04:15:35 pm
content: Thanks, i will test it tomorrow, i ran out of solder, and i only have that horrible lead-free stuff.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 24, 2015, 07:12:54 pm
content: I have managed to solder the thing together, still the same. Changed the main processor to PIC16F1789

author: dannyf
author_rank: Super Contributor
timestamp: November 24, 2015, 07:39:30 pm
content: QuoteIn situations like this, it is helpful to isolate the issue. For example, you will need to assure yourself that 1) you are generating the pulses and 2) the chip is generating the right pulses; and 3) the chip is responding to the right pulses.If you have a logic analyzer or even a scope, it would be fairly easy to do.If not, you should think about utilizing software spi for now and see how far you go with that.Changing chips / mcus without understanding what went wrong isn't going to be terribly productive.

author: 0b01010011
author_rank: Regular Contributor
timestamp: November 24, 2015, 10:17:43 pm
content: Maybe check/wait for the busy flag (BF) to clear before you send e.g.:while (BF);SSP1BUF = x;Otherwise you'll drop or corrupt packets as they are sent.Not sure if this is from PIC or not but I seem to remember you may also have to read SSP1BUF (even if you just dump it) before you can write to it - so you would need more like:while (BF);char dump = SSP1BUF;SSP1BUF = x;Of course you may wish to use the WDT or a timer to allow for timeout.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 25, 2015, 08:53:07 am
content: I have tried all of the above, and then i have put it in the "while(1)" loop (the transmission code, so its constantly transmitting stuff to the ENC chip) but when i probe it with my multimeter, it does not work, the voltage on the bus is 0. Gotta be a problem with the transmissionEDIT: It DOES however, show 3 V (VDD voltage) on the MOSI line... sp00kyEDIT2: Now it does not anymore. sp00ky af

author: 0b01010011
author_rank: Regular Contributor
timestamp: November 26, 2015, 02:56:12 am
content: I think that's quite normal on the PIC MSSP - MOSI will be held at whatever the last bit sent was.I would not put much trust in a multimeter reading though - especially the SCLK pin could go so fast your DMM will not notice, or will just show a very low average voltage swing. Certainly you'll not see it transitioning up-down-up-down etc., unless you significantly slow down the micro.If you can try looking with an oscilloscope at the pins.And I hate to say it, but read, and read again the devices datasheets - certainly I have missed things or not fully understood until I have re-read them!

author: MarkF
author_rank: Super Contributor
timestamp: November 26, 2015, 04:20:55 am
content: First off, a multimeter is going to be useless in this situation. You need a scope.Second, it doesn't appear to me that you're setting SPI Mode 0 and you never set the Chip_Select pin.Here are three code snippets from a pic18f2550:Replace '?'s with suitable values for your hardware wiring.Code:#define CSpin LAT?bits.L?? // Chip Select pin (Note. Replace with the pin you're using)// Initialize SPI{TRIS? = 0b???????? // Set the direction pins for SPI and Chip Select pinSSPSTAT = 0x40; // SPI (mode 0)SSPCON1 = 0x20;CSpin = 1;}// SPI Write{uint8_t data; // Data you want to senduint8_t tmp; // Dummy variable (maybe trash if you don't expect anything back)CSpin = 0;SSPBUF = data;while ( !SSPSTATbits.BF );tmp = SSPBUF;CSpin = 1;}// SPI Read{uint8_t data; // Data you get backCSpin = 0;SSPBUF = 0; // Dummy data to start transferwhile ( !SSPSTATbits.BF );data = SSPBUF;CSpin = 1;}

author: macboy
author_rank: Super Contributor
timestamp: November 26, 2015, 07:04:40 pm
content: I'm looking at the code in post #1. There is no way it would work.First, SPI generally requires a chip select. The SSP peripheral will take care of banging out bits and the clock for them, but it does not handle the chip select line. You need to manually set and clear that in software. Without this, the device you are trying to talk to has no idea when to start watching for bits to be clocked in (data and clock lines can be shared among >1 device). Check the device data sheet to understand whether its CS line is active Hi or Lo. Also double check whether data is clocked in on the Hi-Lo or Lo-Hi transition and make sure to set up the SSP appropriately.Next, as already pointed out, your ENC_WCR function is setting SSPBUF in an unsafe way. The buffer may not be ready to be written to. Check the busy flag first. Only write when not busy. Otherwise you clobber the previous data you tried to send (and/or the data being received).

author: Moondeck
author_rank: Regular Contributor
timestamp: November 26, 2015, 08:07:31 pm
content: I have made the code for CS line since the first post. I have tried all the code, but it looks like its still not doing anything, and i dont have access to an oscilloscope. I will keep trying, i will try to play around with the ENC_WCR function tomorrow.

author: MarkF
author_rank: Super Contributor
timestamp: November 27, 2015, 01:07:11 am
content: You MUST read the SSPBUF register after every transfer or you will lock up the SPI hardware. Your code appears to only write to SSPBUF.

author: Moondeck
author_rank: Regular Contributor
timestamp: November 27, 2015, 08:14:06 am
content: Quote from: MarkF on November 27, 2015, 01:07:11 amOk, i will try, do i just do somevariable = SSPBUF; and then write?

author: Moondeck
author_rank: Regular Contributor
timestamp: November 27, 2015, 09:50:41 am
content: This is the code now. I have integrated it into main.c for nowCode:#include <xc.h>// CONFIG1#pragma config FOSC = INTOSC // Oscillator Selection (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 = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)#pragma config CPD = OFF // Data Memory Code Protection (Data 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)#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)// CONFIG2#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)#pragma config VCAPEN = OFF // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)#pragma config PLLEN = ON // PLL Enable (4x PLL enabled)#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 LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)b#define _XTAL_FREQ 16000000void SPI_init(void){PIE1bits.SSP1IE = 1;LATA = 0x00; //Used Microchip Code Configurator to figure out the pins (for now)TRISA = 0xFF;ANSELA = 0x3F;LATB = 0x00;TRISB = 0xFF;ANSELB = 0x3F;WPUB = 0x00;LATC = 0x00;TRISC = 0xD7;LATD = 0x00;TRISD = 0xFF;ANSELD = 0xFF;LATE = 0x00;TRISE = 0x0F;ANSELE = 0x07;WPUE = 0x00;OPTION_REGbits.nWPUEN = 0x01;APFCON = 0x00;SSPADD = 0x00;SSPSTAT = 0x40; // SPI (mode 0)SSPCON1 = 0x20;TRISCbits.TRISC6 = 0;LATCbits.LATC6 = 0; //board specific !CS code}void SPI_writechar(char writechar){SSPBUF = writechar;}void ENC_WCR(char adr,char value){char dump;SSP1BUF = (adr + 0b01000000);while(SSP1IF != 1);dump = SSP1BUF;SSP1BUF = value;while(SSP1IF != 1);}void main(void){OSCCONbits.IRCF = 0b1111; //16 MHzSPI_init();while(1){ENC_WCR(0x1F, 0b11010010); //set Bank 3 in the ENC28J60ENC_WCR(0x14, 0x14); //set MIREGADR to PHLCON addressENC_WCR(0x16, 0b10111000);ENC_WCR(0x17, 0b00001011);__delay_ms(50);}}

author: dannyf
author_rank: Super Contributor
timestamp: November 27, 2015, 07:05:19 pm
content: QuoteYour original code, with minor modifications, at work:

author: MarkF
author_rank: Super Contributor
timestamp: November 27, 2015, 07:28:50 pm
content: See changes to initialization and output functions. If you're going to use the interrupt flag to check for transfer complete, you need to clear it before starting each transfer.Quote from: Moondeck on November 27, 2015, 09:50:41 am