WIZnet 5200 (Ethernet, UDP) + STM32


WIZnet 5200 — использование с UDP протоколом
Как то раз, появилась острая необходимость в освоении этого модуля.

С Ethernet прежде никогда не работал, так что для меня это было открытием )
Модуль совсем небольшой по размерам.
Он в 2 раза меньше своего ближайшего родственника на WIZ5100 (известного кучей багов и километровой errat-ой).
(слева — родственник на Wiz5100, справа модуль Wiz820io)

Стоит как я понял, в районе 600 рублей.

В Wiz5200 — 24 регистра общего назначения и 24 регистра управления сокетами.
Смысла перепечатывать даташит не вижу.
Лишь порекомендую взять мой, с уже добавленными в закладки регистрами (и почему этого не сделали разработчики?..)
Чтение, запись

В этой части все оказалось простым и понятным.
Адреса всех регистров состоят из двух байтов. Сперва отсылается старший, затем младший.
Сразу после адреса — идут два байта длины данных(указываем сколько байт хотим записать или принять).
Внимание, в самом первом бите, в старшем байте длины(на диаграмме от подписан OP) необходимо выбрать направление данных — OP в 1 — запись, 0 — чтение.
Запись происходит также (помним про OP бит ))
(кто не поймет, — лог от анализатора, качаем, открываем в Saleae, крутим вертим — пока не станет понятно)

КОД
Пришлось описать регистры

uint8_t  MAC[6] =     {0x64, 0xA2, 0x32, 0x01, 0x02, 0x03}; //MAC Address
uint8_t  IP[4] =      {192, 168, 3, 250};                  //SIPR Source IP Address Register
uint8_t  GateWay[4] = {192, 168, 3, 1};                    //Gateway Address
uint8_t  Mask[4] =    {255, 255, 255, 0};                        //SubnetMask Address
uint8_t  Hard_reg [6]={0x00,0x08,0xDC,0x01,0x02,0x03};     //SHAR Source Hardware Address Register
char Receive_DATA[16];
uint8_t Receive_IP[4];
uint8_t Receive_Port_destination[2];
uint8_t  Receive_byte_size[2];
uint8_t Rec_Flag;
uint16_t size;	
// START ADDRESS REGISTERs
#define Ip_address        0x000F
#define Gateway_address   0x0001
#define Mask_address      0x0006
#define Hard_address      0x0010
#define Sn_MR_address     0x4000  //Socket n-th-th Mode Register [R/W]
#define Sn_CR_address     0x4001  //Socket n-th Command Register [R/W] [0x4001
#define Sn_IR_address     0x4002  //Socket n-th Interrupt Register [R] [0x4002+0x0n00]
#define Sn_SR_address     0x4003  //Socket n-th Status Register [R] [0x4003+0x0n00]
#define Sn_PORT_address   0x4004  //Socket  n-th Source  Port  Register [R/W]  [0x4004
#define Sn_DHAR_address   0x4006  //Socket n-th Destination Hardware Address Register [R/W]
#define Sn_DIPR_address   0x400C  //Socket  n-th Destination  IP  Address  Register R/W][0x400C
#define Sn_DPORT_address  0x4010  //Socket  n-th Destination  Port  Register[R/W][0x4010
#define Sn_MSS_address    0x4012  //Socket  n-th Maximum  Segment  Size  Register [R/W][0x4012
#define Sn_TOS_address    0x4015  //Socket n-th IP Type Of Service Register) [R/W] [0x4015
#define Sn_TTL_address    0x4016  //Socket n-th IP Time To Live Register) [R/W] [0x4016
#define Sn_RXMEM_SIZE_address 0x401E //Socket n-th RX Memory Size Register) [R/W] [0x401E
#define Sn_TXMEM_SIZE_address 0x401F //Socket n-th TX Memory size Register) [R/W][0x401F
#define Sn_TX_FSR_address 0x4020  //Socket  n-th TX  Free  Size  Register)  [R]  [0x4020
#define Sn_TX_RD_address  0x4022  //Socket  n-th TX  Read  Pointer  Register)  [R]  [0x4022
#define Sn_TX_WR_address  0x4024  //Socket  n-th TX  Write  Pointer  Register)  [R/W]  [0x4024
#define Sn_RX_RSR_address 0x4026  //SOCKET  n-th Received  Size  Register)  [R]  [0x4026
#define Sn_RX_RD_address  0x4028  //Socket n-th RX Read Pointer Register) [R/W] [0x4028
#define Sn_RX_WR_address  0x402A //Socket n-th RX Write Pointer Register)[R/W][(0x402A
#define Sn_IMR_address    0x402C  //Socket n-th Interrupt Mask Register)[R/W][0x402C
#define Sn_FRAG_address   0x402D  //Socket n-th Fragment Register)[R/W][0x402D
#define Sn_MR_UPD_aadress 0x4000  //This register sets up socket option or protocol type for each socket
#define Socet_receive     0xC000  // Base adress Socket 0 receive
#define Socket_transmit   0x8000  // Base adress Socket 0 transmit  
#define Sn_CR_Open        0x01
#define Sn_CR_Close       0x10
#define Sn_CR_Send        0x20
#define Sn_CR_Send_Mac    0x21
#define Sn_CR_Recv        0x40
#define Sn_CR_Open        0x01
#define WRITE_1Byte 0x8001
#define Read_1Byte  0x0001

И структуру для данных
struct Config_Msg{
 	uint8_t Mac[6];
	uint8_t Ip[4];
	uint8_t Gaw[4];
	uint8_t Mask[4];
	uint8_t Hard_reg[6];
}; struct Config_Msg Config_Msg;

struct UDR_data{
  uint8_t Receive[32];
	uint8_t Transtir[32];
	uint8_t amount_bit_Receive;
	uint8_t amount_bit_Transiver;
	uint8_t Ip_of_the_parcel[4];
}; struct UDR_data UDR_data;

Весь джентльменский набор для работы с UDP :)
Вывод INT, сообщающий нам о принятых данных, либо отправленных (все маскируется как душе угодно)подсоединен к PE10.
Для обработки используется внешнее прерывание (EXTI10_enabled)
uint8_t Receive_UDR(void);
uint8_t Transmit_UDR(char *DATA_TX, uint8_t size);
void Initialization_wiznet( void );
void Set_Hard_reg(uint8_t  *addr);
void _delay_us (int long  delay);
void Set_GateWay(uint8_t *addr);
void EXTI10_enabled(uint8_t on);
void Write_Word(uint16_t data);
void Write_Byte(uint8_t data);
void Set_Mask(uint8_t *addr);
void Set_IP(uint8_t *addr);
void clear_data_UDR(void);
void Socket_Open  (void);
uint8_t Read_Byte(void);
void close(uint8_t s);

Рабочий пример инициализации

void Initialization_wiznet(void){
        uint8_t i;
        // Mac Address
        for (i = 0 ; i < 6; i++) {Config_Msg.Mac[i] = MAC[i];}
        // IP address
        Config_Msg.Ip[0] = IP[0]; Config_Msg.Ip[1] = IP[1]; Config_Msg.Ip[2] = IP[2]; Config_Msg.Ip[3] = IP[3];
        // GateWay address
        Config_Msg.Gaw[0] = GateWay[0]; Config_Msg.Gaw[1] = GateWay[1]; Config_Msg.Gaw[2] = GateWay[2]; Config_Msg.Gaw[3] = GateWay[3];
        // Subnet Mask address
        Config_Msg.Hard_reg[0] = Hard_reg[0]; Config_Msg.Hard_reg[1] = Hard_reg[1]; Config_Msg.Hard_reg[2] = Hard_reg[2]; Config_Msg.Hard_reg[3] = Hard_reg[3];
        
        Set_GateWay(Config_Msg.Gaw);
        Set_Hard_reg(Config_Msg.Hard_reg);
        Set_IP(Config_Msg.Mac);
        Set_Mask(Config_Msg.Mask);

          nSS_0; 
		    Write_Word(0x0034);
		    Write_Word(WRITE_1Byte);  
		    Write_Byte(0x01); nSS_1; 
		    nSS_1;
		 
		    nSS_0; 
		    Write_Word(0x0016);
		    Write_Word(WRITE_1Byte);  
		    Write_Byte(0x01); nSS_1; 
		    nSS_1;   
}


И 8 жизненно необходимых функций, возложивших на себя весь тяжкий труд

void Set_IP(uint8_t *addr){
      uint8_t i;
	    uint8_t add_ip = Ip_address;
	
	for(i=0;i<4;i++){
      nSS_0; Write_Word(add_ip);
      add_ip++;	
		  Write_Word(WRITE_1Byte);  
		  Write_Byte(Config_Msg.Ip[i]); 
		  nSS_1;
   }
}

void Set_GateWay(uint8_t *addr){
	   uint8_t i;
	   uint8_t add_Ga = Gateway_address;
	
	for(i=0;i<4;i++){
      nSS_0; Write_Word(add_Ga);  
		  add_Ga++;
		  Write_Word(WRITE_1Byte);  
		  Write_Byte(Config_Msg.Gaw[i]); 
		  nSS_1;
  }
}

void Set_Mask(uint8_t *addr){
	   uint8_t i;
	   uint8_t add_Mask = Mask_address;
	
	for(i=0;i<4;i++){
      nSS_0; Write_Word(add_Mask);
      add_Mask++;		
	   	Write_Word(WRITE_1Byte);  
		  Write_Byte(Config_Msg.Mask[i]); 
		  nSS_1;
  }
}
void Set_Hard_reg(uint8_t *addr){
	   uint8_t i;
	   uint8_t add_Hard = Hard_address;
	
	for(i=0;i<6;i++){
      nSS_0; Write_Word(add_Hard);
      add_Hard++;		
	   	Write_Word(WRITE_1Byte);  
		  Write_Byte(Config_Msg.Hard_reg[i]); 
		  nSS_1;
  }
}

void Write_Byte(uint8_t data){
  uint8_t i;
  for (i=0;i<8;i=i+1)
    {
    SCLK_0;
      if(data & 0x80)
         MOSI_1;
	  else
	  MOSI_0;
	  SCLK_1;			
	  data=data<<1;
    }
   SCLK_0;				
}

uint8_t Read_Byte(void){
  uint8_t i; 
	unsigned char spidata = 0;
	
  MOSI_0;
  for (i=0;i<8;i=i+1)
    {
  SCLK_0;
   spidata=spidata<<1;
	if(GPIOE->IDR & GPIO_IDR_IDR13) // SPI_SDI
	 spidata |= 0x01;
  SCLK_1;
    }
  SCLK_0;
	return spidata;
}


void Write_Word(uint16_t data){
      {
	Write_Byte ((data & 0xFF00) >> 8);			
	Write_Byte ((data & 0x00FF));	
      }
}

void Socket_Open(void){
     uint8_t i;
	   
	   nSS_0; Write_Word(Sn_RXMEM_SIZE_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0x01);
		 nSS_1;///////////////////////////////////// RXmem size
	
	   nSS_0; Write_Word(Sn_TXMEM_SIZE_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0x01);
		 nSS_1;  /////////////////////////////////// TXmem size
	 
	   nSS_0; Write_Word(Sn_MR_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0x02); // UDP socket
		 nSS_1;
	   
	   nSS_0; Write_Word(Sn_PORT_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0xAA); // Socket used PORT 0xAAAA
		 nSS_1;
	   // 
	   nSS_0; Write_Word(Sn_PORT_address + 1);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0xAA); // Socket used PORT 0xAAAA
		 nSS_1;
	   
	   nSS_0; Write_Word(Sn_CR_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(Sn_CR_Open); // Socket - Open
		 nSS_1;
	
read:
	   nSS_0; Write_Word(Sn_SR_address);
		 Write_Word(Read_1Byte);  
		 i = Read_Byte(); // Socket - Open
		 nSS_1;
	if(i != 0x22){goto read;} // Bit - not UDR protocol  0x22

		 nSS_0; Write_Word(Sn_IMR_address);
		 Write_Word(WRITE_1Byte);  
		 Write_Byte(0x04); // RX interrupt
		 nSS_1;

		 nSS_0; Write_Word(Sn_IMR_address);
		 Write_Word(Read_1Byte);  
		 Read_Byte(); // RX interrupt
		 nSS_1;
}

Из интересных моментов, стоит обратить внимание на вывод PWDN.
В обычной работе (без сбережений) он должен быть в 0.
По началу долго искал почему не пингуется, оказалось пауза с Reset-ом оказалось слишком короткой (должно быть от 2 us в 0, и minimum 200 ms перед самой работой в 1)
Для демонстрации и оттачивания боевых навыков, был набросан проект в Qt Creator-е.
Его задача — отсылать и принимать данные с указанного порта.
Поморгал светодиодом на борде )

   address = strcmp (Receive_DATA, "LED_on");
	if(address==0){LED1_on;}
	 else { address = strcmp (Receive_DATA, "LED_off"); 
           if(address==0){LED1_off;}}
		if(Rec_Flag==1)
	      { 
	           Receive_UDR(); 
		   Rec_Flag=0;
		   Transmit_UDR(Receive_DATA, 20);
		   Transmit_UDR("Receive - Ok", 20);
             }




Всем дочитавшим до конца — спасибо )
Обращаться можно как обычно vk.com/id100603673
  • +5
  • 15 июня 2013, 16:49
  • khomin

Комментарии (50)

RSS свернуть / развернуть
Исходники как обычно в индусском стиле.
Когда ни будь исправлю )
0
Что значит индусский стиль?
0
У Вас в коде сложно будеть отличить переменную от константы

uint8_t Rec_Flag;
uint16_t size;  

#define Sn_CR_Open        0x01
#define Sn_CR_Close       0x10


как правило на С в дефайнах константы пишут все с большой буквы.

#define Sn_CR_Open        0x01
#define Sn_CR_Close       0x10

#define SN_CR_OPEN 0x01
#define SN_CR_CLOSE 0x10


я не придираюсь, просто совет.

+3
Да хороший совет, я тоже теперь буду так делать.
0
Я так понял, что SPI организуется программно, а что мешало использовать аппаратный интерфейс?
+1
На самом борде, не нашлось свободных аппаратных выводов.
Пришлось выдергивать по ножке, дабы наскрести вообще на SPI )
0
может стоило пересмотреть использование соответствующих выводов, или ремапнуть SPI на другие ноги? Хотя конечно ситуации бывают разные.
0
На Дискавери почти все ножки свободные и выведены наружу.
На этой плате напротив, все занято интерфейсами, флешами и т.д.
0
В реальном устройстве — будет задействован аппаратный SPI!
0
А где модуль брали чет нет ссылки?
+2
В самом WIZnet есть за $20.
0
чето канэчна дороговасто 20usd
модули на enc стоят ~4-5 usd
+1
Мне купили...
Ну вот в ЧИП-Дипе он стоит — 2,1 т.р.
Значит делим на 3, (2,100 /3) = 700 рублей он должен стоить :)
+1
на Алиэкспрессе сам wiz5200 есть за 5$
www.aliexpress.com/item/W5200-Ethernet-control-IC-NEW-ORIGINAL-IN-STOCK/591452786.html
0
Там же цена в US $52.00 с фришипингом((((
0
Так то не дорого… (165 рублей)
Но надо еще трансформатор, разъем, + развести это дело, да что бы заработало )
Проще уж модуль купить :)
0
Разъем с трансформатором меньше $2(от 10 шт.) rutaobao.com/pages/item_detail.aspx?ItemId=5ZmDHAeVdiu5npFaqVga%2bw%3d%3d&Nick=%E9%99%88%E5%BA%84123
Сам покупал.
0
Ну то на то и выйдет )
Если не дороже готового модуля…
0
А что за контора такая и как там с доставкой?
0
Там так то справка есть. )) С доставкой как обычно с нашей почтой. Ну и плюс доставка к ним на склад в Пекине.
0
Чего-то я не понял из ссылки: он выводной или SMD и есть ли на нем сразу светодиоды?
0
Выводной со светодиодами и встроенным трансформатором.
0
спасибо
0
… и нужно брать сразу 10 штук минимум))))
0
Я и взял. У нас всяко дороже.
0
0
ошибся в написании ссылки. смотреть сюда
0
Что-то тихо совсем…
ГК детектет? :D

P.S. WIZnet W5200 (LQFP48 SPI 80MHz chip, Ethernet MAC & PHY with Integrated TCP-IP stack TCP/UDP/ICMP/IPv4/ARP/IGMP/PPPoE) вроде бы может стать народным чипом, заместо ENC28J60. W100 и W300 в слишком больших корпусах — LQFP80 и LQFP100, соответственно.
0
в слишком больших корпусах — LQFP80
В каком месте он большой? Это корпус с малым шагом. Кстати, неплохо самоцентруется при пайке феном.
0
Так он же не LQFP, а в QFN48 только, как я просмотрел! Такой хороший чип, и опять геморой… прийдется довольствоваться W5100 в LQFP80.

$24 SPI breakout module W5100

А Arduino как всегда рулит — $8.5
0
У того же CP2102, куда более мелкий шаг.
Этот — думаю вполне реально припаять феном.
0
только маску желательно иметь для нанесения пасты QFN48
снизу у него еще площадки
0
Это да )
Но стоить заметить, разработчики платы для этого модуля, видимо не испытали особой радости от QFN корпуса, и зафигачили огромные участки без маски, за пределом чипа ))
Наверно так брака меньше )
0
маску желательно иметь для нанесения пасты
Ну, маска давно не проблема. А пасту не обязательно, можно и паяльником бугорки припоя сделать и феном запаять.
0
WIZ5100 (известного кучей багов
Каких? У меня без проблем заработала. Был глюк с приемом из-за быстрой обработки процом STM32. Буфер читался раньше, чем принимался. Вылечил, вставив задержку. Видимо прерывание от W5100 возникает раньше окончания приема.
0
Для быстрой обработки иногда полезно иметь возможность узнать о начале приема, а не об окончании. Видимо там так и реализовано.
0
Может быть. Только я об этом не знал )))
0
может где-то неправильно флаги считывали? или алгоритм не полностью отработали? Там же на одну ногу прерывания целый вагон статусов и флагов. Ну и алгоритмы из даташита для приема и передачи надо реализовывать достаточно точно.
0
Я вывод прерывания не использовал, флаги считывал.
Смутило то, что в дебаггере, пошагово, все нормально работает, а в реале читается количество принятых байт (регистр RSR), равное 0. Задержка между чтением флага и чтением RSR проблему решила.
0
ну тут ничего сказать не могу. У меня нога используется. Хотя с задержками у W5100 действительно есть приколы.
0
в режиме UDP там все еще гораздо более плачевнее… При отправке посылки в момент, когда сокет занят приемом — создается исключительная ситуация и сокет глохнет напрочь… в Errata написано, что решения этой проблемы нет. Никак нельзя предотвратить это… Есть только рекомендация, если это произошло — закрывать и открывать сокет снова… В общем — жуть…
0
а момент, когда идет параллельная работа прием/передача довольно частый. Как вариант, чтобы меньше было зависаний — подключить девайс в режиме полудуплекса. Тогда в момент передачи приниматься ничего не может. Однако при приеме вполне может сложиться ситуация, что вы даете команду «передать». И снова зависон… короче, намаялся я с ними… 5100 в этом случае проигрывает старым 3150. У последних можно следить за ногой, индицирующей прием.
0
Чип хороший, на их сайте есть примеры как раз для TCP и UDP, есть простенькая утилитка для для обмена данными!
Вот только, чтобы припаять этот чип к плате вручную (+фен) приходится помучится, хотя может руки не оттуда растут.
:)
+1
паяется леко при помощи фена и термопасты с заливкой флюсом
ставить разве что только под микроскопом придется
+1
в сети есть какое нибудь видео, для примера, как правильно паять микросхемы такого типа? Какая температура, сколько времени греть феном, какую лучше термопасту использовать?
0
Температура в районе 300-350 (я обычно ставлю 330), греть пока не припаяется (это видно — он дергается).
0
а почему у тебя маска подсети нулевая? для твоего примера должна быть 255 255 255 0
0
Да вообще может быть и 255.255.255.255 (компьютер то один).
Но почему то забыл про нули.
Да оно и так работает )
Сейчас исправлю…
0
это описано в Errata. К сожалению, на этот чип Errata тоже есть)))
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.