Programming Tips
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
Memory Map for dsPIC33FJ256GP506
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
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); |
- N must be a power of two, with a minimum value of 2.
<asm/types.h>
- 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
- 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
Clock Speed
- System clock source can be provided by:
- Primary oscillator (OSC1, OSC2)
- Secondary oscillator (SOSCO and SOSCI) with 32kHz crystal
- Internal Fast RC (FRC) oscillator at 7.37MHz (7372800Hz)
- 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
- 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>
- Registers are involved in Interrupts includes:
- Interrupt Flag Status (IFS0-IFS2) registers
- Interrupt Enable Control (IEC0-IEC2) registers
- Interrupt Priority Control (IPC0-IPC10) registers
- Interrupt Priority Level (IPL) register
- Global Interrupt Control (INTCON1, INTCON2) registers
- 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
- 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
- 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
- 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
- Standard speed mode: 100kHz
- Fast speed mode: 400kHz
- 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
- 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)
- 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))
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
Some chips allow programs to write to flash memory (program memory); this process is called RTSP (run-time self-programming).
- 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
- Not POSIX compliant
- Library functions in <dsp.h> include the following categories:
- Vector
- Window
- Matrix
- Filtering
- Transform
- Control
Data Types
- 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
- to be added