STM32F1 ile Basit Ethernet Haberleşmesi

Başlatan Mucit23, 14 Şubat 2021, 15:48:55

Mucit23

Selamlar

Aynı ağ içerisinde bulunan bir Bilgisayar ile STM32F103 çipimi Basit bir şekilde Ethernet Haberleşmesi yaptırmak istiyorum. Amacım Max 1kb'ı geçmeyecek paketler halinde çift yönlü durum verileri göndermek.

Bilgisayar tarafını C# ile kendim yazacağım. Bu tarafta da uygulama yapmışlığım yok. 

STM32 Tarafında ise önerilere ihtiyacım var. Daha önce Ethernet üzerine ufak bir uygulama bile yapmışlığım yok bu yüzden ne tür bir uygulama yapılması gerektiğini bilmiyorum. Donanım olarak W5500 gibi SPI ile haberleşebileceğim bir çip ile haberleşmeyi kurmak istiyorum. Elimde yok ama pazartesi temin edeceğim.

Biraz araştırdım. Muhtemelen STM32 Çipime Sabit IP verebileceğim bir arayüz yazmam gerekecek.  PC tarafında Bu belirlediğim IP'ye bağlanıp veri alışverişi yapmam lazım.

Bunun için kullandığınız düzgün çalışan bir örnek kütüphane varmıdır?

Cemre.

#1
Merhaba,

W5500 SPI için Wiznet'in yayınladığı ioLibrary kütüphanesi ile ilk adımı atmak mümkün. Çok karmaşık bir yapısı yok zaten ben de çok basit bir şeyler denemek için aşağıdaki modülü almıştım.
https://www.robotistan.com/spidan-ethernettcpip-donusturucu-w5500-usr-es1

Eğer bare-metal çalışmak isterseniz STM32F030K6 ile yaptığım uygulamayı da paylaşabilirim. Basit bir TCP listener programına bir string gönderme işlemi gerçekleştirmiştim.

Bir de aliexpress'te satılan UART ile transparan veri aktarımına izin veren modüller mevcut, sanırım kullanımı çok daha kolay olur ama bizzat denemedim...
https://tr.aliexpress.com/i/33016001715.html

Mucit23

Bare-Metal ne demek bilmiyorum Ama paylaşırsanız sevinirim. Yada ioLibrary kütüphanesinin Herhangi bir STM32F çipine Nasıl port edileceğini anlatırsanız bence çok daha iyi olur. Şuanda ioLibrary kütüphanesini indirdim. İlk Hedefim sizin yaptığınız gibi PC'den STM32'ye ve STM32'den PC'e TCP paketleri yollamak. Bu uygulama benim ihtiyaçlarımın %90'ını karşılıyor.

ioLibrary Kütüphanesinde TCP uygulaması için hangi dosyalar projeye eklenmeli? SPI rutinleri nasıl yazılmalı vs gibi konular soru işaret şuanda.

mufitsozen

AN5413 Getting started with the SPC58x Networking
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

Cemre.

#4
Tekrar merhaba,

Bare-metal'den kastım, üreticinin sunduğu kütüphane ve API'ları kullanmadan kendi rutinlerinizi kendiniz yazarak çalışmaktı.

ioLibrary'nin port edilmesi ile ilgili olarak;
Ben denemelerimi STM32 HAL kütüphanesini kullanarak gerçekleştirmiştim. Bu durumda aşağıdaki fonksiyonları tanımlamak yeterli olmuş.
/* USER CODE BEGIN 4 */
void WIZchip_Init(void) {

  csDisable();
  reg_wizchip_spi_cbfunc(spiReadByte, spiWriteByte);
  reg_wizchip_cs_cbfunc(csEnable, csDisable);

  uint8_t tmp;
  //w5500, w5200
  #if _WIZCHIP_ >= W5200
    uint8_t memsize[2][8] = { {2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
  #else
    uint8_t memsize[2][4] = { {2,2,2,2},{2,2,2,2}};
  #endif

  if (ctlwizchip(CW_INIT_WIZCHIP, (void * ) memsize) == -1) {
    //myprintf("WIZCHIP Initialized fail.\r\n");
    //printf("WIZCHIP Initialized fail.\r\n", 1, 10);
    return;
  }
  /* PHY link status check */
  do {
    if (ctlwizchip(CW_GET_PHYLINK, (void * ) & tmp) == -1) {
      //printf("Unknown PHY Link status.\r\n", 1, 10);
      return;
    }
  } while (tmp == PHY_LINK_OFF);
};

void csEnable(void) {
  HAL_GPIO_WritePin(WIZCHIP_CS_PORT, WIZCHIP_CS_PIN, GPIO_PIN_RESET);
};

void csDisable(void) {
  HAL_GPIO_WritePin(WIZCHIP_CS_PORT, WIZCHIP_CS_PIN, GPIO_PIN_SET);
};

void spiWriteByte(uint8_t tx) {
  uint8_t rx;
  HAL_SPI_TransmitReceive( & WIZCHIP_SPI, & tx, & rx, 1, 10);
};

uint8_t spiReadByte(void) {
  uint8_t rx = 0, tx = 0xFF;
  HAL_SPI_TransmitReceive( & WIZCHIP_SPI, & tx, & rx, 1, 10);
  return rx;
};
/* USER CODE END 4 */

Dahil edilen kütüphane header dosyaları şu şekilde;
/* USER CODE BEGIN Includes */
#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_VDM_
#include "socket.h"
#include "dhcp.h"
#include "dns.h"
/* USER CODE END Includes */

Diğer gerekli tanımlamaları da şu şekilde yapmışım;
/* USER CODE BEGIN PD */
#define WIZCHIP_CS_PORT W5500_CS_GPIO_Port
#define WIZCHIP_CS_PIN W5500_CS_Pin
#define WIZCHIP_SPI hspi1
#define DEBUG_UART huart1
/* USER CODE END PD */

Projeyi şuradan indirip denemeleri gerçekleştirebilirsiniz.
https://yadi.sk/d/HbKE2D51jdi5WA

Unutmadan, bu projede STM32 ve W5500, PC'deki TCP Server'a bağlanan bir Client rolündedir. Normal şartlarda bahsettiğiniz gibi bir uygulama için ben STM32 ve W5500'ün TCP Server olarak çalışmasını ve gelen Client taleplerine (bunlar özel bir komut yapısı ile ya da daha bilindik Modbus TCP gibi protokoller ile olabilir) cevap verecek bir yapıda kurgulamayı tercih ederdim.

Mucit23

#5
@Cemre.

Örnek uygulama için Teşekkürler. Biraz geç oldu kusura bakma yazamadım. Şimdi bi tane W5500 modülü sipariş ettim. STM32F103C8 Deneme yapacağım.

Fakat kütüphaneyi port etme işini öğrenmem lazım. ioLibrary'i kendi sitesinden indirdim. Sizin örnek uygulamaya bakarak kendi Projeme dahil ettim. Gerekli tanımlamaları, Path'ları vs yaptım.

SPI init ve Haberleşme rutinlerini ekledim. Bu fonksiyonları ioLibrary'e bildiriyorum. Ben Std kütüphane ile yapmak istedim. Şuanda Main kodum bu şekilde
/*******************************************************************************
  * @file   
  * @author  Ferhat YOL
  * @version V1.0.0
  * @date    
  * @brief   Main program body
  ******************************************************************************
  * @attention
  ******************************************************************************
  */
  /* Includes -----------------------------------------------------------------*/
	#include "main.h"
	#include "systick.h"
  #include "Usart.h"
	
	
	#define _WIZCHIP_IO_MODE_	_WIZCHIP_IO_MODE_SPI_VDM_
	#include "socket.h"
	#include "dhcp.h"
	#include "dns.h"
/* Private typedef -----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/

	uint8_t temp;

	const uint8_t serverIP[] = {192, 168, 0, 100};
	const uint16_t serverPORT = 10000;

	wiz_NetInfo defaultNetInfo = {.mac = {0x00,0x08,0xdc,0xff,0xee,0xdd},
																.ip = {192,168,0,130},
																.sn = {255,255,255,0},
																.gw = {192,168,0,254},
																//.dns = {168, 126, 63, 1},
																.dns = {8, 8, 8, 8},
																.dhcp = NETINFO_STATIC};

	char buffer[255];
															
/* Bits definitions ----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

int main(void)
{ 
	SystemInit();
	SysTick_init();
	GPIO_Configuration();	
	USART1_Configuration();
  WIZnet_W5500_Init();
	
	while(1)
  { 	
		
    delay_ms(100);		
	}
}



void SPI_Configuration(void)
{
	  GPIO_InitTypeDef GPIO_InitStructure;  
    SPI_InitTypeDef   SPI_InitStructure;

	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
	
   /* Configure SPI1 pins: SCK, MISO and MOSI */
   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
   GPIO_Init(GPIOA, &GPIO_InitStructure);

	  /* SPI1 Config -------------------------------------------------------------*/
	  SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
	  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
	  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	  SPI_InitStructure.SPI_CRCPolynomial = 7;
	  SPI_Init(SPI1, &SPI_InitStructure);

		SPI_Cmd(SPI1, ENABLE);
}

void csEnable(void)
{
	 GPIO_ResetBits(W5500_CS_GPIO_Port, W5500_CS_Pin);
};

void csDisable(void)
{
	 GPIO_SetBits(W5500_CS_GPIO_Port, W5500_CS_Pin);	 
};

void spiWriteByte(uint8_t tx)
{
	uint8_t rx;
	
};

uint8_t spiReadByte(void)
{
	uint8_t rx = 0, tx = 0xFF;
	//HAL_SPI_TransmitReceive(&WIZCHIP_SPI, &tx, &rx, 1, 10);
	return rx;
};

void WIZnet_W5500_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  uint8_t tmp;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

  GPIO_InitStruct.GPIO_Pin = W5500_CS_Pin;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(W5500_CS_GPIO_Port, &GPIO_InitStruct);
	
	SPI_Configuration();
	
	csDisable();
	reg_wizchip_spi_cbfunc(spiReadByte, spiWriteByte);
	reg_wizchip_cs_cbfunc(csEnable, csDisable);
	
		//w5500, w5200
	#if _WIZCHIP_ >= W5200
		uint8_t memsize[2][8] = { {2,2,2,2,2,2,2,2},{2,2,2,2,2,2,2,2}};
	#else
		uint8_t memsize[2][4] = { {2,2,2,2},{2,2,2,2}};
	#endif
		
	if(ctlwizchip(CW_INIT_WIZCHIP,(void*)memsize) == -1)
	{
		//myprintf("WIZCHIP Initialized fail.\r\n");
		//printf("WIZCHIP Initialized fail.\r\n", 1, 10);
	  return;
	}
	/* PHY link status check */
	do {
		if(ctlwizchip(CW_GET_PHYLINK, (void*)&tmp) == -1)
		{
			//printf("Unknown PHY Link status.\r\n", 1, 10);
		  return;
		}
	} while (tmp == PHY_LINK_OFF);
	
}

void GPIO_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(GPIOC, &GPIO_InitStruct);
	
}



#ifdef USE_FULL_ASSERT
/*******************************************************************************
* Function Name  : assert_failed
* Description    : Reports the name of the source file and the source line number
*                  where the assert_param error has occurred.
* Input          : - file: pointer to the source file name
*                  - line: assert_param error line source number
* Output         : None
* Return         : None
*******************************************************************************/
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {}
}
#endif

Kodu derlerken linker aşamasında aşağıdaki hataları alıyorum.

Build started: Project: Software
*** Using Compiler 'V5.06 update 6 (build 750)', folder: 'C:\Keil_v5\ARM\ARMCC\Bin'
Build target 'Ethernet Software'
compiling main.c...
..\ioLibrary\Ethernet\wizchip_conf.h(116): warning:  #47-D: incompatible redefinition of macro "_WIZCHIP_IO_MODE_"  (declared at line 17 of "..\User\main.c")
  	#define _WIZCHIP_IO_MODE_           _WIZCHIP_IO_MODE_SPI_
..\User\main.c(102): warning:  #177-D: variable "rx"  was declared but never referenced
  	uint8_t rx;
..\User\main.c(108): warning:  #177-D: variable "tx"  was declared but never referenced
  	uint8_t rx = 0, tx = 0xFF;
..\User\main.c: 3 warnings, 0 errors
linking...
.\Objects\Software.axf: Error: L6218E: Undefined symbol WIZCHIP_READ (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol WIZCHIP_READ_BUF (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol WIZCHIP_WRITE (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol WIZCHIP_WRITE_BUF (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol getSn_RX_RSR (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol getSn_TX_FSR (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol wiz_recv_data (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol wiz_recv_ignore (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol wiz_send_data (referred from socket.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol wiz_mdio_read (referred from wizchip_conf.o).
.\Objects\Software.axf: Error: L6218E: Undefined symbol wiz_mdio_write (referred from wizchip_conf.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 11 error messages.
".\Objects\Software.axf" - 11 Error(s), 3 Warning(s).
Target not created.
Build Time Elapsed:  00:00:01

Belirtilen fonksiyonlar ya tanımlı değil yada header'ları yok. Bir yerde hata yaptığımı düşünüyorum. Sizin örnek uygulamayı inceledim biraz. Kütüphane dosyalarında modifikasyon yok gibi görünüyor. Eksik yaptığım nedir acaba?

Amacım TCP Server uygulaması yapmak. PC'den kendi aygıtıma bağlanıp çift yönlü mesaj göndermeyi hedefliyorum. Projeyi yapabilirsem burada Template bir proje yayınlayacağım.


Ekleme: githubdaki orjinal kütüphane dosyaları ile deneme yaptığımda yukarıdaki hatayı alıyorum. Fakat sizin vermiş olduğunuz projedeki kütüphane dosyalarını kullanınca hatasız derleniyor. Şuan Modülümü bekliyorum modül gelsin deneme yapacağım.

Cemre.

#6
https://github.com/Wiznet/ioLibrary_Driver/blob/master/Ethernet/Socket_APIs_V3.0.3.chm
Bu dokümanı indirip Todo List sayfasını inceleyin. Başka özel bir şey olmaması lazım...

Mucit23

#7
Alıntı yapılan: Cemre. - 16 Şubat 2021, 18:03:18https://github.com/Wiznet/ioLibrary_Driver/blob/master/Ethernet/Socket_APIs_V3.0.3.chm
Bu dokümanı indirip Todo List sayfasını inceleyin. Başka özel bir şey olmaması lazım...

@Cemre. Hocam dün sizin kullanmış olduğunuz W5500 Lite Ethernet modülüm Geldi. Hemen STM32F103 kartıma gerekli bağlantıları yapıp sadece Wiznet modülünün init edildiği kodu yükleyip çalıştırdım. Wiznet sorunsuz init edildi ve Evdeki modeme bağladığımda yazılımda vermiş olduğum IP'yi wiznetin aldığını gördüm. IP scanner ile tarama yaptığımda wiznet i görebiliyorum.

Bir sonraki Hedefim TCP Server uygulaması çalıştırmak. PC ile Wiznet Modülüne bağlanıp çift yönlü veri aktarımı yapmayı düşünüyorum.

TCP Web Server uygulamalarına baktığımda Genellikle State Machine ile sırayla bir takım işlemler yapıldığını görüyorum. İlk önce bağlantı isteği var mı yokmu kontrol et, var ise soket aç. Mesaj gönderip al, Soketi kapat gibi sürekli döngüde bu işlemler yapılıyor. İşin mantığını anlamak için aşama aşama kendim yapmak istiyorum. Ama ilk etapta çalışan bir örnek üzerinden gitsem iyi olacak.

Cemre.

Ben çalışmaya bir süre ara verdiğim için tam olarak sonunu getiremedim ancak WizNet ioLibrary BSD Socket API kullanıyor, dokümantasyon yeterli. Aşağıdaki linki inceleyin, kendiniz halledersiniz zaten..

http://wizwiki.net/wiki/doku.php/products:w5500:application:tcp_function

Mucit23

Alıntı yapılan: Cemre. - 18 Şubat 2021, 13:03:10Ben çalışmaya bir süre ara verdiğim için tam olarak sonunu getiremedim ancak WizNet ioLibrary BSD Socket API kullanıyor, dokümantasyon yeterli. Aşağıdaki linki inceleyin, kendiniz halledersiniz zaten..

http://wizwiki.net/wiki/doku.php/products:w5500:application:tcp_function

Selamlar

Bayadır yazamadım işlerden. Dediğiniz gibi dökümana bakarak kendim TCP Server uygulaması yaptım. State Machine mantığını anladım. Şuanda TCP Server cihazıma Hercules Yazılımı ile belirlediğim ip ve port üzerinden bağlanabiliyorum. Bağlandıktan sonra TCP mesajları gönderebiliyorum. Bu kısım aslında benim Ethernet ihtiyaçlarımın %70-80'ını karşılar.

Fakat sistemi iyileştirmek adına birkaç soru sormak istiyorum.

1-) Diyelimki Şu Web Server için çalışan state machine yapısındaki kodları bir RTOS Taskında çalıştıracağız. Rtos bu task için ne kadar vakit ayırması gerekir? Yani Task'daki işlemleri kontrol etmek için ne kadar sıklıkla bu kodları çalıştırmalıyız?
2-) TCP paketi geldiği zaman kesme oluşmasını sağlamak istiyoruz. Bunu nasıl yaparız? W5500 deki int çıkışını bu amaçla kullanmak istesem nasıl bir yol izlemeliyim?
3-) Server'ıma birden fazla Client bağlanmak istese Server ne yapacak?

Açıkçası bunları öğrenmem benim için iyi olacak. Teşekkürler

Cemre.

#10
1) Server'ınızın ne kadar responsive olması gerekiyorsa o kadar sıklıkta. Çok bekletmek de client tarafında timeout'a sebep olacağından abartmamak lazım :)
2) Interrupt üretmesi için ilgili interrupt mask'larını aktif etmek yeterliydi sanırım. Daha sonra int status flagleri okuyarak hangi int geldiğine karar verebiliyorsunuz.
3) Çip 8 tane simultane socket destekliyor, dolayısıyla aynı anda 8 farklı client ile bağlantı kurmak mümkün gözüküyor. Ancak tek bir socket üzerinden birden fazla client'tan gelen pakete cevap vermek mümkün olmalı, yanlış hatırlamıyorsam..

Mucit23

Teşekkürler  :)

İnterrupt Flaglarını bi inceleyeyim. İnt pini'ni istediğim amaca göre konfigüre edersem kesme alabilirim.

Akşam tekrardan sistemi kurup deneme yapacağım. Aynı anda hem telefonumdan hem de bilgisayarımdan bağlanıp veri aktarmayı deneyeceğim.

mufitsozen

Alıntı yapılan: Mucit23 - 03 Mart 2021, 09:22:40Teşekkürler  :)

İnterrupt Flaglarını bi inceleyeyim. İnt pini'ni istediğim amaca göre konfigüre edersem kesme alabilirim.

Akşam tekrardan sistemi kurup deneme yapacağım. Aynı anda hem telefonumdan hem de bilgisayarımdan bağlanıp veri aktarmayı deneyeceğim.

Interrupti niye karistiriyorsun. RTOS var ise task bir semaphore beklesin. ISR icinde semaphoreu set et. Interrupt bitince RTOS o taska switch eder. (Oncelikler dogru belirlendiyse)
Aptalca bir soru yoktur ve hiç kimse soru sormayı bırakana kadar aptal olmaz.

sımışka

Ayrıca bir başlık açmak istemedim. Bu chip ile yaptığınız veya yararlandığınız Modbus Master/Slave örneği var mı ?

Mucit23

#14
Alıntı yapılan: mufitsozen - 04 Mart 2021, 10:48:14Interrupti niye karistiriyorsun. RTOS var ise task bir semaphore beklesin. ISR icinde semaphoreu set et. Interrupt bitince RTOS o taska switch eder. (Oncelikler dogru belirlendiyse)

Müfit Abiyi Rahmetle anıyorum. Emeğin çok forumda.

Başka bir soru sorayım
STM32 + W5500 çipi ile yapılan Ethernet Server uygulamalarında Client sayısını sınırlayan faktörler nelerdir?