It's been a while since I have posted any thing. I have been very busy with the family and work and did not have much time. Also babies are very time consuming. I will be posting about the ADC and the DAC for the Stm32 In a few weeks since things are finally starting to settle down.
STM32W
The ARM STM32 now has an affordable wireless family. It works on 802.11.4. This is the same as zigbee. A lot more external components are needed for it to work than you typical UC but its not too bad.
The team at Nomi Design are all ready doing some open source development for it. They have a PCB design for this Arm chip and are Porting Contiki as their RTOS (Real Time Operation System). Their final product is planed to be an arduino compatible or a standalone component that can send data threw ethernet, IVP4, IPV6, zigbee, mifi or what ever protocol you can think of. A cheap and sweet way to get your arduino wireless. If you want more details visit their web site http://nomidesign.net.
The beauty with these is that you should be able to port your current project over and they are now wireless. I will be working with these for the next little while. I will be posting articles on how to get it going as soon as I get it my self. I can post schematics, but the guys at Nomi Design would probably have a much better board.
If you want to read up on this ARM chip take a look at ST's website
http://www.st.com/internet/mcu/subclass/1377.jsp
Microcontrollers from a hobbyist perspective
Tuesday, August 9, 2011
Wednesday, March 9, 2011
My Evalbot First Steps: Hosting a webpage From a USB stick
So for starters this is a contiunation of a previous post my evalbot first steps Part 1. In this part though we will be mounting a usb flash drive to host our web page. Since the example provided does not work at all on the arm stellaris evalbot, we will be doing this from scratch except for the fat32 implementation. So load up the LWIP project and lets get started.
Changing the Interupts
So first thing we need to do is open our start up file it should be called something like startup_rvmdk.S. Now look for your interrupt vector. Look for the like DCD lwIPEthernetIntHandler ; Ethernet we will change that to DCD IpInterupt ; Ethernet Now look for the line that ends with ; USB0 it should be 2 lines lower. We will replace IntDefaultHandler with USB0HostIntHandler. What we are actually doing, is replacing the function that will get called when the interupt occurs to our own. USB0HostIntHandler All ready exist within the USB drivers in the Stellaris SDK. We are also replacing the Ethernet interrupt from the LWIP to our own. The reason we are doing that is because right now, when ever we receive data from the Ethernet we call the interrupt. Then we load the web page and send it back and we return to the normal execution. The problem with that, is the the USB driver uses interrupt as well. If we want to load data from the USB we can't be with in an interrupt routine because the USB will not interrupt the interrupt. Now we also need to tell our startup code that the functions actually do exists some where in the code. So scroll up just above the vector table and you should see an external deceleration section. Simply add these 2 lines of code:
EXTERN USB0HostIntHandler
EXTERN IpInterupt
Lets start by getting the #includes out of the way. You will need to add:
#include "usblib/usblib.h"
#include "usblib/usbmsc.h"
#include "usblib/host/usbhost.h"
#include "usblib/host/usbhmsc.h"
#include "driverlib/udma.h"
#include "fatfs/src/ff.h"
Next, you will need to add the USB lib to your project. simply double click on the libraries folder. It should open a file browse window. Navigate to the directory of the stellaris SDK/usblib/rvmdk. you will need to set the Files of type (at the bottom) to all files and add usblib.lib.
Now we need to add a bunch of stuff in our main(). You can put it in a function call initUSB() or something and call that from the main it might be cleaner but that's up to you. So I added my USB init after the SysTick setup (ROM_SysTickIntEnable();). The USB host for mast storage requires UDMA so first thing is to enable that. 3 simple lines should do it
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
ROM_uDMAEnable();
ROM_uDMAControlBaseSet(g_sDMAControlTable);
You will also need to define a global variable:
tDMAControlTable g_sDMAControlTable[6] __attribute__ ((aligned(1024)));
Now we need to enable the USB hardware. The actually USB device is on GPIO portA. If you look at the schematic, we also need GPIO portB. This is used to indicate if we are Host mode or device mode. So lets enable the GPIO we will also enable the USB0 device.
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
Now lets set the chip to USB host mode.
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_0);
ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 0);
Finally lets configure the USB data pin and let the Driver know what pin to use.
GPIOPinConfigure(GPIO_PA6_USB0EPEN);
ROM_GPIOPinTypeUSBDigital(GPIO_PORTA_BASE, GPIO_PIN_6);
Now we need to tell the SDK drive that we want to use the Mass storage driver. Keep note of where you are in your main. When we come back to it we will be adding code there. For now we will need to set a few things up. First we need to define a char buffer for the Host controller. 128 should be big enough. We also need a variable to store what USB is connected host or device. You can use this one if you ever switch between USB host or device. This will get changed in a call back. Nest we will define an enum to hold the different states the USB can be in. This way we know if its ready ect. Also, we need to declare the variable that will hold the instance of the driver. It MUST KEEP the same name as the fat implementation declares it as extern: g_ulMSCInstance . We also will declare an array of USB host drivers and associate our call back with the driver. Since we only wan the Mass storage driver, that is the only one we will put. Finally I will declare 3 functions used for call back. I will go more in details as we define them. Here are what my decelerations look like.
unsigned long g_ulMSCInstance = 0;
unsigned char hostControllerPool[128];
tUSBMode usbMode;
enum {
STATE_NO_DEVICE,
STATE_MSC_INIT,
STATE_MSC_CONNECTED,
STATE_UNKNOWN_DEVICE,
STATE_POWER_FAULT
} usb_state;
void USBEventCallback(void *eventData);
void ModeCallback(unsigned long ulIndex, tUSBMode eMode);
void MSCCallback(unsigned long ulInstance, unsigned long ulEvent, void *pvData);
//associate the call back
DECLARE_EVENT_DRIVER(USBEventHandler, 0, 0, USBEventCallback);
static tUSBHostClassDriver const * const UsbClassDrivers[] =
{
&g_USBHostMSCClassDriver,
&USBEventHandler
};
#define NUM_CLASS_DRIVERS (sizeof(UsbClassDrivers) / sizeof(*UsbClassDrivers))
Now we will define our call back functions. Let start with void USBEventCallback(void *eventData); This function is called at the lowest layer. It gets called every type a USB device is plugged in removed or has power fault. which ever the case we want to know what happen so our program know where we are.
void USBEventCallback(void *eventData)
{
tEventInfo *eventInfo;
eventInfo = (tEventInfo *)eventData;
switch(eventInfo->ulEvent) {
case USB_EVENT_CONNECTED:
usb_state = STATE_UNKNOWN_DEVICE;
break;
case USB_EVENT_DISCONNECTED:
usb_state = STATE_NO_DEVICE;
break;
case USB_EVENT_POWER_FAULT:
usb_state = STATE_POWER_FAULT;
break;
default:
break;
}
}
Next we got void ModeCallback(unsigned long ulIndex, tUSBMode eMode). this specifies if a USB Devices is connected or Host. Ill leave the switch but we don't really need it. It just there in case you want to add to it.
void ModeCallback(unsigned long ulIndex, tUSBMode eMode)
{
usbMode = eMode;
switch(eMode) {
case USB_MODE_HOST:
break;
case USB_MODE_DEVICE:
break;
case USB_MODE_NONE:
break;
default:
break;
}
}
Finally this last call back are event specific to the usb mass storage devices. This only gets called if we have a USB mass storage devices plunged in or unplugged. So if we detect a USB Mass storage, we will mount the file system. (we will only mount sda0 assuming there is only 1 partition.). The actual mounting will be done later for now lets just call our fs init code.
void MSCCallback(unsigned long ulInstance, unsigned long ulEvent, void *pvData)
{
switch(ulEvent)
{
case MSC_EVENT_OPEN:
{
usb_state = STATE_MSC_CONNECTED;
//Ensure we are still connects
USBHCDMain();
//wait for the device to be ready
while(USBHMSCDriveReady(g_ulMSCInstance))
{
//pause to not stress out the device
SysCtlDelay(SysCtlClockGet()/30);
}
fs_init();
break;
case MSC_EVENT_CLOSE:
//should probably unnount the partition.
usb_state = STATE_NO_DEVICE;
break;
default:
break;
}
}
Now lets go back to the main. We will need to add the code to start up the USB host driver. Initialise the USB to not connected and tell the driver that we will be using USB host only.
usb_state = STATE_NO_DEVICE;
USBStackModeSet(0, USB_MODE_HOST, ModeCallback);
Next we need to the the USB host driver, what kind of Host device can we connect. This is where we pass our array of drivers.
USBHCDRegisterDrivers(0, UsbClassDrivers, NUM_CLASS_DRIVERS);
Finally, lest start a instance of the USB mass storage driver, give the USB power and Start the USB Host controller.
//start a MSC USB HOST
g_ulMSCInstance = USBHMSCDriveOpen(0, MSCCallback);
//give the usb some power
USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
//USBOTGModeInit(0, 2000, g_pHCDPool, HCD_MEMORY_SIZE);
USBHCDInit(0, g_pHCDPool2, HCD_MEMORY_SIZE); //dcm
At the end of you main there should be a infinite loop. insert USBHCDMain(); in that loop.
Now the USB should be up and running. Now we need to read from the USB when we load a web page.
int IPInterupted = 0;
int InitDone = 0;
Now we will add our interrupt function. If we are still initializing stuff, then lets simply handle the interrupt in our interrupt request. IF we are done the init, then set the flag so we can handle it in our main execution and free up the interrupts. we will also disable the Ethernet interrupt until we have dealt with this one.
void IpInterupt(void)
{
if(InitDone)
{
IPInterupted = 1;
EthernetIntDisable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);
}
else
{
lwIPEthernetIntHandler();
}
}
Now back to the main. Right before we enter the infinite loop, we will add InitDone = 1; This way our interrupt know not to handle it but flag to let us know it happened and let us deal with the interrupt. Now in the infinite loops we need to add code to handle the interrupt is if occurs and re-enable the Ethernet interrupt once we have dealt with it. Here is what my infinite loop looks like.
InitDone = 1;
while(1)
{
if(IPInterupted)
{
lwIPEthernetIntHandler();
EthernetIntEnable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);
}
USBHCDMain();
}
Now that the files are loaded we are ready to read out htm files from the USB. So lets open the lmi_fs.c file. This files contains the function that httd will call to open the web pages. we will simple add the calls to read the USB here. Before we start modifying code, we need to add a couble of global variables:
FATFS g_sFatFs;
extern enum {
STATE_NO_DEVICE,
STATE_MSC_INIT,
STATE_MSC_CONNECTED,
STATE_UNKNOWN_DEVICE,
STATE_POWER_FAULT
} usb_state;
and include
#include "fatfs/src/ff.h".
Let start with fs_init(). If you remember we call that when we detect a USB key plugged in. In this code, we will need to mount the file system. We only want to mount it though if we have a USB connected.
void fs_init(void)
{
FIL sFileObject;
if(usb_state == STATE_MSC_CONNECTED)
{
f_mount(0, &g_sFatFs);
}
}
Now we want to be able to open the USB files. Lets head over to fs_open(char *name). Right after we allocate the memory for ptFile and check if its not null, we will add our code. we want to return ptFile with and open fat file in the struct and only if we have a USB connected. I left the rest of the code there. That way if the USB is not connected, we will open the default web page. I don't do error handling yet that is left as an exercise for you. Here is a snippet of my code:
if(usb_state == STATE_MSC_CONNECTED)
{
if(f_open(&ptFile->sFileObject, name, FA_READ) != FR_OK)
{
}
return ptFile;
}
In Order to have this working though we will need to add to the struct. So lets open fs.h. If you Are in the project explorer, and you click the + sign next to the lmi_fs, you should see a list of included files. double click on FS.h you should add the include #include "fatfs/src/ff.h" and add FIL sFileObject to the struct. Here is what my struct looks like:
#ifndef __FS_H__
#define __FS_H__
#include "fatfs/src/ff.h"
struct fs_file {
char *data;
int len;
int index;
void *pextension;
FIL sFileObject
};
Now back to lmi_fs.c. We need to implement the read function. This function would return -1 if no more data is left if not it returns the number of bytes read. Again I left the rest of the code there so I can still serve web pages if no usb is connects. here is what my read code looks like:
if(usb_state == STATE_MSC_CONNECTED)
{
f_read(&file->sFileObject, buffer,count,&usBytesRead);
if(!usBytesRead)
{
//no data left
f_close(&file->sFileObject);
return -1;
}
return usBytesRead;
}
And there you go. you should be able to serve web pages from your arm stellaris evalbot by loading the pages from USB. If its not working, I might have forgotten a step. Please let me know and Ill fix it. Also if you see any improvements thanks.
Changing the Interupts
So first thing we need to do is open our start up file it should be called something like startup_rvmdk.S. Now look for your interrupt vector. Look for the like DCD lwIPEthernetIntHandler ; Ethernet we will change that to DCD IpInterupt ; Ethernet Now look for the line that ends with ; USB0 it should be 2 lines lower. We will replace IntDefaultHandler with USB0HostIntHandler. What we are actually doing, is replacing the function that will get called when the interupt occurs to our own. USB0HostIntHandler All ready exist within the USB drivers in the Stellaris SDK. We are also replacing the Ethernet interrupt from the LWIP to our own. The reason we are doing that is because right now, when ever we receive data from the Ethernet we call the interrupt. Then we load the web page and send it back and we return to the normal execution. The problem with that, is the the USB driver uses interrupt as well. If we want to load data from the USB we can't be with in an interrupt routine because the USB will not interrupt the interrupt. Now we also need to tell our startup code that the functions actually do exists some where in the code. So scroll up just above the vector table and you should see an external deceleration section. Simply add these 2 lines of code:
EXTERN USB0HostIntHandler
EXTERN IpInterupt
Getting the USB working
Lets start by getting the #includes out of the way. You will need to add:
#include "usblib/usblib.h"
#include "usblib/usbmsc.h"
#include "usblib/host/usbhost.h"
#include "usblib/host/usbhmsc.h"
#include "driverlib/udma.h"
#include "fatfs/src/ff.h"
Next, you will need to add the USB lib to your project. simply double click on the libraries folder. It should open a file browse window. Navigate to the directory of the stellaris SDK/usblib/rvmdk. you will need to set the Files of type (at the bottom) to all files and add usblib.lib.
Now we need to add a bunch of stuff in our main(). You can put it in a function call initUSB() or something and call that from the main it might be cleaner but that's up to you. So I added my USB init after the SysTick setup (ROM_SysTickIntEnable();). The USB host for mast storage requires UDMA so first thing is to enable that. 3 simple lines should do it
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);
ROM_uDMAEnable();
ROM_uDMAControlBaseSet(g_sDMAControlTable);
You will also need to define a global variable:
tDMAControlTable g_sDMAControlTable[6] __attribute__ ((aligned(1024)));
Now we need to enable the USB hardware. The actually USB device is on GPIO portA. If you look at the schematic, we also need GPIO portB. This is used to indicate if we are Host mode or device mode. So lets enable the GPIO we will also enable the USB0 device.
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0);
Now lets set the chip to USB host mode.
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_0);
ROM_GPIOPinWrite(GPIO_PORTB_BASE, GPIO_PIN_0, 0);
Finally lets configure the USB data pin and let the Driver know what pin to use.
GPIOPinConfigure(GPIO_PA6_USB0EPEN);
ROM_GPIOPinTypeUSBDigital(GPIO_PORTA_BASE, GPIO_PIN_6);
Now we need to tell the SDK drive that we want to use the Mass storage driver. Keep note of where you are in your main. When we come back to it we will be adding code there. For now we will need to set a few things up. First we need to define a char buffer for the Host controller. 128 should be big enough. We also need a variable to store what USB is connected host or device. You can use this one if you ever switch between USB host or device. This will get changed in a call back. Nest we will define an enum to hold the different states the USB can be in. This way we know if its ready ect. Also, we need to declare the variable that will hold the instance of the driver. It MUST KEEP the same name as the fat implementation declares it as extern: g_ulMSCInstance . We also will declare an array of USB host drivers and associate our call back with the driver. Since we only wan the Mass storage driver, that is the only one we will put. Finally I will declare 3 functions used for call back. I will go more in details as we define them. Here are what my decelerations look like.
unsigned long g_ulMSCInstance = 0;
unsigned char hostControllerPool[128];
tUSBMode usbMode;
enum {
STATE_NO_DEVICE,
STATE_MSC_INIT,
STATE_MSC_CONNECTED,
STATE_UNKNOWN_DEVICE,
STATE_POWER_FAULT
} usb_state;
void USBEventCallback(void *eventData);
void ModeCallback(unsigned long ulIndex, tUSBMode eMode);
void MSCCallback(unsigned long ulInstance, unsigned long ulEvent, void *pvData);
//associate the call back
DECLARE_EVENT_DRIVER(USBEventHandler, 0, 0, USBEventCallback);
static tUSBHostClassDriver const * const UsbClassDrivers[] =
{
&g_USBHostMSCClassDriver,
&USBEventHandler
};
#define NUM_CLASS_DRIVERS (sizeof(UsbClassDrivers) / sizeof(*UsbClassDrivers))
Now we will define our call back functions. Let start with void USBEventCallback(void *eventData); This function is called at the lowest layer. It gets called every type a USB device is plugged in removed or has power fault. which ever the case we want to know what happen so our program know where we are.
void USBEventCallback(void *eventData)
{
tEventInfo *eventInfo;
eventInfo = (tEventInfo *)eventData;
switch(eventInfo->ulEvent) {
case USB_EVENT_CONNECTED:
usb_state = STATE_UNKNOWN_DEVICE;
break;
case USB_EVENT_DISCONNECTED:
usb_state = STATE_NO_DEVICE;
break;
case USB_EVENT_POWER_FAULT:
usb_state = STATE_POWER_FAULT;
break;
default:
break;
}
}
Next we got void ModeCallback(unsigned long ulIndex, tUSBMode eMode). this specifies if a USB Devices is connected or Host. Ill leave the switch but we don't really need it. It just there in case you want to add to it.
void ModeCallback(unsigned long ulIndex, tUSBMode eMode)
{
usbMode = eMode;
switch(eMode) {
case USB_MODE_HOST:
break;
case USB_MODE_DEVICE:
break;
case USB_MODE_NONE:
break;
default:
break;
}
}
Finally this last call back are event specific to the usb mass storage devices. This only gets called if we have a USB mass storage devices plunged in or unplugged. So if we detect a USB Mass storage, we will mount the file system. (we will only mount sda0 assuming there is only 1 partition.). The actual mounting will be done later for now lets just call our fs init code.
void MSCCallback(unsigned long ulInstance, unsigned long ulEvent, void *pvData)
{
switch(ulEvent)
{
case MSC_EVENT_OPEN:
{
usb_state = STATE_MSC_CONNECTED;
//Ensure we are still connects
USBHCDMain();
//wait for the device to be ready
while(USBHMSCDriveReady(g_ulMSCInstance))
{
//pause to not stress out the device
SysCtlDelay(SysCtlClockGet()/30);
}
fs_init();
break;
case MSC_EVENT_CLOSE:
//should probably unnount the partition.
usb_state = STATE_NO_DEVICE;
break;
default:
break;
}
}
Now lets go back to the main. We will need to add the code to start up the USB host driver. Initialise the USB to not connected and tell the driver that we will be using USB host only.
usb_state = STATE_NO_DEVICE;
USBStackModeSet(0, USB_MODE_HOST, ModeCallback);
Next we need to the the USB host driver, what kind of Host device can we connect. This is where we pass our array of drivers.
USBHCDRegisterDrivers(0, UsbClassDrivers, NUM_CLASS_DRIVERS);
Finally, lest start a instance of the USB mass storage driver, give the USB power and Start the USB Host controller.
//start a MSC USB HOST
g_ulMSCInstance = USBHMSCDriveOpen(0, MSCCallback);
//give the usb some power
USBHCDPowerConfigInit(0, USBHCD_VBUS_AUTO_HIGH | USBHCD_VBUS_FILTER);
//USBOTGModeInit(0, 2000, g_pHCDPool, HCD_MEMORY_SIZE);
USBHCDInit(0, g_pHCDPool2, HCD_MEMORY_SIZE); //dcm
At the end of you main there should be a infinite loop. insert USBHCDMain(); in that loop.
Now the USB should be up and running. Now we need to read from the USB when we load a web page.
Breaking Out Of The Interrupt Request
Let's start with handelling the Ethernet in our main rather than in a interrupt request. Lest start by declaring 2 global variables. IPInterupted is a flag that we will use to indicate we received a Interrupt and InitDone is a flag to tell us that we have finished the init and we and now in our endless loop in our main.int IPInterupted = 0;
int InitDone = 0;
Now we will add our interrupt function. If we are still initializing stuff, then lets simply handle the interrupt in our interrupt request. IF we are done the init, then set the flag so we can handle it in our main execution and free up the interrupts. we will also disable the Ethernet interrupt until we have dealt with this one.
void IpInterupt(void)
{
if(InitDone)
{
IPInterupted = 1;
EthernetIntDisable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);
}
else
{
lwIPEthernetIntHandler();
}
}
Now back to the main. Right before we enter the infinite loop, we will add InitDone = 1; This way our interrupt know not to handle it but flag to let us know it happened and let us deal with the interrupt. Now in the infinite loops we need to add code to handle the interrupt is if occurs and re-enable the Ethernet interrupt once we have dealt with it. Here is what my infinite loop looks like.
InitDone = 1;
while(1)
{
if(IPInterupted)
{
lwIPEthernetIntHandler();
EthernetIntEnable(ETH_BASE, ETH_INT_RX | ETH_INT_TX);
}
USBHCDMain();
}
Loading A Page From USB
We will need to add 2 files to our 3rd Party folder. ff.c and fat_usbmsc.c. So double click the 3rparty folder. Navigate to the folder the stellaris SDK/third_party/fatfs/port and add the file "fat_usbmsc.c". Now go back one folder go in src, and add the file ff.c.Now that the files are loaded we are ready to read out htm files from the USB. So lets open the lmi_fs.c file. This files contains the function that httd will call to open the web pages. we will simple add the calls to read the USB here. Before we start modifying code, we need to add a couble of global variables:
FATFS g_sFatFs;
extern enum {
STATE_NO_DEVICE,
STATE_MSC_INIT,
STATE_MSC_CONNECTED,
STATE_UNKNOWN_DEVICE,
STATE_POWER_FAULT
} usb_state;
and include
#include "fatfs/src/ff.h".
Let start with fs_init(). If you remember we call that when we detect a USB key plugged in. In this code, we will need to mount the file system. We only want to mount it though if we have a USB connected.
void fs_init(void)
{
FIL sFileObject;
if(usb_state == STATE_MSC_CONNECTED)
{
f_mount(0, &g_sFatFs);
}
}
Now we want to be able to open the USB files. Lets head over to fs_open(char *name). Right after we allocate the memory for ptFile and check if its not null, we will add our code. we want to return ptFile with and open fat file in the struct and only if we have a USB connected. I left the rest of the code there. That way if the USB is not connected, we will open the default web page. I don't do error handling yet that is left as an exercise for you. Here is a snippet of my code:
if(usb_state == STATE_MSC_CONNECTED)
{
if(f_open(&ptFile->sFileObject, name, FA_READ) != FR_OK)
{
}
return ptFile;
}
In Order to have this working though we will need to add to the struct. So lets open fs.h. If you Are in the project explorer, and you click the + sign next to the lmi_fs, you should see a list of included files. double click on FS.h you should add the include #include "fatfs/src/ff.h" and add FIL sFileObject to the struct. Here is what my struct looks like:
#ifndef __FS_H__
#define __FS_H__
#include "fatfs/src/ff.h"
struct fs_file {
char *data;
int len;
int index;
void *pextension;
FIL sFileObject
};
Now back to lmi_fs.c. We need to implement the read function. This function would return -1 if no more data is left if not it returns the number of bytes read. Again I left the rest of the code there so I can still serve web pages if no usb is connects. here is what my read code looks like:
if(usb_state == STATE_MSC_CONNECTED)
{
f_read(&file->sFileObject, buffer,count,&usBytesRead);
if(!usBytesRead)
{
//no data left
f_close(&file->sFileObject);
return -1;
}
return usBytesRead;
}
And there you go. you should be able to serve web pages from your arm stellaris evalbot by loading the pages from USB. If its not working, I might have forgotten a step. Please let me know and Ill fix it. Also if you see any improvements thanks.
Thursday, March 3, 2011
My Evalbot First Steps: Ethernet Controlled robot
Intro
So I finally received my arm stellaris Evalbot. So I put down the ARM stm32 for a bit and took out my evalbot. The I got the eval bot with the 125$ discount i saw on http://hackaday.com/ turns out they sold more than they thought and they were put on back order. I got mine now.
So since I all ready had Keil up and running (still waiting for the non commercial one. If i don't get it soon, I think I will go with a GCC tool chain) I decided to test it with the evalbot. first thing is first I downloaded their stellaris SDK and examples here. Next I downloaded the drivers from TI here. Finally, I stole 4 files from this arm evalbot project. They are for motor control and the oled screen (oled.h/c motor.h/c). They work well so why reinvent the wheel. He also has a neat I2C lib in there. I don't think I plan on ever writing my own code for that since its nicely done and would be a waste of time. Now I am ready do create my project. You can follow the same steps as the ARM STM32 only this time use the device LM3S9B92 by Luminary Micro and the "Stellaris ICDI" instead of "ST-Link" as the debug tool and the utility to flash the program.
so the first thing I did was load the enet_lwip Example. That is how I got the robot working online. This I think is the only example that worked out of the box. I will base this project all off this example project. It should also come with a Keil uproj file and you can launch. Because this code is copyright by TI and not open source, My next step is to build create my own project under the same license and LWIP. For now we are just exploring so this copyright code is good enough.
Adding to the project
If you look at the main, there is not much. I would recommend you look around the code and get familiar with it before reading on as I will just jump right into it.
First thing I did since I never got the UART to work, was Display the ip address once I received it. We will need to add the oled.c file to our project. I like to have a folder for every function in my project folder. I created a folder called oled in the project folder and copied oled.h and oled.c in that folder. Next, you need double click on the 3rdparty folder. I like to add both the .c and the .h file so its easy to open the .h file if I need to view the function names but you just need the .c file. So click on the newly copied oled.c (and oled.h if you want) file and click add then click close. Now we need to include the oled.h file. so scroll to the top and add #include "oled/oled.h". Now that the prep work is done, we will head over to the function called lwIPHostTimerHandler in enet_lwip.c. First we should add our buffer unsigned char screenBuff[17] = {0}; 17 is because the LCD can only display 17 characters and 1 for the \0. There should be an if statement that looks like this:
if(ulIPAddress != g_ulLastIPAddr). In the if block we will add the display code. Here is what mine looks like after i striped the uart code:
if(ulIPAddress != g_ulLastIPAddr)
{
g_ulLastIPAddr = ulIPAddress;
dispClear();
dispString("Ip obtained: ",0,0);
sprintf(screenBuff,"%d.%d.%d.%d",((unsigned char*)&g_ulLastIPAddr)[0],
((unsigned char*)&g_ulLastIPAddr)[1],
((unsigned char*)&g_ulLastIPAddr)[2],
((unsigned char*)&g_ulLastIPAddr)[3]);
dispString(screenBuff,0,1);
ulIPAddress = lwIPLocalNetMaskGet();
ulIPAddress = lwIPLocalGWAddrGet();
}
If you go a head now and plug a network cable in the evalbot, wait a little bit for the DHCP to resolve you will get an IP address and a example web page if you http on that address.
Making it move
Now we will add some CGI to the project. HTTPD that come with the example projects will look for calls to pages that end with the cgi file extension and call the associated function. You can go ahead and repeat the steps we took to add oled to our project but with motor instead (create the motor folder copy over the 2 files and and the .c/h file to the 3rdparty folder). Now we need to include the motor header. #include "motor/motor.h". We will We will add 2 cgi handler 1 for the LCD and 1 for the motor control. Lets create a file called cgi.h/.c and add it to our project. I went a head and created a folder called cgi and saved the 2 files in there. I also created a group called cgi and added both files to that group. In our cgi.h file we will need to include #include "httpserver_raw/httpd.h". we will also need to declare our 2 CGI functions and map them to the page. Here is what my Header file looks like:
#include "httpserver_raw/httpd.h"
#ifndef CGI_H
#define CGI_H
char* LcdCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[]);
char* DriveCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[]);
//Map the CGI function to the cgi url
static const tCGI CGI_URI_MAP[] =
{
{ "/LCD.cgi", LcdCGIHandler },{ "/drive.cgi", DriveCGIHandler } /// CGI_INDEX_CONTROL
};
#endif
All we got left to do is define those 2 functions and enable CGI. Before we do that, I created a .c/.h file called driving. In there I put 4 function up() down() turnRight() turnLeft(). I will use these function in the example here and I will leave implementation of these functions as an exercise to the reader. I will give you a head-start by showing you what my turnRight function looks like. It should be very little work to create the other ones.
int GlobalI; //this is used to stop the compiler from optimising
void turnRight(void)
{
GlobalI=0; //set it to 0
setMotor(MOTOR_L,MOTOR_DIR_F,0x40); //Rotate the Left Motor toward the front
setMotor(MOTOR_R,MOTOR_DIR_B,0x40); //Rotate the Right Motor toward the back
//Wait a little bit taking baby steps
// 0x44000 was chosen sorta randomly.
//I found waiting this long 4 time made it turn about 90 deg
while(GlobalI<0x44000)
{
GlobalI++;
}
setMotor(MOTOR_L,MOTOR_DIR_F,0); // Stop the Left Motor
setMotor(MOTOR_R,MOTOR_DIR_B,0); // Stop the Right motor
}
It should be pretty straight forward to add the other 3 missing functions. No we are going to create our CGI functions. Lets check out what the parameters do. we got iIndex, this is the index of the cgi handler in the array. I am not sure where we would use this. Next there is iNumParams. This is the number of parameter that were found after the ? in the uri. the we have 2 arrays. 1 for the param name and 1 for their value.
So lest make a cgi that will display on the lcd what ever we pass as an argument in the uri. This function returns a char* with the URI of the page the user should be redirected to. We will make a global char* with the uri so that it does not go out or scope after we return.
char* indexName = "/index.htm";
char* LcdCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[])
{
int i=0;
//clear our LCD
dispClear();
//loop threw all the parameters
for(i=0;i<iNumParams;i++)
{
//check if we found the parameter we want
if(!strcmp(pcParam[i],"name") )
{
//display the value we received
dispString(pcValue[i],0,0);
//exit the loop
i=iNumParams;
}
// return the Index uri to redirect the user
return indexName;
}
It was pretty straight forward. Now all we got to do is make the drive handler. I will only show how to make it turn right and it will be up to you to do the rest. Make sure you include #include "motor/driving.h".
char* DriveCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[])
{
int i=0;
//loop threw the parameters
for(i=0;i<iNumParams;i++)
{
//did we find the parameter we are looking for?
if(!strcmp(pcParam[i],"dir") )
{
// lest compare the directions
if(!strcmp(pcValue[i],"up") )
{
}
else if(!strcmp(pcValue[i],"dn") )
{
}
else if(!strcmp(pcValue[i],"rt") )
{
turnRight();
}
else if(!strcmp(pcValue[i],"lt") )
{
}
}
}
// return the user to /index.htm
return indexName;
}
All we got left to do in enable our CGI. By default, the CGI is not compiled so we will need to add a define to tell httpd to compile it so lets add the define INCLUDE_HTTPD_CGI in the options. Hit Alt+F7 to open the option dialog. head over to the C/C++ tab and in the define section add INCLUDE_HTTPD_CGI.
Now head back to your main. Right under the variable decelerations, you can call init_display(); and init_motorPWM(); I actually added it right after the ROM_SysCtlClockSet call.
Now right on top of httpd_init(); we will add our CGI map like this
http_set_cgi_handlers(CGI_URI_MAP, 2); //the 2 is the number of CGI handlers we have defined. Don't forget to #include "cgi/cgi.h".
Now if you go to your browser and navigate to http://evalbotIP/LCD.cgi?name=Hello
it should display hello on the LCD. if you head over to http://evalbotIP/drive.cgi?dir=rt
the evalbot robot should turn right.
My Next post in the next couple days will be hosting the web page from a USB key.
Neat little ARM based robot.
So I finally received my arm stellaris Evalbot. So I put down the ARM stm32 for a bit and took out my evalbot. The I got the eval bot with the 125$ discount i saw on http://hackaday.com/ turns out they sold more than they thought and they were put on back order. I got mine now.
So since I all ready had Keil up and running (still waiting for the non commercial one. If i don't get it soon, I think I will go with a GCC tool chain) I decided to test it with the evalbot. first thing is first I downloaded their stellaris SDK and examples here. Next I downloaded the drivers from TI here. Finally, I stole 4 files from this arm evalbot project. They are for motor control and the oled screen (oled.h/c motor.h/c). They work well so why reinvent the wheel. He also has a neat I2C lib in there. I don't think I plan on ever writing my own code for that since its nicely done and would be a waste of time. Now I am ready do create my project. You can follow the same steps as the ARM STM32 only this time use the device LM3S9B92 by Luminary Micro and the "Stellaris ICDI" instead of "ST-Link" as the debug tool and the utility to flash the program.
so the first thing I did was load the enet_lwip Example. That is how I got the robot working online. This I think is the only example that worked out of the box. I will base this project all off this example project. It should also come with a Keil uproj file and you can launch. Because this code is copyright by TI and not open source, My next step is to build create my own project under the same license and LWIP. For now we are just exploring so this copyright code is good enough.
Adding to the project
If you look at the main, there is not much. I would recommend you look around the code and get familiar with it before reading on as I will just jump right into it.
First thing I did since I never got the UART to work, was Display the ip address once I received it. We will need to add the oled.c file to our project. I like to have a folder for every function in my project folder. I created a folder called oled in the project folder and copied oled.h and oled.c in that folder. Next, you need double click on the 3rdparty folder. I like to add both the .c and the .h file so its easy to open the .h file if I need to view the function names but you just need the .c file. So click on the newly copied oled.c (and oled.h if you want) file and click add then click close. Now we need to include the oled.h file. so scroll to the top and add #include "oled/oled.h". Now that the prep work is done, we will head over to the function called lwIPHostTimerHandler in enet_lwip.c. First we should add our buffer unsigned char screenBuff[17] = {0}; 17 is because the LCD can only display 17 characters and 1 for the \0. There should be an if statement that looks like this:
if(ulIPAddress != g_ulLastIPAddr). In the if block we will add the display code. Here is what mine looks like after i striped the uart code:
if(ulIPAddress != g_ulLastIPAddr)
{
g_ulLastIPAddr = ulIPAddress;
dispClear();
dispString("Ip obtained: ",0,0);
sprintf(screenBuff,"%d.%d.%d.%d",((unsigned char*)&g_ulLastIPAddr)[0],
((unsigned char*)&g_ulLastIPAddr)[1],
((unsigned char*)&g_ulLastIPAddr)[2],
((unsigned char*)&g_ulLastIPAddr)[3]);
dispString(screenBuff,0,1);
ulIPAddress = lwIPLocalNetMaskGet();
ulIPAddress = lwIPLocalGWAddrGet();
}
If you go a head now and plug a network cable in the evalbot, wait a little bit for the DHCP to resolve you will get an IP address and a example web page if you http on that address.
Making it move
Now we will add some CGI to the project. HTTPD that come with the example projects will look for calls to pages that end with the cgi file extension and call the associated function. You can go ahead and repeat the steps we took to add oled to our project but with motor instead (create the motor folder copy over the 2 files and and the .c/h file to the 3rdparty folder). Now we need to include the motor header. #include "motor/motor.h". We will We will add 2 cgi handler 1 for the LCD and 1 for the motor control. Lets create a file called cgi.h/.c and add it to our project. I went a head and created a folder called cgi and saved the 2 files in there. I also created a group called cgi and added both files to that group. In our cgi.h file we will need to include #include "httpserver_raw/httpd.h". we will also need to declare our 2 CGI functions and map them to the page. Here is what my Header file looks like:
#include "httpserver_raw/httpd.h"
#ifndef CGI_H
#define CGI_H
char* LcdCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[]);
char* DriveCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[]);
//Map the CGI function to the cgi url
static const tCGI CGI_URI_MAP[] =
{
{ "/LCD.cgi", LcdCGIHandler },{ "/drive.cgi", DriveCGIHandler } /// CGI_INDEX_CONTROL
};
#endif
All we got left to do is define those 2 functions and enable CGI. Before we do that, I created a .c/.h file called driving. In there I put 4 function up() down() turnRight() turnLeft(). I will use these function in the example here and I will leave implementation of these functions as an exercise to the reader. I will give you a head-start by showing you what my turnRight function looks like. It should be very little work to create the other ones.
int GlobalI; //this is used to stop the compiler from optimising
void turnRight(void)
{
GlobalI=0; //set it to 0
setMotor(MOTOR_L,MOTOR_DIR_F,0x40); //Rotate the Left Motor toward the front
setMotor(MOTOR_R,MOTOR_DIR_B,0x40); //Rotate the Right Motor toward the back
//Wait a little bit taking baby steps
// 0x44000 was chosen sorta randomly.
//I found waiting this long 4 time made it turn about 90 deg
while(GlobalI<0x44000)
{
GlobalI++;
}
setMotor(MOTOR_L,MOTOR_DIR_F,0); // Stop the Left Motor
setMotor(MOTOR_R,MOTOR_DIR_B,0); // Stop the Right motor
}
It should be pretty straight forward to add the other 3 missing functions. No we are going to create our CGI functions. Lets check out what the parameters do. we got iIndex, this is the index of the cgi handler in the array. I am not sure where we would use this. Next there is iNumParams. This is the number of parameter that were found after the ? in the uri. the we have 2 arrays. 1 for the param name and 1 for their value.
So lest make a cgi that will display on the lcd what ever we pass as an argument in the uri. This function returns a char* with the URI of the page the user should be redirected to. We will make a global char* with the uri so that it does not go out or scope after we return.
char* indexName = "/index.htm";
char* LcdCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[])
{
int i=0;
//clear our LCD
dispClear();
//loop threw all the parameters
for(i=0;i<iNumParams;i++)
{
//check if we found the parameter we want
if(!strcmp(pcParam[i],"name") )
{
//display the value we received
dispString(pcValue[i],0,0);
//exit the loop
i=iNumParams;
}
// return the Index uri to redirect the user
return indexName;
}
It was pretty straight forward. Now all we got to do is make the drive handler. I will only show how to make it turn right and it will be up to you to do the rest. Make sure you include #include "motor/driving.h".
char* DriveCGIHandler(int iIndex, int iNumParams, char *pcParam[],char *pcValue[])
{
int i=0;
//loop threw the parameters
for(i=0;i<iNumParams;i++)
{
//did we find the parameter we are looking for?
if(!strcmp(pcParam[i],"dir") )
{
// lest compare the directions
if(!strcmp(pcValue[i],"up") )
{
}
else if(!strcmp(pcValue[i],"dn") )
{
}
else if(!strcmp(pcValue[i],"rt") )
{
turnRight();
}
else if(!strcmp(pcValue[i],"lt") )
{
}
}
}
// return the user to /index.htm
return indexName;
}
All we got left to do in enable our CGI. By default, the CGI is not compiled so we will need to add a define to tell httpd to compile it so lets add the define INCLUDE_HTTPD_CGI in the options. Hit Alt+F7 to open the option dialog. head over to the C/C++ tab and in the define section add INCLUDE_HTTPD_CGI.
Now head back to your main. Right under the variable decelerations, you can call init_display(); and init_motorPWM(); I actually added it right after the ROM_SysCtlClockSet call.
Now right on top of httpd_init(); we will add our CGI map like this
http_set_cgi_handlers(CGI_URI_MAP, 2); //the 2 is the number of CGI handlers we have defined. Don't forget to #include "cgi/cgi.h".
Now if you go to your browser and navigate to http://evalbotIP/LCD.cgi?name=Hello
it should display hello on the LCD. if you head over to http://evalbotIP/drive.cgi?dir=rt
the evalbot robot should turn right.
My Next post in the next couple days will be hosting the web page from a USB key.
Neat little ARM based robot.
Wednesday, February 16, 2011
Keil and non-commercial license
So Keil is trying to develop a non-commercial license. I don't have any details yet. Just thought I would give a heads up. If you want more info contact their sales office. It might also show that there is interest in this.
sales.us (at) keil.com
sales.us (at) keil.com
Labels:
ARM,
C++,
electronic,
embed,
Keil,
Micro controller,
stellaris
Tuesday, February 1, 2011
STM32 Discovery: Porting Polar SSL
Well my next step was porting polarSSL to the arm STM32 chip. I needed this for one of my current project so I am writing how I got it to work. I will be starting from the buffered usart project.
Setting up PolarSLL
So the first thing you need for this task is polar SSL. you can get it here. Copy over the 2 folders: Library and include. Next we will need to add the include folder to our include path. right click on Target and go to the options. Head over to the C/C++ tab. hit the "..." button next to the Include path text box. Then hit the square thing next to the x in the next dialog. Finlay click the "..." button and go find the include folder you just pasted.
From here I would add a new group and call it pollarssl. Then I would add all the files that were in the library folder we just copied over. You will need to add all the C files though to your project.
Now we want to copy the main from ssl_client1.c. you can find it in the source of pollarSSL in the programs\ssl\ folder or here. You also want to keep the code that is all ready in your main. like stm32_Init (); and the initialization of the buffer.
Before we make any changes, we will need to add #include "polarssl/ssl.h". Also, I added this dummy function on top of my main.
Now the changes we need to do. First we will not be using havege so we can remove the following lines:
havege_state hs;
printf( "\n . Connecting to tcp/%s/%4d...", SERVER_NAME,
SERVER_PORT );
fflush( stdout );
if( ( ret = net_connect( &server_fd, SERVER_NAME,
SERVER_PORT ) ) != 0 )
{
printf( " failed\n ! net_connect returned %d\n\n", ret );
goto exit;
}
printf( " ok\n" );
we will also need to change these lines:
net_close( server_fd );
The rest can stay. with these settings I could connect to all the site I need to you might need to un-comment different ciphersuites depending on the site.
Making the code Fit
The havege used for random is quite large. Later we will have to make a hardware random but for now we used our little function called notRandom using the right signature.
Now we need to head over to config.h. we will start commenting out everything we don't need
Next click on the C/C++ tab and set optimization to 3. Also check the on ELF Section per Function. this will remove all unused functions from your code. Setting optimization to level 3 will make it harder to debug but will get your code under 32k
Now with MicroLib, you will not be able to compile. The reason for that is the time function is not coded so we need to code one our self. Simply create and a file and add a empty function like this
#include <time.h>
time_t time ( time_t * timer )
{
return 0;
}
Pollar SSL uses the Time function to determine if the session has expired and if we can reuse past session keys. Making the time function all ways return 0, will force to renegotiate the keys and cause it to take more time. we will have to deal with that for now. I will later change it to return the right time but for now it's not a priority.
That is it. using the application created in the previous blog you can send https request or posts.
There are other ways to connecting to the internet thought. you can use different modules like the network shield they have for the arduino http://www.sparkfun.com/products/9026 or one like this http://www.sparkfun.com/products/9473 that supports sockets or a wifi one like this http://www.sparkfun.com/products/10050.
I want to get this code working with the last one. I will post my changes once I get the part.
Setting up PolarSLL
So the first thing you need for this task is polar SSL. you can get it here. Copy over the 2 folders: Library and include. Next we will need to add the include folder to our include path. right click on Target and go to the options. Head over to the C/C++ tab. hit the "..." button next to the Include path text box. Then hit the square thing next to the x in the next dialog. Finlay click the "..." button and go find the include folder you just pasted.
From here I would add a new group and call it pollarssl. Then I would add all the files that were in the library folder we just copied over. You will need to add all the C files though to your project.
Building the main
Now we want to copy the main from ssl_client1.c. you can find it in the source of pollarSSL in the programs\ssl\ folder or here. You also want to keep the code that is all ready in your main. like stm32_Init (); and the initialization of the buffer.
Before we make any changes, we will need to add #include "polarssl/ssl.h". Also, I added this dummy function on top of my main.
int notRandom(void* param)
{
return 0xE3A576DC;
}
I will go threw, later, how to use the ADC and a random signal generator to make this really random{
return 0xE3A576DC;
}
Now the changes we need to do. First we will not be using havege so we can remove the following lines:
havege_state hs;
/*
* 0. Initialize the RNG and the session data
*/
havege_init( &hs );
memset( &ssn, 0, sizeof( ssl_session ) );
I also remove the net connect code* 0. Initialize the RNG and the session data
*/
havege_init( &hs );
memset( &ssn, 0, sizeof( ssl_session ) );
printf( "\n . Connecting to tcp/%s/%4d...", SERVER_NAME,
SERVER_PORT );
fflush( stdout );
if( ( ret = net_connect( &server_fd, SERVER_NAME,
SERVER_PORT ) ) != 0 )
{
printf( " failed\n ! net_connect returned %d\n\n", ret );
goto exit;
}
printf( " ok\n" );
we will also need to change these lines:
ssl_set_rng( &ssl, havege_rand, &hs );
ssl_set_dbg( &ssl, my_debug, stdout );
ssl_set_bio( &ssl, net_recv, &server_fd,net_send, &server_fd );
We will replace net_recv with my_recv and net_send with my_send and havege_rand with notRandom. the second parameter will be null. This is what it looks likessl_set_dbg( &ssl, my_debug, stdout );
ssl_set_bio( &ssl, net_recv, &server_fd,net_send, &server_fd );
ssl_set_rng( &ssl, notRandom, 0 );
ssl_set_bio( &ssl, my_recv, 0,
my_send, 0 );
Finaly, you need to remove this line near the endssl_set_bio( &ssl, my_recv, 0,
my_send, 0 );
net_close( server_fd );
The rest can stay. with these settings I could connect to all the site I need to you might need to un-comment different ciphersuites depending on the site.
Making the code Fit
The havege used for random is quite large. Later we will have to make a hardware random but for now we used our little function called notRandom using the right signature.
Now we need to head over to config.h. we will start commenting out everything we don't need
//#define POLARSSL_DEBUG_MSG
//#define POLARSSL_SELF_TEST
//#define POLARSSL_VERSION_C
//#define POLARSSL_GENPRIME
//#define POLARSSL_AES_C // You might need this one depending on the site you are accessing
//#define POLARSSL_CAMELLIA_C // You might need this one depending on the site you are accessing
//#define POLARSSL_CERTS_C
//#define POLARSSL_DEBUG_C
//#define POLARSSL_DES_C // You might need this one depending on the site you are accessing
//#define POLARSSL_DHM_C
//#define POLARSSL_HAVEGE_C
//#define POLARSSL_NET_C
//#define POLARSSL_PADLOCK_C
//#define POLARSSL_SHA2_C
//#define POLARSSL_SHA4_C
//#define POLARSSL_SSL_SRV_C
//#define POLARSSL_TIMING_C
//#define POLARSSL_X509_WRITE_C
//#define POLARSSL_XTEA_C
So I will show you how to add some compiler optimization on your code with keil that will remove allot of the unused code. Unfortunately, it would require allot of #ifdef code to do this manually. First off, open you project settings. on the target Tab check use microLIB and Use Link-Time Code Generation. The last one will make it a little longer to compile but will save you around 4k in program space.Next click on the C/C++ tab and set optimization to 3. Also check the on ELF Section per Function. this will remove all unused functions from your code. Setting optimization to level 3 will make it harder to debug but will get your code under 32k
Now with MicroLib, you will not be able to compile. The reason for that is the time function is not coded so we need to code one our self. Simply create and a file and add a empty function like this
#include <time.h>
time_t time ( time_t * timer )
{
return 0;
}
Pollar SSL uses the Time function to determine if the session has expired and if we can reuse past session keys. Making the time function all ways return 0, will force to renegotiate the keys and cause it to take more time. we will have to deal with that for now. I will later change it to return the right time but for now it's not a priority.
That is it. using the application created in the previous blog you can send https request or posts.
There are other ways to connecting to the internet thought. you can use different modules like the network shield they have for the arduino http://www.sparkfun.com/products/9026 or one like this http://www.sparkfun.com/products/9473 that supports sockets or a wifi one like this http://www.sparkfun.com/products/10050.
I want to get this code working with the last one. I will post my changes once I get the part.
Forwarding the serial port to the net
This section will discus reading the serial port and forwarding the content to a socket and vise versa in C++. I will be using specific api from win32 so it will not build in linux. If I ever see interest on getting it working on linux, I will write one for linux. I will only cover briefly the socket information since there are so many in depth tutorials out there.
For starters we will need to create a new visual studio console application project. This is pretty basic and could be different from version so I will assume you can do this on your own. I will be using visual studio 2010
Now that we have our applications, we need to include the following.
we also need to include the lib file so the linker knows where to get the functions from.
simply add #pragma comment(lib, "Ws2_32.lib") after all your includes
Setting up the the socket
the first thing we need to add before using any winsoc, is WSAStartup. It takes a version number as a parameter (we are using version 2) and a struct that will be use by its internals.
read more
read more
The inet_addr function converts an IP string to a long used by winsock.
read more
and the htons converts the windows byte order to the network byte order as some system have different byte orders.
read more
memset(&addrInfo,0,sizeof(sockaddr_in));
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons(80); /http port
addrInfo.sin_addr.S_un.S_addr = inet_addr("74.125.226.81"); //Google's IP adress
Now that we have all our information we need to create the socket and connect it.
we create the socket with the socket function. This function takes 3 parameters.
First we specify that the address is a IPv4 adress, we what stream socket (goes in hand with tcp) as opposed to a data gram and we want to use TCP protocol.
read more
The connect takes the newly created socket and the addrInfo we created just 1 step before.
if (connect(hSocket, (sockaddr*)(&addrInfo), sizeof(addrInfo))!=0)
{
EXIT("can't connect\n");
}
Finaly I added this line so that when I read from the socket it does not block untill data is received
Setting up the Serial port
We will open the serialport using the function CreateFile. The file that we will use is COM# where the # is the number of the comport. For now I will use COM1. We will also be using a struct called DCB. This struct contains information about the serial port configurations that we will use. Note that these settings must match with those used with you STM32 Discovery
read more
We will also use the function SetCommState. This function simply applies the new settings.
read more
The Loop
Now we simply need to read from the serial port and send it to the net and then read from the net and send it to the serial port. We will use ReadFile and WriteFile to read an write to the serial port. We will pass the following parameter. the handle to the Serial port, the pointer to the buffer, the size of the buffer or the content and the address to a variable that will contain the number of bytes sent or read. we set the last parameter to null since it only used if you need to share access to the file and we don't.
We will be using recv and send to read and write to our socket. These functions take similar parameters except instead of passing the adress to a variable to know how many byte were read or sent, it is return.
here is my loop
now if you run this and you run your STM32 Dsicovery, what ever you send to serial will then be sent to (google in this example) the net. Try doing a http get and get this page?
Next I will be posting on how to get HTTPS working on the stm32 Discover using this as your connection.
I will also discuss what else you can use to connect to the internet
For starters we will need to create a new visual studio console application project. This is pretty basic and could be different from version so I will assume you can do this on your own. I will be using visual studio 2010
Now that we have our applications, we need to include the following.
#include <stdio.h>
#include <winsock.h>
#include <Windows.h>
I read that some people have trouble with the linker depending on the order of the include. This order worked for me. I also put the includes in my precompiled header (stdafx.h).#include <winsock.h>
#include <Windows.h>
we also need to include the lib file so the linker knows where to get the functions from.
simply add #pragma comment(lib, "Ws2_32.lib") after all your includes
Setting up the the socket
the first thing we need to add before using any winsoc, is WSAStartup. It takes a version number as a parameter (we are using version 2) and a struct that will be use by its internals.
read more
WSAData wsaData;
if ( WSAStartup(MAKEWORD(2,0), &wsaData))
{
printf("could not load WSAStratup");
return -1;
}
Now we need to prepare our connection information. We will use the sockaddr_in to store all the information needed for the socket. There are a couple different types of sockaddr but for IPv4 sockaddr_in is the one we use. read more
The inet_addr function converts an IP string to a long used by winsock.
read more
and the htons converts the windows byte order to the network byte order as some system have different byte orders.
read more
SOCKET hSocket;
sockaddr_in addrInfo;memset(&addrInfo,0,sizeof(sockaddr_in));
addrInfo.sin_family = AF_INET;
addrInfo.sin_port = htons(80); /http port
addrInfo.sin_addr.S_un.S_addr = inet_addr("74.125.226.81"); //Google's IP adress
Now that we have all our information we need to create the socket and connect it.
we create the socket with the socket function. This function takes 3 parameters.
First we specify that the address is a IPv4 adress, we what stream socket (goes in hand with tcp) as opposed to a data gram and we want to use TCP protocol.
read more
The connect takes the newly created socket and the addrInfo we created just 1 step before.
hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket==INVALID_SOCKET)
{
EXIT("can't create Socket\n");
}EXIT("can't create Socket\n");
if (connect(hSocket, (sockaddr*)(&addrInfo), sizeof(addrInfo))!=0)
{
EXIT("can't connect\n");
}
Finaly I added this line so that when I read from the socket it does not block untill data is received
ioctlsocket(hSocket,FIONBIO,&timeout);
Setting up the Serial port
We will open the serialport using the function CreateFile. The file that we will use is COM# where the # is the number of the comport. For now I will use COM1. We will also be using a struct called DCB. This struct contains information about the serial port configurations that we will use. Note that these settings must match with those used with you STM32 Discovery
read more
We will also use the function SetCommState. This function simply applies the new settings.
read more
HANDLE hSerial;
hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
//Comm settings
DCB serialInfo = {0};
serialInfo.DCBlength=sizeof(serialInfo);
serialInfo.BaudRate=CBR_14400;
serialInfo.ByteSize=8;
serialInfo.StopBits=ONESTOPBIT;
serialInfo.Parity=NOPARITY;
SetCommState(hSerial, &serialInfo);
serialInfo.BaudRate=CBR_14400;
serialInfo.ByteSize=8;
serialInfo.StopBits=ONESTOPBIT;
serialInfo.Parity=NOPARITY;
SetCommState(hSerial, &serialInfo);
The Loop
Now we simply need to read from the serial port and send it to the net and then read from the net and send it to the serial port. We will use ReadFile and WriteFile to read an write to the serial port. We will pass the following parameter. the handle to the Serial port, the pointer to the buffer, the size of the buffer or the content and the address to a variable that will contain the number of bytes sent or read. we set the last parameter to null since it only used if you need to share access to the file and we don't.
We will be using recv and send to read and write to our socket. These functions take similar parameters except instead of passing the adress to a variable to know how many byte were read or sent, it is return.
here is my loop
char* readbuffer = new char[READ_BUFFER_SIZE];
char* writebuffer = new char[BUFFER_2_SIZE];
DWORD readlen = 0, bytesLeft = READ_BUFFER_SIZE;
int writelen = 0;
do
{
//reset the variables
readlen = 0;
writelen = 0;
//read data from serial port
ReadFile(hSerial,readbuffer,READ_BUFFER_SIZE,&readlen,0);
//was there actual data read?
if(readlen)
{
//send the bytes to the serial port
bytesLeft = readlen;
while(bytesLeft)
{
//did we send all the byte?
bytesLeft -= send(hSocket,readbuffer+(readlen - bytesLeft),bytesLeft,0);
}
}
//read the data from the net
writelen = recv(hSocket,writebuffer,WRITE_BUFFER_SIZE,0);
//did we receive any thing?
if(writelen>0)
{
bytesLeft = writelen;
while(bytesLeft) //did we send every thing to the serial port?
{
DWORD byteSent = 0;
//send to the serial port
WriteFile(hSerial,writebuffer + (writelen - bytesLeft),bytesLeft,&byteSent,0);
bytesLeft -= byteSent;
}
}
else if(writelen < 0)
{
//we go an error we can ignore it if err == WSAEWOULDBLOCK
long err = WSAGetLastError ();
}
if(writelen > 0 && readlen == 0)
{
Sleep(100);
}
}while(writelen); // the socket is closed
now if you run this and you run your STM32 Dsicovery, what ever you send to serial will then be sent to (google in this example) the net. Try doing a http get and get this page?
Next I will be posting on how to get HTTPS working on the stm32 Discover using this as your connection.
I will also discuss what else you can use to connect to the internet
Monday, January 31, 2011
STM32 Discovery: The Basics - Buffering the USART and Error Handling
So this part is mostly coding a linked list. probably not the most efficient linked list either but it servers its purpose does not have much over head too. The code also does not do checks if pointer is null and such and assumes you will be able to add that. The code has room for improvement but this is to give a basic idea.
so I created 2 files urat.h and uart.c. I will not describe adding the function signatures in the header and assume you will do that.
we will first need to declare out struct for the linked list.
We also nee a couple global variables
//a pointer to the buffer we are currently reading from
extern LinkedReadWriteBuffer* receiveReadBuffer;
//the pointer to the buffer we are writing to
extern LinkedReadWriteBuffer* receiveWriteBuffer;
We will define these variables in uart.c
now in our main we will need to instantiate those variables
now we are ready to tackle uart.c
The first function we need to define is initRWBuff() this function will initialize a buffer to its default values.
here is what mine looks like
void initRWBuff(ReadWriteBuffer * tmpBuff)
{
tmpBuff->buffer = (int*)malloc( sizeof(int) * tmpBuff->size);
if(!tmpBuff->buffer)
{
HardFault_Handler();
}
tmpBuff->writeAt = 0 ;
tmpBuff->readAt = 0 ;
}
So now we need to write the the buffer every time we receive a byte from our interrupt routine. Basicalyy we will read the char we are receiving and insert it into the buffer. we will increment our counter of where the last byte was written. If the buffer if full, we will create a new buffer to write in the next time. It would probably be more efficent to have it create the buffer only one we need to use it.
Here is what my routine looks like
here is what it looks like:
int jm_recv(void* uart, unsigned char * buff, int size)
{
int i=0;
for(;i<size && receiveReadBuffer->buffer.readAt < receiveReadBuffer->buffer.writeAt;i++)
{
buff[i] = (char) receiveReadBuffer->buffer.buffer[receiveReadBuffer->buffer.readAt];
receiveReadBuffer->buffer.readAt++;
if(receiveReadBuffer->buffer.readAt == receiveReadBuffer->buffer.size)
{
if(receiveReadBuffer->next)
{
LinkedReadWriteBuffer* p = receiveReadBuffer;
receiveReadBuffer = receiveReadBuffer->next;
free(p->buffer.buffer);
free(p);
}
}
}
return i;
}
if you want o wrap SendByte with a similar signature. here what I did.
now for simple error handling we just need to override the error functions we want to handle. similar to our IRQ. I simply Added this under the main.
void HardFault_Handler(void)
{
int error = 0;
while(1);
}
Eventually I will make it reboot the device. a few more error functions you can override are
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
You can see the list in STM32F10x.s
Finlay you might want a larger stack then the default. the stm32 Discovery has 92kb of ram. I set my stack to half of that. Open up STM32F10x.s and click the Configuration Wizard at the bottom. open the stack Configuration and increase the Stack size. I set mine to 0x14000.
The next post will cover using the usart to connect to a pc that will then connect to the internet. I will also suggest other ways to connect to the internet with out the PC
so I created 2 files urat.h and uart.c. I will not describe adding the function signatures in the header and assume you will do that.
we will first need to declare out struct for the linked list.
#define READ_BUFF_SIZE 1024
//structs
typedef struct ReadWriteBufferStruct
{
int size;
int readAt; //the next byte to read
int writeAt; //the next byte to write
int* buffer;
}ReadWriteBuffer ;
typedef struct LinkedReadWriteBufferStruct
{
struct LinkedReadWriteBufferStruct* next;
ReadWriteBuffer buffer;
}LinkedReadWriteBuffer ;
We also nee a couple global variables
//a pointer to the buffer we are currently reading from
extern LinkedReadWriteBuffer* receiveReadBuffer;
//the pointer to the buffer we are writing to
extern LinkedReadWriteBuffer* receiveWriteBuffer;
We will define these variables in uart.c
now in our main we will need to instantiate those variables
receiveReadBuffer = (LinkedReadWriteBuffer*) malloc(sizeof(LinkedReadWriteBuffer) ) ;
receiveWriteBuffer = receiveReadBuffer;
receiveReadBuffer->buffer.size = READ_BUFF_SIZE;
receiveWriteBuffer = receiveReadBuffer;
receiveReadBuffer->buffer.size = READ_BUFF_SIZE;
initRWBuff(&(receiveReadBuffer->buffer));
now we are ready to tackle uart.c
The first function we need to define is initRWBuff() this function will initialize a buffer to its default values.
here is what mine looks like
void initRWBuff(ReadWriteBuffer * tmpBuff)
{
tmpBuff->buffer = (int*)malloc( sizeof(int) * tmpBuff->size);
if(!tmpBuff->buffer)
{
HardFault_Handler();
}
tmpBuff->writeAt = 0 ;
tmpBuff->readAt = 0 ;
}
So now we need to write the the buffer every time we receive a byte from our interrupt routine. Basicalyy we will read the char we are receiving and insert it into the buffer. we will increment our counter of where the last byte was written. If the buffer if full, we will create a new buffer to write in the next time. It would probably be more efficent to have it create the buffer only one we need to use it.
Here is what my routine looks like
void USART1_IRQHandler (void)
{
volatile unsigned int IIR;
IIR = USART1->SR;
if (IIR & USART_FLAG_RXNE) // read interrupt
{
USART1->SR &= ~USART_FLAG_RXNE; // clear interrupt
receiveWriteBuffer->buffer.buffer[receiveWriteBuffer->buffer.writeAt] = (USART1->DR & 0x1FF);
receiveWriteBuffer->buffer.writeAt++;
if(receiveWriteBuffer->buffer.writeAt == receiveWriteBuffer->buffer.size)
{
LinkedReadWriteBuffer* tmpBuff;
tmpBuff = (LinkedReadWriteBuffer*) malloc(sizeof(LinkedReadWriteBuffer) ) ;
if(!tmpBuff)
{
HardFault_Handler();
}
tmpBuff->buffer.size = READ_BUFF_SIZE;
if(!tmpBuff->buffer.size)
{
HardFault_Handler();
}
tmpBuff->next = 0 ;
initRWBuff(&(tmpBuff->buffer));
receiveWriteBuffer->next = tmpBuff;
receiveWriteBuffer = tmpBuff;
}
}
}
now we need to create a function that will read the buffer and skip to the next one when we get to the end. I will call mine my_recv() because I will use this function else where for socket connection. {
volatile unsigned int IIR;
IIR = USART1->SR;
if (IIR & USART_FLAG_RXNE) // read interrupt
{
USART1->SR &= ~USART_FLAG_RXNE; // clear interrupt
receiveWriteBuffer->buffer.buffer[receiveWriteBuffer->buffer.writeAt] = (USART1->DR & 0x1FF);
receiveWriteBuffer->buffer.writeAt++;
if(receiveWriteBuffer->buffer.writeAt == receiveWriteBuffer->buffer.size)
{
LinkedReadWriteBuffer* tmpBuff;
tmpBuff = (LinkedReadWriteBuffer*) malloc(sizeof(LinkedReadWriteBuffer) ) ;
if(!tmpBuff)
{
HardFault_Handler();
}
tmpBuff->buffer.size = READ_BUFF_SIZE;
if(!tmpBuff->buffer.size)
{
HardFault_Handler();
}
tmpBuff->next = 0 ;
initRWBuff(&(tmpBuff->buffer));
receiveWriteBuffer->next = tmpBuff;
receiveWriteBuffer = tmpBuff;
}
}
}
here is what it looks like:
int jm_recv(void* uart, unsigned char * buff, int size)
{
int i=0;
for(;i<size && receiveReadBuffer->buffer.readAt < receiveReadBuffer->buffer.writeAt;i++)
{
buff[i] = (char) receiveReadBuffer->buffer.buffer[receiveReadBuffer->buffer.readAt];
receiveReadBuffer->buffer.readAt++;
if(receiveReadBuffer->buffer.readAt == receiveReadBuffer->buffer.size)
{
if(receiveReadBuffer->next)
{
LinkedReadWriteBuffer* p = receiveReadBuffer;
receiveReadBuffer = receiveReadBuffer->next;
free(p->buffer.buffer);
free(p);
}
}
}
return i;
}
if you want o wrap SendByte with a similar signature. here what I did.
int jm_send(void * param, unsigned char * buff, int size)
{
int i;
for(i=0;i<size;i++)
{
while (!(USART1->SR & USART_FLAG_TXE));
USART1->DR = ((buff[i]) & 0xFF);
}
return size;
}
I warped these this way because I will be using them with polarssl {
int i;
for(i=0;i<size;i++)
{
while (!(USART1->SR & USART_FLAG_TXE));
USART1->DR = ((buff[i]) & 0xFF);
}
return size;
}
now for simple error handling we just need to override the error functions we want to handle. similar to our IRQ. I simply Added this under the main.
void HardFault_Handler(void)
{
int error = 0;
while(1);
}
Eventually I will make it reboot the device. a few more error functions you can override are
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
You can see the list in STM32F10x.s
Finlay you might want a larger stack then the default. the stm32 Discovery has 92kb of ram. I set my stack to half of that. Open up STM32F10x.s and click the Configuration Wizard at the bottom. open the stack Configuration and increase the Stack size. I set mine to 0x14000.
The next post will cover using the usart to connect to a pc that will then connect to the internet. I will also suggest other ways to connect to the internet with out the PC
Labels:
ARM,
C++,
electronic,
embed,
Keil,
Micro controller,
STM32
Subscribe to:
Posts (Atom)