text: Contents
  - 1 Assignment
  - 2 Assessment
  - 3 Hints
  - 3.1 Resources
  - 3.2 Things to avoid
  - 3.3 Working with floating point numbers
  - 3.4 How to generate PWM on MPC5200
  - 3.5 How to handle IRC interrupts
  - 3.6 Use of on-board LEDs
  - 3.7 Web server
  - 3.8 FAQ
  1 Assignment
  The goal of the semestral work is to create a digital motor controller. Your program will control the position of the motor according to the set-point given by the position of another motor, moved by hand (steer-by-wire). The set-point will be transferred between the two motors using UDP messages. The actual state of the controller and its history will be published as live graphs over the HTTP protocol.
  1.1 Version Control
  Work in couples and use a version control system. We expect commits from both of you in the repository. You can use any open source version control software. We manage GIT repositories that you can use, but it is possible to use any other repository (and version control system) which we can access.
  1.2 More Details
  The motor is connected to the MPC5200B-based board and the goals of the control application are to:
  Read the motor position by decoding the IRC sensor signal. This two phase signal is connected to CPU signals TIMER_4 and TIMER_5 (see RYU_EDU_midam v1.B schematics) that should be configured as inputs. The motor contains a circuit for detecting changes in the IRC signals and asserts IRQ0 interrupt signal on every change.
  Generate the PWM signal to rotate the motor. Power stage inputs are connected to the CPU outputs of TIMER_1 and TIMER_2. The corresponding hardware timers in the CPU can be configured to generate the PWM signal.
  Implement PID controller to control the shaft position.
  Implement UDP communication from one board to another. Use standard BSD sockets API.
  The controller should contain a web-server, which serves live graphs of at least:
  - actual motor position,
  - requested position and
  - current PWM duty cycle.
  The graphs should show at least 2 seconds of history.
  There is a more detailed description of the motor, its connection to PC and basic implementation hints. The document deals with motor control under RT Linux (different RTOS than VxWorks), but the general ideas presented there can be helpful even for VxWorks.
  2 Assessment
  Semestral work assessment comprises the following independently assessed parts:
  2.1 Motor control program (max. 18 points)
  Rows marked by * represent the minimum for "zápočet".
  2.2 Use of version control system (max. 4 points)
  You must use a distributed version control system. Git is strongly recommended. See the instructions for using Git on our server. There will be two check dates:
  - December 14, 2016 and
  - when submitting the work.
  We will evaluate the items from the table below.
  It is required that both persons from the team have commits in the repository.
  2.3 Programmer's documentation (max. 3 points)
  Use tools that generate the documentation automatically from the source code (e.g. Doxygen). Documentation should contain:
  Look at example documentation created with Doxygen and the corresponding source code. Text-based part of the documentation is contained in file CANR_16X.H.
  3 Hints
  3.1 Resources
  - Shark mother board documentation
  - RYU_EDU_midam v1.B schematics
  - Motor driver schematics, internal connector
  - MPC5200B User's manual - PWM generation is described in section 7.4 (General Purpose Timers), IRC is connected to the GPIO (section 7.3).
  - Source code of Linux driver for the motor
  - Addresses of MPC5200 hardware registers are defined in /opt/WindRiver/vxworks-6.9/target/h/arch/ppc/ppc5200.h.
  - How to access the registers can be seen in /opt/WindRiver/vxworks-6.9/target/src/drv/timer/m5200GPTimer.c and /opt/WindRiver/vxworks-6.9/target/h/drv/timer/m5200GPTimer.h.
  3.2 Things to avoid
  (you should know why from lectures)
  - Calling blocking functions (e.g. printf()) in interrupt handlers.
  - Using floating point operations in interrupt handlers.
  3.3 Working with floating point numbers
  You must include the VX_FP_TASK option when creating a task that does any of the following:
  - Performs floating-point operations.
  - Calls any function that returns a floating-point value.
  - Calls any function that takes a floating-point value as an argument.
  For example:
  tid = taskSpawn ("tMyTask", 90, VX_FP_TASK, 20000, myFunc, 2387, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  Some routines perform floating-point operations internally. The VxWorks documentation for each of these routines clearly states the need to use the VX_FP_TASK option.
  3.4 How to generate PWM on MPC5200
  PWM generation is controlled by setting GPT (General Purpose Timers) processor registers. The addresses and symbolic names of GPT registers are defined in/opt/WindRiver/vxworks-6.9/target/h/arch/ppc/ppc5200.h and /opt/WindRiver/vxworks-6.9/target/config/lite5200b. In ppc5200.h, you can find a section with useful macros that simplify the work with GPT registers.
  To successfully include this file, it is necessary to modify search path in project properties "Build Properties->Build Paths". Add there a line containing: -I$(WIND_BASE)/target/config/lite5200b. Then include the following lines in your source code:
  #include "vxWorks.h" #include <arch/ppc/ppc5200.h> #include <lite5200b.h>
  For PWM generation, the following registers are important: CIR PWMCR and EMSR. The upper word of CIR register contains prescaler (clock multiplier), which should be set to 0x1. The rest of the register indicates the length of the entire PWM period. The value to write there can be calculated by dividing the processor bus frequency (132 MHz) with desired PWM frequency (20 kHz). The upper word of PWMCR register contains PWM pulse duration in the same units as the PWM period in CIR register. Beware that specifying longer pulse width than the period means that the timer output will not change! Bit 23 in the lower word is used to set PWM polarity (1 - an active width of the PWM output is zero, 0 - otherwise). Finally, writing 0x3 to EMSR register will start generation of PWM signal.
  The above procedure must be repeated for both PWM channels. The connection between motor inputs and CPU timer channels can be seen in RYU_EDU board schematics. You can also take some inspiration from the Linux driver mentioned above.
  Example code:
   PWMF_TIMER_NUM 2 #define PWMB_TIMER_NUM 3 #define PWM_PERIOD 6600 /* = 132 000 kHz / 20 kHz */ void pwm_width(int width) { if (width >= 0) { if(width > PWM_PERIOD) width = PWM_PERIOD; *GPT_PWMCR(PWMF_TIMER_NUM) = (width<<16) | MPC52xx_GPT_PWM_OP; *GPT_PWMCR(PWMB_TIMER_NUM) = (0) | MPC52xx_GPT_PWM_OP; } else { width = -width; if(width > PWM_PERIOD) width = PWM_PERIOD; *GPT_PWMCR(PWMF_TIMER_NUM) = (0) | MPC52xx_GPT_PWM_OP; *GPT_PWMCR(PWMB_TIMER_NUM) = (width<<16) | MPC52xx_GPT_PWM_OP; } } void init_pwm() { pwm_width(0); *GPT_CIR(PWMF_TIMER_NUM) = (1<<16) | PWM_PERIOD; *GPT_CIR(PWMB_TIMER_NUM) = (1<<16) | PWM_PERIOD; *GPT_EMSR(PWMF_TIMER_NUM) = MPC52xx_GPT_MODE_PWM; *GPT_EMSR(PWMB_TIMER_NUM) = MPC52xx_GPT_MODE_PWM; } void disable_pwm() { *GPT_EMSR(PWMF_TIMER_NUM) = 0; *GPT_EMSR(PWMB_TIMER_NUM) = 0; }
  3.5 How to handle IRC interrupts
   IRCA_TIMER_NUM 4 #define IRCB_TIMER_NUM 5 void irc_irq_handler(int val) { unsigned int stateA; unsigned int stateB; stateA = (*GPT_SR(IRCA_TIMER_NUM) & MPC52xx_GPT_STATUS_PIN) != 0; stateB = (*GPT_SR(IRCB_TIMER_NUM) & MPC52xx_GPT_STATUS_PIN) != 0; m5200IntAck(INUM_IRQ0); } void irc_init() { // Initialize GPIOs for IRC input *GPT_EMSR(IRCA_TIMER_NUM) = MPC52xx_GPT_MODE_GPIO | MPC52xx_GPT_MODE_GPIO_IN; *GPT_EMSR(IRCB_TIMER_NUM) = MPC52xx_GPT_MODE_GPIO | MPC52xx_GPT_MODE_GPIO_IN; // Configure interrupt controller: Rising edge triggers interrupt *ICTL_EEETR |= ICTL_EEETR_ETYPE0_RISING_EDGE; intConnect(INUM_TO_IVEC(INUM_IRQ0), irc_irq_handler, 0); // Enable interrupt intEnable(INUM_IRQ0); } void irc_disable() { *GPT_EMSR(IRCA_TIMER_NUM) = 0; *GPT_EMSR(IRCB_TIMER_NUM) = 0; intDisable(INUM_IRQ0); intDisconnect(INUM_TO_IVEC(INUM_IRQ0), irc_irq_handler, 0); }
  Warning
  Don't forget to declare global variables used both from interrupt and task context as volatile!
  3.6 Use of on-board LEDs
  To control the on-board LEDs, it is necessary to set four registers. Their definition and useful macros can be found again in ppc5200.h header file (section GPIO). More detailed description can be found in MPC5200B User's manual starting from section 7.3.2.1. Since the PSC3 port, where are the LEDs connected to, can be multiplexed between GPIO, UART and SPI it is first necessary to configure it as GPIO by zeroing bits 20-23 in PCR register. Furthermore, we have to enable them by settings the same bits in SEN register. This made the pins PSC3.0 through PSC3.3 available to us. By setting additional bits, we can also enable PSC3.6 and PSC3.7 if needed. What remains is to set GPIO as outputs by settings bits 20-23 in SDD register. The voltage on the pins is controlled by setting/clearing the corresponding bits in SDO register.
  The code below implements the above procedure.
  // PSC3 = GPIO *GPIO_PCR &= 0xFFFFF0FF; // Enable PSC3.0 to PSC3.3 *GPIO_SEN |= 0x00000F00; // PSC3 -> OUT_MODE *GPIO_SDD |= 0x00000F00; // PSC3 OUT 0 - switch on all LEDs *GPIO_SDO = 0x00000F00;
  3.7 Web server
  3.7.1 Skeleton of a simple web server
  Example of communication between WWW browser and server
  Web server skeleton:
  #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <sockLib.h> #include <string.h> #define SERVER_PORT 80 /* Port 80 is reserved for HTTP protocol */ #define SERVER_MAX_CONNECTIONS 20 void www(int argc, char *argv[]) { int s; int newFd; struct sockaddr_in serverAddr; struct sockaddr_in clientAddr; int sockAddrSize; sockAddrSize = sizeof(struct sockaddr_in); bzero((char *) &serverAddr, sizeof(struct sockaddr_in)); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(SERVER_PORT); serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); s=socket(AF_INET, SOCK_STREAM, 0); if (s<0) { printf("Error: www: socket(%d)\n", s); return; } if (bind(s, (struct sockaddr *) &serverAddr, sockAddrSize) == ERROR) { printf("Error: www: bind\n"); return; } if (listen(s, SERVER_MAX_CONNECTIONS) == ERROR) { perror("www listen"); close(s); return; } printf("www server running\n"); while(1) { /* accept waits for somebody to connect and the returns a new file descriptor */ if ((newFd = accept(s, (struct sockaddr *) &clientAddr, &sockAddrSize)) == ERROR) { perror("www accept"); close(s); return; } /* The client connected from IP address inet_ntoa(clientAddr.sin_addr) and port ntohs(clientAddr.sin_port). Start a new task for each request. The task will parse the request and sends back the response. Don't forget to close newFd at the end */ } }
  3.7.2 Using fdopen() to generate server responses
  The easiest way to generate webserver responses is to use fdopen() function to "convert" the file descriptor returned by accept() to a FILE pointer. Then you can use all standard libc functions such as printf() to generate the response. For example:
  FILE *f = fdopen(newFd, "w"); fprintf(f, "HTTP/1.0 200 OK\r\n\r\n"); fprintf(f, "Current time is %ld.\n", time(NULL)); fclose(f);
  3.7.3 How to refresh a web page periodically?
  Use javascript code in the onload attribute of the body element as in the example below. This example reloads the page approximately every 100 milliseconds and shows actual time in milliseconds.
  <html> <head> <title>Test</title> </head> <body onload="setTimeout(function(){location.reload()}, 100);"> <script>document.write(Date.now());</script> </body> </html>
  3.7.4 Drawing graphs as SVG
  Look at this example for how to include SVG graphics in the html page.
  Direct link to the SVG source