-
Notifications
You must be signed in to change notification settings - Fork 15
예제 enet_thermometer
myCortex-LM8962 보드는 이더넷 PHY/MAC 장치를 내장하고 있어 임베디드 이더넷 시스템을 간단하게 구성할 수 있습니다. 이더넷은 구성 요소가 복잡하고 구현해야 할 양이 많은 통신 수단이며 UART나 I2C 처럼 간단한 예제로 설명하기에는 어려움이 있습니다. 하지만 일반적으로 스텍이라 불리우는 소프트웨어 라이브러리를 활용하면 내부 구성을 잘 이해하지 못하더라도 비교적 어렵지 않게 구현할 수 있습니다.
enet_thermometer 예제는 uIP 스텍을 사용하여 간단한 임베디드 웹서버를 구현하고 있습니다. uIP 스텍에는 여러가지 예제 소스가 포함되어 있으며, 본 예제는 uIP에 있는 httpd 예제를 변형하여 myCortex-LM8962 보드에 맞게끔 수정한 것입니다.
본 예제에서는 사용자의 http GET 요청에 하나의 html 문서를 반환하는 예를 구현하고 있습니다. html 문서는 본 예제에 관한 간략한 설명과 함께 현재 MCU 코어 내부의 온도와 시스템 런타임을 담고 있습니다. PC나 스마트폰의 웹브라우저에서 접속하여 html 페이지를 확인할 수 있습니다.
본 예제와 동일한 펌웨어를 이용하여 서버를 만들어 상시 운영중입니다. http://enet_thermometer.withrobot.com 으로 접속하면 위드로봇에서 운영중인 임베디드 웹서버에 접속하여 html 파일을 보실 수 있습니다.
- SysCtl
- GPIO
- ETH
84 line:
//*****************************************************************************
//
// Default TCP/IP Settings for this application.
//
// Default to Link Local address ... (169.254.1.0 to 169.254.254.255). Note:
// This application does not implement the Zeroconf protocol. No ARP query is
// issued to determine if this static IP address is already in use.
//
//*****************************************************************************
#define DEFAULT_IPADDR0 192
#define DEFAULT_IPADDR1 168
#define DEFAULT_IPADDR2 10
#define DEFAULT_IPADDR3 175
#define DEFAULT_NETMASK0 255
#define DEFAULT_NETMASK1 255
#define DEFAULT_NETMASK2 255
#define DEFAULT_NETMASK3 0
myCortex-LM8962 보드가 사용할 IP 주소와 Net mask 값을 지정합니다. 본 예제 소스에서는 192.168.10.175 주소를 사용합니다. 사용자 환경에 따라 이 값을 적절히 변경해야 합니다.
148 line:
//
// Enable and Reset the Ethernet Controller.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_ETH);
SysCtlPeripheralReset(SYSCTL_PERIPH_ETH);
ETH 장치를 enable합니다.
155 line:
//
// Enable Port F for Ethernet LEDs.
// LED0 Bit 3 Output
// LED1 Bit 2 Output
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIODirModeSet(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3, GPIO_DIR_MODE_HW);
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_2 | GPIO_PIN_3,
GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_STD);
myCortex-LM8962 보드의 이더넷 커넥터에는 두 개의 LED가 내장되어 있습니다. 위 코드는 이 LED를 제어하는 용도로 PF2와 PF3 핀을 사용하도록 설정하는 코드입니다.
165 line:
//
// Intialize the Ethernet Controller and disable all Ethernet Controller
// interrupt sources.
//
EthernetIntDisable(ETH_BASE, (ETH_INT_PHY | ETH_INT_MDIO | ETH_INT_RXER |
ETH_INT_RXOF | ETH_INT_TX | ETH_INT_TXER | ETH_INT_RX));
ulTemp = EthernetIntStatus(ETH_BASE, false);
EthernetIntClear(ETH_BASE, ulTemp);
이더넷 장치 설정을 들어가기 앞서 우선 인터럽트를 disable 시키고 현재 set 되어 있는 인터럽트 flag들을 모두 clear 해서 깨끗한 상태에서 설정을 시작합니다.
174 line:
//
// Initialize the Ethernet Controller for operation.
//
EthernetInitExpClk(ETH_BASE, SysCtlClockGet());
//
// Configure the Ethernet Controller for normal operation.
// - Full Duplex
// - TX CRC Auto Generation
// - TX Padding Enabled
//
EthernetConfigSet(ETH_BASE, (ETH_CFG_TX_DPLXEN | ETH_CFG_TX_CRCEN |
ETH_CFG_TX_PADEN));
ETH 장치를 설정합니다.
188 line:
//
// Enable the Ethernet Controller.
//
EthernetEnable(ETH_BASE);
//
// Enable the Ethernet interrupt.
//
IntEnable(INT_ETH);
//
// Enable the Ethernet RX Packet interrupt source.
//
EthernetIntRegister(ETH_BASE, EthernetIntHandler);
EthernetIntEnable(ETH_BASE, ETH_INT_RX);
//
// Enable all processor interrupts.
//
IntMasterEnable();
ETH 장치를 enable 시키고, ETH 인터럽트를 설정합니다. 마지막으로 glabal 인터럽트를 enable 시켜줍니다.
209 line:
//
// Initialize the uIP TCP/IP stack.
//
uip_init();
uip_ipaddr(ipaddr, DEFAULT_IPADDR0, DEFAULT_IPADDR1, DEFAULT_IPADDR2,
DEFAULT_IPADDR3);
uip_sethostaddr(ipaddr);
uip_ipaddr(ipaddr, DEFAULT_NETMASK0, DEFAULT_NETMASK1, DEFAULT_NETMASK2,
DEFAULT_NETMASK3);
uip_setnetmask(ipaddr);
이제 uIP를 설정할 차례입니다. uip_init()부터 시작하여 사용할 IP 주소와 subnet mask를 설정합니다.
220 line:
// 실험목적으로 사용하는 MAC 주소.
// 이 주소는 정식으로 IEEE로 부터 할당받은 주소가 아니다.
// 상용 제품을 출시하는 경우에는 정식 MAC 주소를 할당받아 사용해야 한다.
sTempAddr.addr[0] = 0xb6;
sTempAddr.addr[1] = 0x1a;
sTempAddr.addr[2] = 0x00;
sTempAddr.addr[3] = 0x67;
sTempAddr.addr[4] = 0x45;
sTempAddr.addr[5] = 0x23;
myCortex-LM8962 보드의 이더넷 MAC 주소를 설정합니다. 이 주소는 정식으로 할당받은 주소가 아니므로 상용품을 제작할 때에 사용해서는 안됩니다.
231 line:
//
// Program the hardware with it's MAC address (for filtering).
//
EthernetMACAddrSet(ETH_BASE, (unsigned char *)&sTempAddr);
uip_setethaddr(sTempAddr);
ETH 장치에 MAC 주소를 설정합니다.
237 line:
//
// Initialize the TCP/IP Application (e.g. web server).
//
httpd_init();
httpd 를 초기화 합니다. 이 예제는 ETH -> uIP -> httpd 형태로 라이브러리 스텍(stack)이 구성되어 있습니다.
244 line:
//
// Configure SysTick for a periodic interrupt.
//
SysTickPeriodSet(SysCtlClockGet() / 100); // 100Hz timer
SysTickIntRegister(SysTickIntHandler);
SysTickEnable();
SysTickIntEnable();
주기적으로 ETH 장치에서 들어온 메시지를 확인하기 위해 SysTick 타이머를 설정합니다. 본 예제는 100Hz의 타이머를 이용하여 매 10ms 마다 ETH 메시지를 확인하고 수신된 메시지가 있다면 그에 따를 처리를 하는 형태로 구성되어 있습니다.
254 line:
//
// Configure ADC
// 1초 간격으로 온도 센서를 ADC하여 현재 온도를 전역 변수에 저장해 둔다.
// client에서 http request가 들어오면 이 전역 변수에 저장된 온도 값을
// HTML로 만들어 전송한다.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC);
SysCtlADCSpeedSet(SYSCTL_ADCSPEED_500KSPS);
// 내장 온도 센서 채널만으로 ADC sequence를 구성한다.
ADCSequenceDisable(ADC_BASE, 0);
ADCSequenceConfigure(ADC_BASE, 0, ADC_TRIGGER_TIMER, 0);
ADCSequenceStepConfigure(ADC_BASE, 0, 0, ADC_CTL_TS | ADC_CTL_IE | ADC_CTL_END);
ADCSequenceEnable(ADC_BASE, 0);
ADCIntRegister(ADC_BASE, 0, ADCIntHandler);
ADCIntEnable(ADC_BASE, 0);
//
// Configure Timer to trigger ADC
// 매 1초마다 자동으로 ADC 될 수 있도록 ADC trigger timer를 설정한다.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure( TIMER0_BASE, TIMER_CFG_32_BIT_PER );
TimerControlTrigger(TIMER0_BASE, TIMER_A, true);
TimerLoadSet( TIMER0_BASE, TIMER_A, SysCtlClockGet() / 1 ); // 1Hz trigger
TimerEnable( TIMER0_BASE, TIMER_A );
ADC와 타이머를 초기화합니다. 본 예제에서는 MCU 코어 내장 온도 센서를 읽어 코어 온도를 섭씨단위로 표시하는 html 파일을 전송합니다. 이를 위해 주기적으로 ADC를 수행하고 그 결과를 전역 변수에 저장해 두었다가 클라이언트 접속이 들어오면 가장 최근 온도를 담아 html 문서를 생성합니다.
286 line:
lPeriodicTimer = 0;
lARPTimer = 0;
ETH 장치는 주기적으로 메시지 수신 여부를 확인하여 처리해야 합니다. 이를 위해 전역변수로 타이머 변수를 두고 사용하고 있습니다. 이 변수들은 SysTick 타이머 인터럽트 핸들러에서 시간을 관리합니다.
295 line:
SysCtlSleep(); // SysTick/ADC/ETH 인터럽트가 발생할 때 까지 sleep
인터럽트가 발생할 때 까지 이곳에서 sleep 합니다. 본 예제에서는 3가지 인터럽트가 사용되며 이들 중 어떠한 것이라도 발생하면 sleep에서 깨어납니다.
298 line:
//
// Check for an RX Packet and read it.
//
lPacketLength = EthernetPacketGetNonBlocking(ETH_BASE, uip_buf, sizeof(uip_buf));
수신된 패킷이 있는지 확인하고 있다면 수신한 패킷을 가져옵니다.
이하의 코드들은 uIP의 main control loop에 해당하는 코드입니다.
위 그림과 같이 수신된 패킷을 확인하여 처리하고, 타임아웃을 확인하여 처리하는 형태로 구성되어 있습니다. 코드와 구성에 관한 자세한 내용은 uIP 문서를 참조하시기 바랍니다. 예제 소스 중 enet_thermometer/uip-1.0/doc 폴더에 문서가 포함되어 있습니다.
if(lPacketLength > 0)
{
//
// Set uip_len for uIP stack usage.
//
uip_len = (unsigned short)lPacketLength;
//
// Renable RX Packet interrupts.
//
EthernetIntEnable(ETH_BASE, ETH_INT_RX);
//
// Process incoming IP packets here.
//
if(BUF->type == htons(UIP_ETHTYPE_IP))
{
uip_arp_ipin();
uip_input();
//
// should be sent out on the network, the global variable
// uip_len is set to a value > 0.
//
if(uip_len > 0)
{
uip_arp_out();
EthernetPacketPut(ETH_BASE, uip_buf, uip_len);
uip_len = 0;
}
}
//
// Process incoming ARP packets here.
//
else if(BUF->type == htons(UIP_ETHTYPE_ARP))
{
uip_arp_arpin();
//
// If the above function invocation resulted in data that
// should be sent out on the network, the global variable
// uip_len is set to a value > 0.
//
if(uip_len > 0)
{
EthernetPacketPut(ETH_BASE, uip_buf, uip_len);
uip_len = 0;
}
}
}
//
// Process TCP/IP Periodic Timer here.
//
if(lPeriodicTimer > UIP_PERIODIC_TIMER_MS)
{
lPeriodicTimer = 0;
for(ulTemp = 0; ulTemp < UIP_CONNS; ulTemp++)
{
uip_periodic(ulTemp);
//
// If the above function invocation resulted in data that
// should be sent out on the network, the global variable
// uip_len is set to a value > 0.
//
if(uip_len > 0)
{
uip_arp_out();
EthernetPacketPut(ETH_BASE, uip_buf, uip_len);
uip_len = 0;
}
}
#if UIP_UDP
for(ulTemp = 0; ulTemp < UIP_UDP_CONNS; ulTemp++)
{
uip_udp_periodic(i);
//
// If the above function invocation resulted in data that
// should be sent out on the network, the global variable
// uip_len is set to a value > 0.
//
if(uip_len > 0)
{
uip_arp_out();
EthernetPacketPut(ETH_BASE, uip_buf, uip_len);
uip_len = 0;
}
}
#endif // UIP_UDP
}
//
// Process ARP Timer here.
//
if(lARPTimer > UIP_ARP_TIMER_MS)
{
lARPTimer = 0;
uip_arp_timer();
}
412 line:
//
// SysTick tiemr interrupt handler
//
static void SysTickIntHandler(void)
{
// SysTick timer는 100Hz 주기로 설정되어 있으므로 10ms 간격으로 인터럽트 발생.
lPeriodicTimer += 10;
lARPTimer += 10;
g_tick++;
if (g_tick % 100 == 0) // 1초 경과
{
g_runtime++;
}
}
SysTick 타이머 인터럽트 핸들러입니다. uIP의 타임아웃 이벤트를 위해 lPeriodicTimer와 lARPTimer 변수를 관리합니다. 또한 html 문서에서 보여 줄 런타입 정보를 만들기 위해 g_runtime 변수를 관리합니다.
433 line:
//
// The interrupt handler for the Ethernet interrupt.
//
static void EthernetIntHandler(void)
{
unsigned long ulTemp;
//
// Read and Clear the interrupt.
//
ulTemp = EthernetIntStatus(ETH_BASE, false);
EthernetIntClear(ETH_BASE, ulTemp);
//
// Check to see if an RX Interrupt has occured.
//
if(ulTemp & ETH_INT_RX)
{
//
// Disable Ethernet RX Interrupt.
//
EthernetIntDisable(ETH_BASE, ETH_INT_RX);
}
}
ETH 인터럽트 핸들러입니다. ETH에서는 메시지가 수신되었을 때 인터럽트가 발생합니다. 이 핸들러에서는 메시지 수신 시 더이상 인터럽트가 발생하지 않도록 disable 시켜주기만 합니다. 나머지 작업들은 uIP 스텍 내부에서 처리됩니다.
462 line:
/*
* ADC Interrupt Handler
*/
static void ADCIntHandler(void)
{
unsigned long adc_result[8];
double temp;
ADCIntClear(ADC_BASE, 0);
ADCSequenceDataGet(ADC_BASE, 0, adc_result);
temp = (2.7 - 3.0 * (double)adc_result[0] / 1024.0) * 75.0 - 55.0;
g_temp = (unsigned long)(temp * 100);
}
온도를 읽기 위한 ADC 인터럽트 핸들러입니다. 자세한 내용은 [adc_temperature 예제](예제 adc_temperature)를 참조하시기 바랍니다.
이 파일은 클라이언트 접속이 들어왔을 때 html파일을 생성하는 코드입니다. httpd_appcall() 함수에서 현재 상태에 따라 html 파일을 생성하여 send 하고 있습니다.
웹서버에서 표시되는 내용을 변경하고자 할 경우 이 파일의 내용을 수정해야 합니다.
- myCortex-LM8962 보드에 이더넷 케이블을 연결합니다. 많이 사용되는 유무선 공유기 환경이 적합합니다.
- 펌웨어를 빌드한 후 다운로드 하고, 보드의 리셋 버튼을 1회 누릅니다.
- 이더넷 커넥터에 있는 두 개의 LED가 깜박이는지 확인합니다.
- 동일한 공유기에 연결된 PC 혹은 스마트폰에서 웹브라우저를 사용하여 myCortex-LM8962 보드로 접속하여 html 문서가 표시되는지 확인합니다.
- 동일한 공유기가 아닌 경우 공유기에서 포트포워딩을 설정해야 접속 가능합니다.