Exemple d'un programme écrit en C et s'exécutant sur un ARM7


Le programme suivant (main.c) est un test pour l'interface série USB et la DEL de la carte Olimex DEV-00268 comprenant la puce Philips LPC2294. Les commentaires sont en anglais pour être le plus près possible de la terminologie utilisée par les manufacturiers.

// Author Richard St-Denis, Universite de Sherbrooke, 2013.
#include "uart.h"

// Addresses of SCB (System Control Block) registers
#define SCB_MEMMAP *((volatile unsigned int*)0xE01FC040)
// interrupt vectors are not re-mapped and reside in flash
#define MEMMAP_USER_FLASH_MODE (1 << 0)
#define SCB_PLLCON *((volatile unsigned int*)0xE01FC080)
#define PLLE 0         // enable PLL (Phase Locked Loop) 
#define PLLC 1         // connect PLL (Phase Locked Loop)
#define SCB_PLLCFG *((volatile unsigned int*)0xE01FC084)
#define PLL_M 4        // multiplier
#define MSEL (PLL_M-1) // multiplier - 1
#define PSEL0 5
#define PSEL1 6
#define SCB_PLLSTAT *((volatile unsigned int*)0xE01FC088)
#define PLOCK 10       // PLOCK bit
#define SCB_PLLFEED *((volatile unsigned int*)0xE01FC08C)
#define PLL_FEED1 0xAA // feed sequence
#define PLL_FEED2 0x55
#define SCB_VPBDIV *((volatile unsigned int*)0xE01FC100)
// VPB should run at full speed
#define VPBDIV_VAL 1

// Addresses of MAM (Memory Accelerator Module) registers
#define MAM_MAMCR *((volatile unsigned int*)0xE01FC000)
#define MAM_MODE 2     // MAM fully enabled
#define MAM_MAMTIM *((volatile unsigned int*)0xE01FC004)
#define MAM_FETCH 3    // 3 cclks are proposed as fetch timing

void systemInit(void)
{
 // Enable and connect the PLL (Phase Locked Loop)
 // 1. Set multiplier and divider
 SCB_PLLCFG = MSEL | (1 << PSEL1) | (0 << PSEL0);
 // 2. Enable PLL
 SCB_PLLCON = (1 << PLLE);
 // 3. Feed sequence
 SCB_PLLFEED = PLL_FEED1;
 SCB_PLLFEED = PLL_FEED2;
 // 4. Wait for PLL lock (PLOCK bit is set if locked)
 while (!(SCB_PLLSTAT & (1 << PLOCK)));
 // 5. Connect (and enable) PLL
 SCB_PLLCON = (1 << PLLE) | (1 << PLLC);
 // 6. Feed sequence
 SCB_PLLFEED = PLL_FEED1;
 SCB_PLLFEED = PLL_FEED2;
	
 // Setup and enable the MAM (Memory Accelerator Module)
 // 1. Start change by turning of the MAM (redundant)
 MAM_MAMCR = 0;	
 // 2. Set MAM-Fetch cycle to 3 cclks as recommended for > 40MHz
 MAM_MAMTIM = MAM_FETCH;
 // 3. Enable MAM 
 MAM_MAMCR = MAM_MODE;

 // --- set VPB speed ---
 SCB_VPBDIV = VPBDIV_VAL;
	
 SCB_MEMMAP = MEMMAP_USER_FLASH_MODE; // according to the Makefile
}
// Addresses of PCB (Pin Connect Block) registers
#define PCB_PINSEL1 *((volatile unsigned int*)0xE002C004)
// Addresses of GPIO (General Purpose I/O) registers
#define GPIO_IOSET *((volatile unsigned int*)0xE0028004)
#define GPIO_IODIR *((volatile unsigned int*)0xE0028008)
#define GPIO_IOCLR *((volatile unsigned int*)0xE002800C)
// GPIO pin associated to the LED
#define LEDPIN 30

void gpioInit(void)
{
 GPIO_IODIR |= (1 << LEDPIN);	// define LED-Pin as output
 PCB_PINSEL1 = 0x0;
 GPIO_IOSET  = (1 << LEDPIN);	// set Bit = LED off (active low)
}

void ledToggle(void)
{
 static unsigned char state=0;

 state = !state;
 if (state)
     GPIO_IOCLR = (1 << LEDPIN);  // set Bit = LED on
 else 
     GPIO_IOSET = (1 << LEDPIN);  // set Bit = LED off
}

// Line Control Register bit definitions
#define ULCR_CHAR_8 (3 << 0)      // 8-bit character length
#define ULCR_STOP_1 (0 << 2)      // 1 stop bit
#define ULCR_PAR_NO (0 << 3)      // no parity

// Definition of a typical UART 'mode' setting
#define UART_8N1 (uint8_t)(ULCR_CHAR_8 + ULCR_PAR_NO + ULCR_STOP_1)

// FIFO Control Register bit definitions
#define UFCR_FIFO_ENABLE (1 << 0)  // FIFO enable
#define UFCR_FIFO_TRIG8  (2 << 6)  // Rx trigger, 8 characters in FIFO

// Definition of a typical UART 'fmode' setting
#define UART_FIFO_8 (uint8_t)(UFCR_FIFO_ENABLE + UFCR_FIFO_TRIG8)

#define BAUD 9600
#define FOSC 14745000
// Baud-Rate is calculated based on pclk (VPB-clock)
// the devisor must be 16 times the desired baudrate
#define UART_BAUD(baud) (uint16_t)(((FOSC*PLL_M/VPBDIV_VAL) / ((baud) * 16.0)) + 0.5)

int main(void)
{
 int ch;

 systemInit();
 gpioInit();
	
 uart0Init(UART_BAUD(BAUD), UART_8N1, UART_FIFO_8);

 uart0Puts("\r\nTesting the USB serial interface of the Olimex DEV-00268 board\r\n");
 uart0Puts("and testing the LED...\r\n");
	
 while(1) {
   if ((ch = uart0Getch()) >= 0) {
       uart0Putch(ch);
       uart0Puts("\r\n");
       ledToggle();
   }
 }	
 return 0;
}

Voici le contenu du fichier uart.h.

/******************************************************************************
 * Based on software from:
 * Copyright 2004, R O SoftWare
 * No guarantees, warrantees, or promises, implied or otherwise.
 * May be used for hobby or commercial purposes provided copyright
 * notice remains intact.
 * 
 * Reduced to see what has to be done for minimum UART-support
 * by Richard St-Denis, Universite de Sherbrooke, 2013.
 *****************************************************************************/
#ifndef INC_UART_H
#define INC_UART_H

#include "inttypes.h"

void uart0Init(uint16_t baud, uint8_t mode, uint8_t fmode);
int uart0Getch(void);
int uart0Putch(int ch);
const char *uart0Puts(const char *string);

#endif


Voici le contenu du fichier uart.c.

/******************************************************************************
 * This module provides interface routines to the LPC ARM UARTs.
 * Copyright 2004, R O SoftWare
 * No guarantees, warrantees, or promises, implied or otherwise.
 * May be used for hobby or commercial purposes provided copyright
 * notice remains intact.
 *
 * Reduced to see what has to be done for minimum UART-support
 * by Richard St-Denis, Universite de Sherbrooke, 2013.
 *****************************************************************************/

#include "uart.h"

// Addresses of UART0 registers
#define UART0_RBR *((volatile unsigned int*)0xE000C000)
#define UART0_THR *((volatile unsigned int*)0xE000C000)
#define UART0_IER *((volatile unsigned int*)0xE000C004)
#define UART0_IIR *((volatile unsigned int*)0xE000C008)
#define UART0_FCR *((volatile unsigned int*)0xE000C008)
#define UART0_LCR *((volatile unsigned int*)0xE000C00C)
#define ULCR_DLAB_ENABLE (1 << 7)  // Enable Divisor Latch Access
#define UART0_LSR *((volatile unsigned int*)0xE000C014)
#define ULSR_RDR  (1 << 0)         // Receive Data Ready
#define ULSR_THRE (1 << 5)         // Transmit Holding Register Empty
#define UART0_DLL *((volatile unsigned int*)0xE000C000)
#define UART0_DLM *((volatile unsigned int*)0xE000C004)

// Addresses of PCB (Pin Connect Block) registers
#define PCB_PINSEL0 *((volatile unsigned int*)0xE002C000)

#define PINSEL_BITPIN0 0           // TX-Pin for Pin 0.0
#define PINSEL_BITPIN1 2           // RX-Pin for Pin 0.1
// PINSEL0 has to be set to "UART-Function" = Function "01"
#define PINSEL_FIRST_ALT_FUNC 1
// Values of bits 0-3 in PINSEL to activate UART0
#define UART0_PINSEL ((PINSEL_FIRST_ALT_FUNC << PINSEL_BITPIN0)|(PINSEL_FIRST_ALT_FUNC << PINSEL_BITPIN1))
#define UART0_PINMASK (0x0000000F) // PINSEL0 Mask for UART0

void uart0Init(uint16_t baud, uint8_t mode, uint8_t fmode)
{
 // Setup Pin Function Select Register (Pin Connect Block) 
 // Make sure old values of Bits 0-4 are masked out and
 // Set them according to UART0-Pin-Selection
 PCB_PINSEL0 = (PCB_PINSEL0 & ~UART0_PINMASK) | UART0_PINSEL;

 UART0_IER = 0x00;   // clear Interrupt Enable Register
 UART0_IIR = 0x00;   // clear Interrupt Identification Register
 UART0_LSR = 0x00;   // clear Line Status Register

 // Set the baud rate - DLAB must be set to access DLL/DLM
 UART0_LCR = (ULCR_DLAB_ENABLE);    // set divisor latches (DLAB)
 UART0_DLL = (uint8_t)baud;         // set for baud low byte
 UART0_DLM = (uint8_t)(baud >> 8);  // set for baud high byte

 // Setup Line Control Register (Parity, Stop bit, Word length)
 UART0_LCR = (mode & ~(ULCR_DLAB_ENABLE));   // and clear DLAB 
 // Setup FIFO Control Register (FIFO-Enabled + Rx Trigger) 
 UART0_FCR = fmode;
}

int uart0Getch(void)
{                            // get a character
 if (UART0_LSR & ULSR_RDR)   // check if a character is available
     return UART0_RBR;       // return the character
 return -1;
}

int uart0Putch(int ch)
{                            // put a character
 while (!(UART0_LSR & ULSR_THRE))
   continue;                 // wait for an empty buffer
 UART0_THR = (uint8_t)ch;    // put the character to the Transmit Holding Register
 return (uint8_t)ch;         // return char
}

const char *uart0Puts(const char *string)
{                            // put a string
 char ch;

 while ((ch = *string)) {
   if (uart0Putch(ch)<0) break;
       string++;
 }
 return string;
}

Installation du pilote USB-Serial sous Windows Server 2003

  1. Branchez la carte Olimex DEV-00268 dans votre ordinateur en utilisant le câble USB
  2. Windows Server 2003 devrait chercher automatiquement le pilote et l'installer sur votre ordinateur
  3. Déterminez le port du dispositif USB-Serial
    • Control Panel → sous-menu System → table Hardware → bouton Device Manager → entrée Ports (COM & LPT)
    • Le port est généralement com3 our com4

Modification du Makefile

  1. Nous supposons que le projet existe déjà et qu'il contient les fichiers main.c, uart.h, uart.c et Makefile
  2. Modifiez le Makefile tel que la ligne 149 indique le port approprié (COMx) et la ligne 150 indique le baud rate désiré (9600)

Compilation et chargement du code sur la carte dans l'environnement de développement Programmers Notepad 2

  1. Compilez le programme main.c
    • Barre de menu → menu Tools → sous-menu Make All
    • Vérifiez que le message est affiché
  2. Chargez le programme sur la carte Olimex DEV-00268
    • Positionnez le commutateur DIP 1 à ICSP (les autres commutateurs doivent être positionnés à ON)
    • Appuyez sur le bouton reset
    • Barre de menu → menu Tools → sous-menu Make Program
    • Vérifiez que le message est affiché

Exécution du programme sous le ARM en utilisant le terminal virtuel Tera Term

  1. Démarrez Tera Term et la boîte de dialogue Tera Term New Connection
    • bouton Serial → sous-dialogue PortCOMx
    • Barre de menu → menu Setup → sous-menu Serial port...Data 8 bits, Parity none, Stop 1 bit
  2. Lancez l'exécution du programme
    • Positionnez le commutateur DIP 1 à RUN (les autres commutateurs doivent être positionnés à ON)
    • Appuyez sur le bouton reset