Lập Trình VĐK Điều Khiển ENC28J60

You might also like

You are on page 1of 10

Bi 3:-Lp trnh iu khin ENC28J60:

Phn ny c tham kho cc project open source ca nc ngoi, thng tin v ti liu tham
kho s c nu c th cui tut
To cc file enc28j60.c, enc28j60.h v enc28j60conf.h. Add vo project
ENC28J60 c iu khin bi mt tp kh ln cc thanh ghi iu khin, d liu (frame
ehternet gi/nhn) c lu tr trn 1 buffer. Vic c/ghi vo cc thanh ghi iu khin
cng nh buffer d liu c thc hin qua giao tip SPI ti 1 a ch xc nh.
M file enc28j60.h, khai bo a ch cc thanh ghi vo file
Code:
//---------------------------------------------------------------------------// Writen by NTTam - PTITHCM
//---------------------------------------------------------------------------#ifndef ENC28J60_H
#define ENC28J60_H
// ENC28J60 Control Registers
// Control register definitions are a combination of address,
// bank number, and Ethernet/MAC/PHY indicator bits.
// - Register address
(bits 0-4)
// - Bank number
(bits 5-6)
// - MAC/PHY indicator
(bit 7)
#define ADDR_MASK
0x1F
#define BANK_MASK
0x60
#define SPRD_MASK
0x80
// All-bank registers
#define EIE
0x1B
#define EIR
0x1C
#define ESTAT
0x1D
#define ECON2
0x1E
#define ECON1
0x1F
// Bank 0 registers
#define ERDPTL
(0x00|0x00)
#define ERDPTH
(0x01|0x00)
#define EWRPTL
(0x02|0x00)
#define EWRPTH
(0x03|0x00)
#define ETXSTL
(0x04|0x00)
#define ETXSTH
(0x05|0x00)
#define ETXNDL
(0x06|0x00)
#define ETXNDH
(0x07|0x00)
#define ERXSTL
(0x08|0x00)
#define ERXSTH
(0x09|0x00)
#define ERXNDL
(0x0A|0x00)
#define ERXNDH
(0x0B|0x00)
#define ERXRDPTL (0x0C|0x00)
#define ERXRDPTH (0x0D|0x00)
#define ERXWRPTL (0x0E|0x00)
#define ERXWRPTH (0x0F|0x00)
#define EDMASTL
(0x10|0x00)
#define EDMASTH
(0x11|0x00)
#define EDMANDL
(0x12|0x00)
#define EDMANDH
(0x13|0x00)
#define EDMADSTL (0x14|0x00)
#define EDMADSTH (0x15|0x00)
#define EDMACSL
(0x16|0x00)
#define EDMACSH
(0x17|0x00)
// Bank 1 registers
#define EHT0
(0x00|0x20)
#define EHT1
(0x01|0x20)
#define EHT2
(0x02|0x20)

#define EHT3
(0x03|0x20)
#define EHT4
(0x04|0x20)
#define EHT5
(0x05|0x20)
#define EHT6
(0x06|0x20)
#define EHT7
(0x07|0x20)
#define EPMM0
(0x08|0x20)
#define EPMM1
(0x09|0x20)
#define EPMM2
(0x0A|0x20)
#define EPMM3
(0x0B|0x20)
#define EPMM4
(0x0C|0x20)
#define EPMM5
(0x0D|0x20)
#define EPMM6
(0x0E|0x20)
#define EPMM7
(0x0F|0x20)
#define EPMCSL
(0x10|0x20)
#define EPMCSH
(0x11|0x20)
#define EPMOL
(0x14|0x20)
#define EPMOH
(0x15|0x20)
#define EWOLIE
(0x16|0x20)
#define EWOLIR
(0x17|0x20)
#define ERXFCON
(0x18|0x20)
#define EPKTCNT
(0x19|0x20)
// Bank 2 registers
#define MACON1
(0x00|0x40|0x80)
#define MACON2
(0x01|0x40|0x80)
#define MACON3
(0x02|0x40|0x80)
#define MACON4
(0x03|0x40|0x80)
#define MABBIPG
(0x04|0x40|0x80)
#define MAIPGL
(0x06|0x40|0x80)
#define MAIPGH
(0x07|0x40|0x80)
#define MACLCON1 (0x08|0x40|0x80)
#define MACLCON2 (0x09|0x40|0x80)
#define MAMXFLL
(0x0A|0x40|0x80)
#define MAMXFLH
(0x0B|0x40|0x80)
#define MAPHSUP
(0x0D|0x40|0x80)
#define MICON
(0x11|0x40|0x80)
#define MICMD
(0x12|0x40|0x80)
#define MIREGADR (0x14|0x40|0x80)
#define MIWRL
(0x16|0x40|0x80)
#define MIWRH
(0x17|0x40|0x80)
#define MIRDL
(0x18|0x40|0x80)
#define MIRDH
(0x19|0x40|0x80)
// Bank 3 registers
#define MAADR1
(0x00|0x60|0x80)
#define MAADR0
(0x01|0x60|0x80)
#define MAADR3
(0x02|0x60|0x80)
#define MAADR2
(0x03|0x60|0x80)
#define MAADR5
(0x04|0x60|0x80)
#define MAADR4
(0x05|0x60|0x80)
#define EBSTSD
(0x06|0x60)
#define EBSTCON
(0x07|0x60)
#define EBSTCSL
(0x08|0x60)
#define EBSTCSH
(0x09|0x60)
#define MISTAT
(0x0A|0x60|0x80)
#define EREVID
(0x12|0x60)
#define ECOCON
(0x15|0x60)
#define EFLOCON
(0x17|0x60)
#define EPAUSL
(0x18|0x60)
#define EPAUSH
(0x19|0x60)
// PHY registers
#define PHCON1
0x00
#define PHSTAT1
0x01

#define
#define
#define
#define
#define
#define
#define

PHHID1
PHHID2
PHCON2
PHSTAT2
PHIE
PHIR
PHLCON

0x02
0x03
0x10
0x11
0x12
0x13
0x14

// ENC28J60 EIE Register Bit Definitions


#define EIE_INTIE
0x80
#define EIE_PKTIE
0x40
#define EIE_DMAIE
0x20
#define EIE_LINKIE
0x10
#define EIE_TXIE
0x08
#define EIE_WOLIE
0x04
#define EIE_TXERIE
0x02
#define EIE_RXERIE
0x01
// ENC28J60 EIR Register Bit Definitions
#define EIR_PKTIF
0x40
#define EIR_DMAIF
0x20
#define EIR_LINKIF
0x10
#define EIR_TXIF
0x08
#define EIR_WOLIF
0x04
#define EIR_TXERIF
0x02
#define EIR_RXERIF
0x01
// ENC28J60 ESTAT Register Bit Definitions
#define ESTAT_INT
0x80
#define ESTAT_LATECOL
0x10
#define ESTAT_RXBUSY
0x04
#define ESTAT_TXABRT
0x02
#define ESTAT_CLKRDY
0x01
// ENC28J60 ECON2 Register Bit Definitions
#define ECON2_AUTOINC
0x80
#define ECON2_PKTDEC
0x40
#define ECON2_PWRSV
0x20
#define ECON2_VRPS
0x08
// ENC28J60 ECON1 Register Bit Definitions
#define ECON1_TXRST
0x80
#define ECON1_RXRST
0x40
#define ECON1_DMAST
0x20
#define ECON1_CSUMEN
0x10
#define ECON1_TXRTS
0x08
#define ECON1_RXEN
0x04
#define ECON1_BSEL1
0x02
#define ECON1_BSEL0
0x01
// ENC28J60 MACON1 Register Bit Definitions
#define MACON1_LOOPBK
0x10
#define MACON1_TXPAUS
0x08
#define MACON1_RXPAUS
0x04
#define MACON1_PASSALL
0x02
#define MACON1_MARXEN
0x01
// ENC28J60 MACON2 Register Bit Definitions
#define MACON2_MARST
0x80
#define MACON2_RNDRST
0x40
#define MACON2_MARXRST
0x08
#define MACON2_RFUNRST
0x04
#define MACON2_MATXRST
0x02
#define MACON2_TFUNRST
0x01
// ENC28J60 MACON3 Register Bit Definitions
#define MACON3_PADCFG2
0x80
#define MACON3_PADCFG1
0x40

#define MACON3_PADCFG0
0x20
#define MACON3_TXCRCEN
0x10
#define MACON3_PHDRLEN
0x08
#define MACON3_HFRMLEN
0x04
#define MACON3_FRMLNEN
0x02
#define MACON3_FULDPX
0x01
// ENC28J60 MICMD Register Bit Definitions
#define MICMD_MIISCAN
0x02
#define MICMD_MIIRD
0x01
// ENC28J60 MISTAT Register Bit Definitions
#define MISTAT_NVALID
0x04
#define MISTAT_SCAN
0x02
#define MISTAT_BUSY
0x01
// ENC28J60 PHY PHCON1 Register Bit Definitions
#define PHCON1_PRST
0x8000
#define PHCON1_PLOOPBK
0x4000
#define PHCON1_PPWRSV
0x0800
#define PHCON1_PDPXMD
0x0100
// ENC28J60 PHY PHSTAT1 Register Bit Definitions
#define PHSTAT1_PFDPX
0x1000
#define PHSTAT1_PHDPX
0x0800
#define PHSTAT1_LLSTAT
0x0004
#define PHSTAT1_JBSTAT
0x0002
// ENC28J60 PHY PHCON2 Register Bit Definitions
#define PHCON2_FRCLINK
0x4000
#define PHCON2_TXDIS
0x2000
#define PHCON2_JABBER
0x0400
#define PHCON2_HDLDIS
0x0100
// ENC28J60 Packet Control Byte Bit Definitions
#define PKTCTRL_PHUGEEN 0x08
#define PKTCTRL_PPADEN
0x04
#define PKTCTRL_PCRCEN
0x02
#define PKTCTRL_POVERRIDE 0x01
#endif //ENC28J60_H
//----------------------------------------------------------------------------

Khi giao tip vi ENC28J60 qua SPI, ngoi a ch th cn c Operating code iu khin thao
tc c/ghi/
Thm nh ngha cc code ny vo file trn (trn dng #endif //ENC28J60_H nh)
Code:
// SPI operation codes
#define ENC28J60_READ_CTRL_REG
#define ENC28J60_READ_BUF_MEM
#define ENC28J60_WRITE_CTRL_REG
#define ENC28J60_WRITE_BUF_MEM
#define ENC28J60_BIT_FIELD_SET
#define ENC28J60_BIT_FIELD_CLR
#define ENC28J60_SOFT_RESET

0x00
0x3A
0x40
0x7A
0x80
0xA0
0xFF

Khai bo a ch bt u v kt thc buffer d liu gi v nhn trn ENC28J60:


Code:
#define TXSTART_INIT
#define TXSTOP_INIT
#define RXSTART_INIT

0x0000 //Dia chi bat dau buffer gui


0x05FF //Dia chi ket thuc buffer gui
0x0600 //Dia chi bat dau buffer nhan

#define RXSTOP_INIT

0x1FFF //Dia chi ket thuc buffer nhan

//Khai bao kich thuoc frame ethernet max va min


#define MAX_FRAMELEN
1518
#define ETHERNET_MIN_PACKET_LENGTH
0x3C

Tip theo, m file enc28j60conf.h, thm vo cc khai bo v IO port s dng iu khin


ENC28J60 v mt s thng tin cu hnh khc (a ch MAC)
Code:
//---------------------------------------------------------------------------// Writen by NTTam - PTITHCM
//---------------------------------------------------------------------------#ifndef ENC28J60CONF_H
#define ENC28J60CONF_H
//
//Khai bao cac chan IO cho ENC28J60
#define ENC28J60_CONTROL_DDR
DDRB
#define ENC28J60_CONTROL_PORT
PORTB
#define ENC28J60_SPI_DDR
DDRB
#define ENC28J60_SPI_PORT
PORTB
//
#define ENC28J60_CONTROL_CS
3
#define ENC28J60_CONTROL_RESET
4
#define ENC28J60_SPI_SCK
7
#define ENC28J60_SPI_MISO
6
#define ENC28J60_SPI_MOSI
5
#define ENC28J60_SPI_SS
4
#define ENC28J60_SPI_CS
3
//
//Dinh nghia macro chon chip ENC28J60
#define ENC28J60_CS_LO()
ENC28J60_CONTROL_PORT &=
~(1<<ENC28J60_CONTROL_CS);
#define ENC28J60_CS_HI()
ENC28J60_CONTROL_PORT |=
(1<<ENC28J60_CONTROL_CS);
//
#define ETH_INTERRUPT
INT2_vect
//
#if defined (__AVR_ATmega32__)
#define ETH_INT_ENABLE
GICR |= (1<<INT2)
#define ETH_INT_DISABLE GICR &= ~(1<<INT2)
#endif
#if defined (__AVR_ATmega644__) || defined (__AVR_ATmega644P__)
#define ETH_INT_ENABLE
EIMSK |= (1<<INT2)
#define ETH_INT_DISABLE EIMSK &= ~(1<<INT2)
#endif
// MAC address for this interface
#ifdef ETHADDR0
#define ENC28J60_MAC0 ETHADDR0
#define ENC28J60_MAC1 ETHADDR1
#define ENC28J60_MAC2 ETHADDR2
#define ENC28J60_MAC3 ETHADDR3
#define ENC28J60_MAC4 ETHADDR4
#define ENC28J60_MAC5 ETHADDR5
#else
#define ENC28J60_MAC0 '0'
#define ENC28J60_MAC1 'F'
#define ENC28J60_MAC2 'F'

#define ENC28J60_MAC3 'I'


#define ENC28J60_MAC4 'C'
#define ENC28J60_MAC5 'E'
#endif
#endif // ENC28J60CONF_H
//----------------------------------------------------------------------------

Trc ht ta cn 1 hm c v 1 hm ghi d liu vo 1 a ch xc nh trn ENC28J60 qua


SPI:
M file enc28j60.c, thm vo cc hm (sau nh thm khai bo hm vo file header
enc28j60.h na nh)
Code:
//---------------------------------------------------------------------------// Writen by NTTam - PTITHCM
//---------------------------------------------------------------------------#include <avr/io.h>
#include "ntAVRnet.h"
#include "enc28j60.h"
#include "enc28j60conf.h"
#include <avr/pgmspace.h>
//---------------------------------------------------------------------------unsigned char enc28j60SPIRead(unsigned char op, unsigned char address)
{
unsigned char res;
ENC28J60_CS_LO();
SPDR = op | (address & ADDR_MASK);
while(!(SPSR & (1<<SPIF)));
SPDR = 0x00;
while(!(SPSR & (1<<SPIF)));
if(address & 0x80){
SPDR = 0x00;
while(!((SPSR) & (1<<SPIF)));
}
res = SPDR;
ENC28J60_CS_HI();
return res;
}
void enc28j60SPIWrite(unsigned char op, unsigned char address, unsigned char data)
{
ENC28J60_CS_LO();
SPDR = op | (address & ADDR_MASK);
while(!(SPSR & (1<<SPIF)));
SPDR = data;
while(!(SPSR & (1<<SPIF)));
ENC28J60_CS_HI();
}

ENC28J60 chia tp thanh ghi thnh cc bank, ta vit 1 hm set bank thanh ghi:
Trc ht ta khai bo 1 bin kiu char lu bank hin ti (thm vo u file):
Code:
unsigned char Enc28j60Bank;

V vit hm:

Code:
void enc28j60SetBank(unsigned char address)
{
if((address & BANK_MASK) != Enc28j60Bank)
{
enc28j60SPIWrite(ENC28J60_BIT_FIELD_CLR, ECON1, (ECON1_BSEL1|
ECON1_BSEL0));
enc28j60SPIWrite(ENC28J60_BIT_FIELD_SET, ECON1, (address &
BANK_MASK)>>5);
Enc28j60Bank = (address & BANK_MASK);
}
}

phc v cho vic c v ghi buffer d liu, ta vit tip 2 hm c v ghi buffer:
Code:
void enc28j60ReadBuffer(unsigned int len, unsigned char* data)
{
ENC28J60_CS_LO();
SPDR = ENC28J60_READ_BUF_MEM;
while(!(SPSR & (1<<SPIF)));
while(len--)
{
SPDR = 0x00;
while(!(SPSR & (1<<SPIF)));
*data++ = SPDR;
}
ENC28J60_CS_HI();
}
void enc28j60WriteBuffer(unsigned int len, unsigned char* data)
{
ENC28J60_CS_LO();
SPDR = ENC28J60_WRITE_BUF_MEM;
while(!(SPSR & (1<<SPIF)));
while(len--)
{
SPDR = *data++;
while(!(SPSR & (1<<SPIF)));
}
ENC28J60_CS_HI();
}

Da vo c s cc hm ny, ta xy dng cc hm c ghi thanh ghi iu khin, thanh ghi


PHY ca ENC28J60:
Code:
unsigned char enc28j60Read(unsigned char address)
{
enc28j60SetBank(address);
return enc28j60SPIRead(ENC28J60_READ_CTRL_REG, address);
}
void enc28j60Write(unsigned char address, unsigned char data)
{
enc28j60SetBank(address);
enc28j60SPIWrite(ENC28J60_WRITE_CTRL_REG, address, data);
}
unsigned int enc28j60PhyRead(unsigned char address)

{
unsigned int data;
enc28j60Write(MIREGADR, address);
enc28j60Write(MICMD, MICMD_MIIRD);
while(enc28j60Read(MISTAT) & MISTAT_BUSY);
enc28j60Write(MICMD, 0x00);
data = enc28j60Read(MIRDL);
data |= enc28j60Read(MIRDH);
return data;
}
void enc28j60PhyWrite(unsigned char address, unsigned int data)
{
enc28j60Write(MIREGADR, address);
enc28j60Write(MIWRL, data);
enc28j60Write(MIWRH, data>>8);
while(enc28j60Read(MISTAT) & MISTAT_BUSY);
}

Cc hm trn cho php truy xut y vo tp thanh ghi ca ENC28J60


Tip theo ta vit tip 2 hm gi v nhn 1 gi tin:
Code:
void enc28j60PacketSend(unsigned int len, unsigned char* packet)
{
enc28j60Write(EWRPTL, TXSTART_INIT);
enc28j60Write(EWRPTH, TXSTART_INIT>>8);
enc28j60Write(ETXNDL, (TXSTART_INIT+len));
enc28j60Write(ETXNDH, (TXSTART_INIT+len)>>8);
enc28j60SPIWrite(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
enc28j60WriteBuffer(len, packet);
enc28j60SPIWrite(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}
unsigned int NextPacketPtr;
unsigned int enc28j60PacketReceive(unsigned int maxlen, unsigned char* packet)
{
unsigned int rxstat;
unsigned int len;
if( !enc28j60Read(EPKTCNT) )
return 0;
enc28j60Write(ERDPTL, (NextPacketPtr));
enc28j60Write(ERDPTH, (NextPacketPtr)>>8);
NextPacketPtr = enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0);
NextPacketPtr |= ((unsigned int)enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0))<<8;
len = enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0);
len |= ((unsigned int)enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0))<<8;
rxstat = enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0);
rxstat |= ((unsigned int)enc28j60SPIRead(ENC28J60_READ_BUF_MEM, 0))<<8;
len = ((len<maxlen)?(len) : (maxlen));
enc28j60ReadBuffer(len, packet);
enc28j60Write(ERXRDPTL, (NextPacketPtr));
enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);
enc28j60SPIWrite(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
return len;
}

V hm khi ng IC ENC28J60:
c th nhanh chng truy xut thng tin cu hnh khi ng cho ENC28J60, ta lu ton b
thng tin ny vo mt array trong b nh flash cha a ch cc thanh ghi v gi tr khi to
tng ng, khai bo array ny trong file enc28j60.c:
Code:
prog_char enc28j60_config[44] PROGMEM = {
ETXSTL, LO8(TXSTART_INIT), //start lo
ETXSTH, HI8(TXSTART_INIT), //start hi
ETXNDL, LO8(TXSTOP_INIT ), //end lo
ETXNDH, HI8(TXSTOP_INIT ), //end hi
ERXSTL,
ERXSTH,
ERXNDL,
ERXNDH,

LO8(RXSTART_INIT), //start lo
HI8(RXSTART_INIT), //start hi
LO8(RXSTOP_INIT ), //end lo
HI8(RXSTOP_INIT ), //end hi

MACON2, 0x00,
MACON1, (MACON1_MARXEN | MACON1_RXPAUS | MACON1_TXPAUS),
MACON3, ( MACON3_PADCFG0 | MACON3_TXCRCEN | MACON3_FRMLNEN),
MAMXFLL, LO8(1518),
MAMXFLH, HI8(1518),
MABBIPG, 0x12, //half duplex
MAIPGL, 0x12,
MAIPGH, 0x0C, //half duplex
MAADR5,
MAADR4,
MAADR3,
MAADR2,
MAADR1,
MAADR0,
};

ENC28J60_MAC0,
ENC28J60_MAC1,
ENC28J60_MAC2,
ENC28J60_MAC3,
ENC28J60_MAC4,
ENC28J60_MAC5

Trong hm ny, ta s cn hm delay_us delay


Code:
void delay_us(unsigned short time_us)
{
unsigned short delay_loops;
register unsigned short i;
delay_loops = (time_us+3)/5*CYCLES_PER_US; // +3 for rounding up (dirty)
// one loop takes 5 cpu cycles
for (i=0; i < delay_loops; i++) {};
}

Trong hm delay trn ta cn 1 thng tin l s chu k my ca CPU/micro giy, do ta cn


thm cc thng tin ny vo file cu hnh chung:
M file ntAVRnet.h, thm vo cc define sau:
Code:
//---------------------------------------------------------------------------// Writen by NTTam - PTITHCM
//---------------------------------------------------------------------------#ifndef NTAVRNET_H
#define NTAVRNET_H

#ifndef F_CPU
#define F_CPU
12000000
// Cho toc do la 12MHz
#endif //F_CPU
#define CYCLES_PER_US ((F_CPU+500000)/1000000)
//So chu ky lenh trong 1 micro giay
#define LO8(x) ((x)&0xFF)
#define HI8(x) (((x)>>8)&0xFF)
#endif //NTAVRNET_H

V y l hm khi ng ENC28J60:
Code:
void enc28j60Init(void)
{
unsigned char i;
unsigned int timeout=0;
Enc28j60Bank = 0xFF;
ENC28J60_CONTROL_DDR |= (1<<ENC28J60_CONTROL_CS);
ENC28J60_CS_HI();
ENC28J60_SPI_PORT |= (1<<ENC28J60_SPI_SCK); //sck = hi
ENC28J60_SPI_DDR |= (1<<ENC28J60_SPI_SS)|(1<<ENC28J60_SPI_MOSI)|
(1<<ENC28J60_SPI_SCK); //SS,MOSI,SCK = OUT
ENC28J60_SPI_DDR &= ~(1<<ENC28J60_SPI_MISO); //MISO = IN
SPCR = (0<<SPIE)|(1<<SPE)|(0<<DORD)|(1<<MSTR)|(0<<CPOL)|(0<<CPHA)|(0<<SPR1)|
(0<<SPR0);
SPSR = (1<<SPI2X);
delay_us(65000);delay_us(65000);delay_us(65000);
enc28j60SPIWrite(ENC28J60_SOFT_RESET,0, ENC28J60_SOFT_RESET);
delay_us(65000);delay_us(65000);delay_us(65000);
while((!(enc28j60Read(ESTAT) & 0x01)) && (timeout<65000)){timeout++;};
if(timeout>=65000){timeout=0;}
NextPacketPtr = RXSTART_INIT;
for(i=0; i<2*22; i+=2){

enc28j60Write(pgm_read_byte(&enc28j60_config[i+0]),pgm_read_byte(&enc28j60_config[i+1]
));
}
enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS); //=no loopback of transmitted frames
enc28j60SPIWrite(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE);
enc28j60SPIWrite(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);
enc28j60PhyWrite(PHLCON, 0x347A);
}

Cui cng, nh thm phn khai bo tt c cc hm

You might also like