Pumpkin, Inc.

Pumpkin User Forums

Superloop C to Salvo RTOS

If you can't make Salvo do what you want it to do, post it here.

Superloop C to Salvo RTOS

Postby aastokes » Thu Nov 01, 2007 4:15 am

Hello,

Hoping someone will be able to help. Below is my code for running a SiLabs C8051F120 MCU with an AB4 Ethernet Board, this has all been coded in a "superloop" structure and I'm having trouble working out how to run my output clocks and motor direction signals in "real time". The control directions are of lower priority than home and end switch inputs. I'm just looking for an approach to convert this code into Salvos style code. Any suggestions welcomed... also should I run the disable watchdog as a task and how do I make it run only once and before everything else?

Any help much appreciated.

Adam.

code:
#include "CGI_DHTML_type.h"							// to be the first of include file
#include "mn_userconst.h" // TCP/IP Library Constants
#include "mn_stackconst.h" // TCP/IP Library Constants
#include "mn_errs.h" // Library Error Codes
#include "mn_defs.h" // Library Type definitions
#include "mn_funcs.h" // Library Function Prototypes
#include "VFILE_DIRindex.h"


//------------------------------------------------------------------------------
// Function Prototypes
//------------------------------------------------------------------------------

// Initialization Routines
void PORT_Init (void);
void SYSCLK_Init (void);
void EMIF_Init(void);
int establish_network_connection();

// CGI Script
void get_data_func( PSOCKET_INFO socket_ptr );

//-----------------------------------------------------------------------------
// Main Routine
//-----------------------------------------------------------------------------
void main(void)
{
int retval;

// Disable watchdog timer
#ifdef C8051F120_H
WDTCN = 0xde;
WDTCN = 0xad;
#endif
#ifdef C8051F340_H
PCA0MD = 0x00;
#endif

// Initialize the MCU
PORT_Init();
SYSCLK_Init();
EMIF_Init();

while(1)
{
// Initialize the TCP/IP stack.
if (mn_init() < 0)
{
// If code execution enters this while(1) loop, the stack failed to initialize.
// Verify that all boards are connected and powered properly.
while(1);
}

// Connect to the network
establish_network_connection();

// Add web page to virtual file system.
// The main page MUST be called index.htm or index.html.
mn_vf_set_entry((byte *)"index.html", INDEX_SIZE, index_html, VF_PTYPE_FLASH);

// Add CGI Script to Virtual File System
mn_pf_set_entry
(
(byte*)"get_data", // Script Name (ASCII)
get_data_func // Function Pointer
);

// Start the Application Layer Services
// If this routine exits, check the return value for an error code.
retval = mn_server();

}
}

//-----------------------------------------------------------------------------
// establish_network_connection
//-----------------------------------------------------------------------------
//
// This function calls mn_ether_init() to initialize the CP2200 and attach to
// the network.
//
// If there is a network connection, the function returns 1.
//
// In the call to mn_ether_init(), NUM_AUTONEG_ATTEMPTS is set to 0, so the
// function will not return until it successfully auto-negotiates.
//
// mn_ether_init() will not be a blocking call if NUM_AUTONEG_ATTEMPTS is set
// to a value greater than 0.
//
int establish_network_connection()
{
int retval;

do
{
// mn_ether_init() initializes the Ethernet controller.
// AUTO_NEG indicates that the controller will auto-negotiate.
retval = mn_ether_init(AUTO_NEG, 0, 0);

// If there is no link, poll link_status until it sets or the
// CP2200 resets and then call mn_ether_init() again.
if (retval == LINK_FAIL)
{
while(!link_status && !ether_reset);
}

// If retval is less than zero and is not LINK_FAIL, there is a
// hardware error.
else if (retval < 0)
{
// Verify that the Ethernet controller is connected and powered properly.
// Verity that the EMIF has been configured at a speed compatible with the
// Ethernet controller.
while(1);
}

}while(retval < 0);

return (1);

}


//-----------------------------------------------------------------------------
// Initialization Routines
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Configure the Interrupts, Crossbar and GPIO ports
//
void PORT_Init (void)
{
#ifdef C8051F120_H
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = CONFIG_PAGE; // set SFR page
P1MDOUT |= 0x40; // Set P1.6(TB_LED) to push-pull
P2MDOUT |= 0x08; // Set P2.2(AB4_LED1)
P4MDOUT = 0xC0; // /WR, /RD to push-pull
P5MDOUT = 0xFF; // Address line to push-pull
P6MDOUT = 0xFF;
P7MDOUT = 0xFF; // data line to push-pull

// Enable UART0, CP0, and /INT0. This puts /INT0 on P0.3.
XBR0 = 0x84;
XBR1 = 0x04;
XBR2 = 0x40;

P4 = 0xDF; // /WR, /RD, are high, RESET is low
P5 = 0xFF;
P6 = 0xFF; // P5, P6 contain the address lines
P7 = 0xFF; // P7 contains the data lines

SFRPAGE = TIMER01_PAGE; // set SFR page
TCON &= ~0x01; // Make /INT0 level triggered

SFRPAGE = SFRPAGE_SAVE; // Restore SFR page
#endif

#ifdef C8051F340_H
IT01CF = 0x03; // Enable Interrupt 0 on P0.3
TCON &= ~0x01; // Make /INT0 level triggered

XBR0 = 0x01; // Enable UART on P0.4(TX) and P0.5(RX)
XBR1 = 0x40; // Enable crossbar and enable
// weak pull-ups

P0MDOUT |= 0x10; // enable UTX as push-pull output
P1MDOUT |= 0xD8; // /WR and /RD are push-pull
// AB4 LEDs are push-pull
P2MDOUT |= 0xFF;
P3MDOUT |= 0xFF;
P4MDOUT |= 0xFF;
#endif
}

//-----------------------------------------------------------------------------
// EMIF_Init
//-----------------------------------------------------------------------------
//
// Configure the External Memory Interface for both on and off-chip access.
//
void EMIF_Init (void)
{
#ifdef C8051F120_H
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = LEGACY_PAGE;

EMI0CF = 0x3B; // Split-mode, non-multiplexed on P4 - P7
#endif
#ifdef C8051F340_H
EMI0CF = 0x1B; // non-muxed mode; split mode
// with bank select
#endif
EMI0TC = EMIF_TIMING; // This constant may be modified
// according to SYSCLK to meet the
// timing requirements for the CP2200
EMI0CN = BASE_ADDRESS; // Page of XRAM accessed by EMIF

#ifdef C8051F120_H
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page
#endif
}

//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// This routine initializes the system clock.
//
void SYSCLK_Init (void)
{
int i = 0;

#ifdef C8051F120_H
char SFRPAGE_SAFE = SFRPAGE;
SFRPAGE = CONFIG_PAGE;
OSCICN = 0x83;
OSCXCN = 0x00;
PLL0CN = 0x01;
SFRPAGE = LEGACY_PAGE;
FLSCL = 0x30;
SFRPAGE = CONFIG_PAGE;
PLL0DIV = 0x01;
PLL0MUL = 0x04;
PLL0FLT = 0x01;
for (i=0; i < 256; i++); // Wait at least 5us
PLL0CN |= 0x02; // Enable the PLL
while(!(PLL0CN & 0x10)); // Wait until PLL frequency is locked
CLKSEL = 0x02;
SFRPAGE = SFRPAGE_SAFE;
#endif

#ifdef C8051F340_H
OSCICN |= 0x03; // Configure internal oscillator for
// its maximum frequency
CLKMUL = 0x00; // Reset Clock Multiplier and select
// internal oscillator as input source
CLKMUL |= 0x80; // Enable the Clock Multiplier
for(i = 0; i < 256; i++); // Delay at least 5us
CLKMUL |= 0xC0; // Initialize the Clock Multiplier
while(!(CLKMUL & 0x20)); // Wait for MULRDY => 1
RSTSRC = 0x06; // Enable missing clock detector
// and VDD monitor
FLSCL |= 0x10; // Set Flash Scale for 48MHz
CLKSEL |= 0x03; // Select output of clock multiplier
// as the system clock.
#endif
}


//-----------------------------------------------------------------------------
// ether_reset_low
//-----------------------------------------------------------------------------
//
// This routine drives the reset pin of the ethernet controller low.
//
#ifdef C8051F120_H
sbit reset_port = P4^5;
#endif
#ifdef C8051F340_H
sbit reset_port = P1^0;
#endif

void ether_reset_low()
{
#ifdef C8051F120_H
unsigned char SFRPAGE_Save;
SFRPAGE_Save = SFRPAGE; // Save Current SFR page
SFRPAGE = CONFIG_PAGE; // Switch to ports SFR page
#endif

reset_port = 0; // Pull reset low

#ifdef C8051F120_H
SFRPAGE = SFRPAGE_Save; // Restore SFR page
#endif
}

//-----------------------------------------------------------------------------
// ether_reset_high
//-----------------------------------------------------------------------------
//
// This routine places the reset pin in High-Z allowing it to be pulled up
// using the external pull-up resistor.
//
// Additionally, this routine waits for the reset pin to read high before
// exiting.
//
void ether_reset_high (void)
{
#ifdef C8051F120_H
unsigned char SFRPAGE_Save;
SFRPAGE_Save = SFRPAGE; // Save Current SFR page
SFRPAGE = CONFIG_PAGE; // Switch to ports SFR page
#endif

reset_port = 1; // Allow /RST to rise
while( !reset_port ); // Wait for /RST to go high

#ifdef C8051F120_H
SFRPAGE = SFRPAGE_Save; // Restore SFR page
#endif
}

//-----------------------------------------------------------------------------


#include <stdio.h>
#include <string.h>

#define CGI_REPORT_LEN 32
#define CGI_MSGLEN 32

unsigned char xdata cgi_reportBuf[ CGI_REPORT_LEN ];

#define X_DIV 100
#define Y_DIV 100
static signed int pos_x = 0;
static signed int pos_y = 0;

//-----------------------------------------------------------------------------
// CGI Script: get_data_func
//-----------------------------------------------------------------------------
//
// This routine is called when the following is typed into the address bar
// of a web browser:
//
// http://<ip-address>/get_data?motor_speed=xx&motor_current=yy
//
// where <ip-address> = the IP address of the embedded system
//

void get_data_func (PSOCKET_INFO socket_ptr)
{
byte msg_buf[ CGI_MSGLEN ];
byte xdata * buf = cgi_reportBuf;

buf[ 0 ] = ''; // clear the output buffer

// when the query includes X position
if ( mn_http_find_value( BODYptr, (byte*)"pos_x", msg_buf ) ) {
// output X data
buf += sprintf(buf, "pos_x=%d&", pos_x);
}
// when the query includes Y position
if ( mn_http_find_value( BODYptr, (byte*)"pos_y", msg_buf ) ) {
// output Y data
buf += sprintf(buf, "pos_y=%d&", pos_y);
}
// when the query includes button HOME
if ( mn_http_find_value( BODYptr, (byte*)"btnH", msg_buf ) ) {
pos_x = pos_y = 0;
}
// when the query includes button up
if ( mn_http_find_value( BODYptr, (byte*)"btnU", msg_buf ) ) {
pos_y += Y_DIV;
}
// when the query includes button down
if ( mn_http_find_value( BODYptr, (byte*)"btnD", msg_buf ) ) {
pos_y -= Y_DIV;
}
// when the query includes button left
if ( mn_http_find_value( BODYptr, (byte*)"btnL", msg_buf ) ) {
pos_x -= X_DIV;
}
// when the query includes button right
if ( mn_http_find_value( BODYptr, (byte*)"btnR", msg_buf ) ) {
pos_x += X_DIV;
}

// send back the buffer contents
buf += sprintf(buf, "
");
socket_ptr->send_ptr = cgi_reportBuf;
socket_ptr->send_len = (word16)(buf - cgi_reportBuf);
}


[This message has been edited by aek (edited November 01, 2007).]

aastokes
 
Posts: 5
Joined: Mon Oct 29, 2007 11:00 pm
Location: Edinburgh, Scotland

Re: Superloop C to Salvo RTOS

Postby aek » Thu Nov 01, 2007 6:41 am

OK, a couple of hints:

1) Stuff that needs to be done once can be done in a variety of ways, e.g.

i) In main(), before tasks start.
ii) In the preamble of a task, before the task's infinite loop
iii) In a "startup task", the only task to be created in main(), which then creates all the other tasks as it progresses, and then ends in an OS_Stop().

2) What you want to do is group like functionality into tasks. E.g. you can probably put all your Ethernet stuff into a single task -- init in the preamble, and repeated / regularly executing stuff into the main, infinite loop. With stuff like TCP/IP stacks, you need to setup the task so that it runs often enough (if it has to poll something) to be responsive. Priority will be based on the task's functionality and relationship to the rest of the application.

The key thing here is that you're now working in a loosely coupled environment, where the focus is not linear execution (superloop) but priority-based, event-driven execution where each new bit of functionality you write is likely to be "packaged" into a task.

------------------

-------
aek
aek
 
Posts: 1888
Joined: Sat Aug 26, 2000 11:00 pm

Re: Superloop C to Salvo RTOS

Postby aek » Thu Nov 01, 2007 6:46 am

One more thing -- see Salvo's "golden rules" in the Reference Chapter of the User Manual.

One common mistake of newbie Salvo users is to e.g. call OS_Delay() from a function that's called by a task. This violates one of Salvo's golden rules -- "Thou shalt only invoke context switches (OS_Xyz()) from the task level." That's because it would require much more memory (which you don't have much on in an 8051) to be able to context-switch at arbitrarily deep call levels. So ultimately this restriction forces you to keep your tasks relatively short (for comprehension) and "flat". While somewhat restrictive, it's also IMO beneficial in that it keeps things simple, easier to understand, and easier to debug.

------------------

-------
aek
aek
 
Posts: 1888
Joined: Sat Aug 26, 2000 11:00 pm


Return to Coding

Who is online

Users browsing this forum: No registered users and 3 guests

cron