Programming Tips

From OpenCircuits
Revision as of 07:11, 19 October 2011 by DavidCary (talk | contribs) (merge sections)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This wiki describes an example coding of the freertos_posix driver for the dsPic33 development board. Please refer to the actual coding used from here.

Memory[edit]

Memory Map for dsPIC33FJ256GP506[edit]

Type Start Address End Address Size
Flash 0x000000 0x02ABFF 171K[1]
+--Flash: Reset Vector 0x000000 0x000003 4
+--Flash: Interrupt Vector Table 0x000004 0x0000FF 252
+--Flash: Alternate Vector Table 0x000104 0x0001FF 252
+--Flash: User Program 0x000200 0x02ABFF 170.5K
Programming Executive 0x800000 0x800FFF 4K[1]
Config Registers 0xF80000 0xF80017 24
Device ID (0xF5) 0xFF0000 0xFF0003 4

[1] Each address is 16-bit wide. Every two addresses correspond to a 24-bit instruction. Each even address contains 2 valid bytes; each odd address contains 1 valid byte plus 1 fathom byte.


Data Location[edit]

Type Description Example
_XBSS(N) [1] RAM Data in X-memory, aligned at N, no initilization int _XBSS(32) xbuf[16];
_XDATA(N) [1] RAM Data in X-memory, aligned at N, with initialization int _XDATA(32) xbuf[] = {1, 2, 3, 4, 5};
_YBSS(N) [1] RAM Data in Y-memory, aligned at N, no initialization int _YBSS(32) ybuf[16];
_YDATA(N) [1] RAM Data in Y-memory, aligned at N, with initialization int _YDATA(32) ybuf[16] = {1, 2, 3, 4, 5};
__attribute__((space(dma)) RAM Data in DMA int __attribute__((space(dma)) data[128];
__attribute__((space(const))) Flash ROM data, constant, accessed by normal C
statements, but 32K max.
int i __attribute__((space(const))) = 10;
__attribute__((space(prog))) Flash ROM data, read/write by program space visibility
window (psv)
int i __attribute__((space(prog)));
__attribute__((space(auto_psv))) Flash ROM data, read by normal C statements, write
by accessing psv
int i __attribute__((space(auto_psv)));
__attribute__((space(psv))) Flash ROM data, read/write by (psv) int i __attribute__((space(psv)));
_PERSISTENT RAM Data, data remain after reset int _PERSISTENT var1, var2;
_NEAR RAM Data at near section int _NEAR var1, var2;
__attribute__((__interrupt__)) Interrupt service rountine void __attribute__((__interrupt__)) _INT0Interrupt(void);
  1. N must be a power of two, with a minimum value of 2.


<asm/types.h>[edit]

  • The following maps the basic data types:
 typedef unsigned char           __u8;
 typedef char                    __s8;
 typedef unsigned int            __u16;
 typedef int                     __s16;
 typedef unsigned long           __u32;
 typedef long                    __s32;
 typedef unsigned long long      __u64;
 typedef long long               __s64;
 
 //to be used in <time.h>
 typedef unsigned long           time_t;
  • The following macros are the platform-dependent
 /** Interrupt Request */
 #define _IRQ                    __attribute__((__interrupt__))
 /** TRAP IRQ for saving program counter: declare __u16 StkAddrLo, StkAddrHi in trap.c (order matters) */
 #define _TRAP_IRQ               __attribute__((__interrupt__(__preprologue__( \
                                 "mov #_StkAddrHi,w1\n\tpop [w1--]\n\tpop [w1++]\n\tpush [w1--]\n\tpush [w1++]"))))
 /** IO Stub Functions are placed in .libc section so that the standard libraries can access these functions using short jumps. */
 #define _LIBC                   __attribute__((section(".libc")))
 /** FAST RAM */
 #define _DMA                    __attribute__((space(dma),aligned(256)))


Custom Linker Script to Maximize Space for Constant Data[edit]

  • Constant data declared using keyword const will be stored in the .const section in the flash memory.
  • Normally, during compilation, the linker will assign these data after the program code (.text section).
  • Since .const is accessed by auto-psv function, to maximize the space for constant data (32kb), the .const section needs to be aligned at 0x80000 boundary.
  • This requires the following change in linker script:
 __CONST_BASE = 0x8000;
 
 .text __CODE_BASE :
 {
 	*(.reset);
       *(.handle);
       *(.libc) *(.libm) *(.libdsp);  /* keep together in this order */
       *(.lib*);
       /* *(.text);		deleted to maximize space for const data */
 } >program
 
 .const __CONST_BASE :
 {
 	*(.const);
 } >program
  • If your program is large, after this change in linker script, function calls may involve large jump in the memory map (>32kB). As a result, you may need to enable the large code and large memory model during compilation. In such case, use the following options in your build path:
   -mlarge-code -mlarge-data
  • Meanwhile, functions that are defined in the standard C libraries, but are replaced with your own implementations (e.g. I/O stubs: open(), read(), write(), lseek(), ioctl() etc.) may have the following linker error:
   /usr/pic30-elf/lib//libc-elf.a(fflush.eo)(.libc+0x3c): In function '.LM11':
   : Link Error: relocation truncated to fit: PC RELATIVE BRANCH _write
   /usr/pic30-elf/lib//libc-elf.a(fclose.eo)(.libc+0x42): In function '.LM18':
   : Link Error: relocation truncated to fit: PC RELATIVE BRANCH _close 
  • To resolve the problem, you need to place the functions in the .libc section rather than in the .text section, like this:
   int _LIBC open(const char *pathname, int flags){ ... }
   int _LIBC close(int fd){ ... }
   int _LIBC write(int fd, void* buf, int count) { ... }
   int _LIBC read(int fd, void* buf, int count) { ... }
   int _LIBC ioctl(int fd, int request, void* argp) { ... }
   int _LIBC lseek(int fd, int offset, int whence) { ... }


System Setup[edit]

Clock Speed[edit]

  • System clock source can be provided by:
  1. Primary oscillator (OSC1, OSC2)
  2. Secondary oscillator (SOSCO and SOSCI) with 32kHz crystal
  3. Internal Fast RC (FRC) oscillator at 7.37MHz (7372800Hz)
  4. Low-Power RC (LPRC) oscillator (Watchdog Timer) at 512 kHz.
  • These clock sources can be incorporated with interal Phase-locked-loop (PLL) x4, x8 or x16 to yield the osciallator frequrence FOSC
  • The system clock is divided by 4 to yield the internal instruction cycle clock, FCY=FOSC/4


System Clock[edit]

  • Each timer is 16-bit (i.e. counting from 0 to 65535).
  • Prescale is the ratio between timer counts and system clock counts. Prescales of 1:1, 1:8, 1:64 and 1:256 are available.
  • Let required time for ticking be PERIOD.
  • Number of instruction cycles during PERIOD = PERIOD*FCY cycles
  • Using a prescale of 1:x, the timer period count register = # of cycles/x
  • e.g. PERIOD = 10ms; # of cycles = 10ms*40MHz = 400000 cycles; Using 1:8 Prescale, register setting = 400000/8 = 50000
  void
  prvSetupTimerInterrupt (void)
  {
    T1CON = 0;
    TMR1 = 0;
    PR1 = 50000;
    //============================================================
    IPC0bits.T1IP = configKERNEL_INTERRUPT_PRIORITY;
    IFS0bits.T1IF = 0;
    IEC0bits.T1IE = 1;
    //============================================================
    T1CONbits.TCKPS0 = 1;
    T1CONbits.TCKPS1 = 0;
    T1CONbits.TON = 1; 
  }
  //********************************************************************
  void _IRQ 
  _T1Interrupt (void)
  {
    IFS0bits.T1IF = 0;
    vTaskIncrementTick();
    portYIELD();
  }


<asm/system.h>[edit]

  • Registers are involved in Interrupts includes:
  1. Interrupt Flag Status (IFS0-IFS2) registers
  2. Interrupt Enable Control (IEC0-IEC2) registers
  3. Interrupt Priority Control (IPC0-IPC10) registers
  4. Interrupt Priority Level (IPL) register
  5. Global Interrupt Control (INTCON1, INTCON2) registers
  6. Interrupt vector (INTTREG) register
  • User may assign priority level 0-7 to a specific interrupt using IPC. Setting priority to 0 disable a specific interrupt. Level 7 interrupt has the highest priority.
  • Current priority level is stored in bit<7:5> of Status Register (SR). Setting Interrupt Priority Level (IPL) to 7 disables all interrupts (except traps).
  • sti() and cli() can be defined to enable and disable global interrupts for time critical functions:
 #define IPL              ( 0x00e0 )
 #define cli()            SR |= IPL    //Set IPL to 7
 #define sti()            SR &= ~IPL   //Set IPL to 0


POSIX System Call and Drivers[edit]

  • POSIX System Calls (open(), close(), read(), write(), lseek()) are used to access hardware devices related to data stream.
  • The file descriptor return by open() for these devices are statically assigned at compile time.


UART[edit]

  • Serves as the default communication channel for STDIN, STDOUT and STDERR.
  • Implementation of this driver allows transparent operation of printf() in standard C library.


I2C[edit]

  • A number of I2C devices can be added using this driver (e.g. I2C DAC, I2C EEPROM, etc)
  • Two lines are devoted for the serial communication. SCL for clock, SDA for data.
  • Standard communication speed includes
  1. Standard speed mode: 100kHz
  2. Fast speed mode: 400kHz
  3. High speed mode: 3.4MHz
  • Pull-up resistors are required for both SCL and SDA. Minimum pull-up resistance is given by:
    Pull-up resistor (min) = (Vdd-0.4)/0.003  ......  [See section 21.8 in Family reference manual]
  • 2.2Kohm is typical for standard speed mode.
  • After initiating a start/stop/restart bit, add a small delay (e.g. no operation) before polling the corresponding control bit (hardware controlled).
  • After sending a byte and receiving an acknowledgment from the slave device, ensure to change to idle state.


ADC[edit]

  • 12-bit ADC: (Max 18 Channels)
  • ADC uses DMA to buffer the adc data.
  • A maximum of 500kps of sampling rate when using auto sampling mode.


Simple PWM (Output Compare Module)[edit]

  • The PWM module consists of 8 channels using the output compare module of dsPic.
  • These channels are locate at pin 46 (OC1), 49 (OC2), 50 (OC3), 51 (OC4), 52 (OC5), 53 (OC6), 54 (OC7), 55 (OC8). These pins are shared with port D.
  • The range of PWM freqeuencies obtainable is 2Hz to 15MHz (See Figure 6.3). Suggested range of operation is 2Hz to 120kHz. The relationship between resolution r and PWM frequency fPWM is given by:
        fPWM = fCY/(Prescale*10rlog(2))
Relationship of Resolution and PWM Frequency
Resolution (bit) Prescale=1 Prescale=8 Prescale=64 Prescale=256
1 15,000,000 1,875,000 234,375 58,594
2 7,500,000 937,500 117,188 29,297
3 3,750,000 468,750 58,594 14,648
4 1,875,000 234,375 29,297 7,324
5 937,500 117,188 14,648 3,662
6 468,750 58,594 7,324 1,831
7 234,375 29,297 3,662 916
8 117,188 14,648 1,831 458
9 58,594 7,324 916 229
10 29,297 3,662 458 114
11 14,648 1,831 229 57
12 7,324 916 114 29
13 3,662 458 57 14
14 1,831 229 29 7
15 916 114 14 4
16 458 57 7 2


run-time self-programming flash[edit]

Some chips allow programs to write to flash memory (program memory); this process is called RTSP (run-time self-programming).

Flash-emulated EEPROM

  • Using built-in functions __builtin_tblpage(), __builtin_tbloffset() to set special-purpose registers to access flash memory
  • Using assembly code to read and write flash memory.

"Trying to get my head around RTSP"

The KeaDrone project has some code for reading and writing flash on a PIC24F chip: [1]

DSP Library[edit]

  • Not POSIX compliant
  • Library functions in <dsp.h> include the following categories:
  1. Vector
  2. Window
  3. Matrix
  4. Filtering
  5. Transform
  6. Control


Data Types[edit]

  • Signed Fractional Value (1.15 data format)
    • Inputs and outputs of the dsp functions adopt 1.15 data format, which consumes 16 bits to represent values between -1 to 1-2-15 inclusive.
    • Bit<15> is a signed bit, positive = 0, negative = 1.
    • Bit<14:0> are the exponent bits e.
    • Positive value = 1 - 2-15*(32768 - e)
    • Negative value = 0 - 2-15*(32768 - e)
  • 40-bit Accumulator operations (9.31 data format)
    • The dsp functions use the 40 bits accumalators during arithmatic calculations.
    • Bit<39:31> are signed bits, positive = 0x000, negative = 0x1FF.
    • Bit<30:0> are exponent bits.
  • IEEE Floating Point Values
    • Fractional values can be converted to Floating point values using: fo = Fract2Float(fr); for fr = [-1, 1-2-15]
    • Floating point values can be converted to Fractional values using: fr = Float2Fract(fo); or fr = Q15(fo); for fo = [-1, 1-2-15]
    • Float2Fract() is same as Q15(), except having saturation control. When +ve >= 1, answer = 215-1 = 32767 (0x7FFF). When -ve < -1, answer = -215 = -32767 (0x8000)


Fast Fourier Transform[edit]

  • to be added