Friday 10 February, 2023 Expand to read article belowThe list file is produced to show the assembly code created for the C source code. Each C source line has the corresponding assembly lines under it to show the compiler's work. The following three special cases make the .LST file look strange to the first time viewer. Understanding how the compiler is working in these special cases will make the .LST file appear quite normal and very useful.
1. Stray code near the top of the program is sometimes under what looks like a non-executable source line.
Some of the code generated by the compiler does not correspond to any particular source line. The compiler will put this code either near the top of the program or sometimes under a #USE that caused subroutines to be generated.
2. The addresses are out of order.
The compiler will create the .LST file in the order of the C source code. The linker has re-arranged the code to properly fit the functions into the best code pages and the best half of a code page. The resulting code is not in source order. Whenever the compiler has a discontinuity in the .LST file, it will put a * line in the file. This is most often seen between functions and in places where INLINE functions are called. In the case of an INLINE function, the addresses will continue in order up where the source for the INLINE function is located.
3. The compiler has gone insane and generated the same instruction over and over.
For example:

This effect is seen when the function is an INLINE function and is called from more than one place. In the above case, the A=0 line is in an INLINE function called in four places. Each place it is called from gets a new copy of the code. Each instance of the code is shown along with the original source line, and the result may look unusual until the addresses and the * are noticed. | Friday 10 February, 2023 Expand to read article belowCCS has implemented the ability to use multiple ICD units on the same PC. This allows you program or debug different target devices in separate projects without needing to close out of any files.
How to Do It with PCW:
Need two ICD units and target devices.
To run two PCWs → use the command-line option force_new
c:ProgramFilesPICCpcw → will open the first PCW
c:ProgramFilesPICCpcw+force_new → will open the second PCW
The USB port used by each target device will be visible in the Debug Configure tab within the Advanced Debugger under "port". You can select which ICD the Debugger should talk to using this setting.
How to Use with ICD Control Panels:
Need two ICD units and target devices
Open two ICD control panels by running the executable from your desktop.
At the start-up, the USB port will automatically be assigned for each ICD unit. You can then go the "Configure Port" button the control panel to switch ports. | Friday 10 February, 2023 Expand to read article belowThese options lets the user configure the Pulse Width Modulation (PWM) pins. They are only available on devices equipped with PWM. The options for these functions vary depending on the chip and are listed in the device header file.
Relevant Functions: - setup_power_pwm(config) - Sets up the PWM clock, period, dead time etc.
- setup_power_pwm_pins(module x) - Configure the pins of the PWM to be in Complementary, ON or OFF mod.
- set_power_pwmx_duty(duty) - Stores the value of the duty cycle in the PDCXL/H register. This duty cycle value is the time for which the PWM is in active state.
- set_power_pwm_override(pwm,override,value) - This function determines whether the OVDCONS or the PDC registers determine the PWM output.
Relevant Preprocessor:
None
Relevant Interrupts: - #INT_PWMTB - PWM Timebase Interrupt (Only available on PIC18XX31)
Relevant Include Files:
None, all functions are built-in
Relevant getenv() Parameters:
None
Example Code:
....
long duty_cycle, period;
...
// Configures PWM pins to be ON,OFF or in
// Complimentary mode. setup_power_pwm_pins(PWM_COMPLEMENTARY ,PWM_OFF, PWM_OFF, PWM_OFF_; // Sets up PWM clock , postscale and period. Here
// Here period is used to set the PWM Frequency as follows
// Frequency=Fosc/(4* (period+1)*postcae)
// *postscale)
setup_power_pwm(PWM_CLOCK_DIV_4|PWM_FREE_RUN,1,0,period,0,1,0);
set_power_pwm0_duty(duty_cycle)); // Sets the duty cycle of the PWM 0,1 in // Complementary mode | Friday 10 February, 2023 Expand to read article belowThe CCS C Compiler from the beginning has made it easy to communicate over an RS232 like port. Many of our users have used this extensively, not only for communicating with serial devices but also for diagnostics and debugging. Now, PC's and many other devices have replaced their RS232 ports with USB ports. The USB hardware and software is much more complex than RS232. This has discouraged many from migrating over or they have used a crutch like an external chip to do the USB magic (like FTDI). This article shows how to easily add a USB port to your PICR MCU application.
HARDWARE
Microchip has a number of chips with built in USB. For example, the PIC16F1459 ($1.38/100) or PIC18F14K50 ($1.79/100). Here is an example schematic: (note: pin numbers apply to PDIP, SOIC, and SSOP packages)

Shown here is a USBmicro style connector. The more common type B connector is the same pinout except there is no pin 5 and 4 is the ground. The D+ and D- pins are for the data and sometimes there will be a 27 ohm series resistor and/or zener protection diodes on those pins. When using a peripheral device, pin 1 will supply 5 volts (up to a half amp). The board can be powered from that 5 volts; however, in this schematic it is to simply detect if the USB cable is plugged in. Although users can do that from the data lines, it is easier to detect the 5 volts like this. Sometimes users will put series coils on the 5 volts and ground to reduce noise.
The Vusb on this chip simply needs a cap for an internal voltage regulator. Some chips have a dedicated Vbus pin for the 5 volts detect. Careful with the pin names, as they are not consistent between chips.
HOST SOFTWARE
The USB bus has a number of protocols that can be used, each with many options and configurations. HID (human Input device) is used for keyboards and mice and is easy to use on any OS because the drivers are built in. There is a data limit however, (like 8 bytes per ms) that makes it impractical for many applications. CDC is a protocol designed to emulate an RS232 port. The Windows drivers will create a virtual COM port for a CDC USB device so the PC application can use COM1... just like an RS232 port.
When a Windows 10 sees a CDC device, it automatically installs a driver for it. For older versions of Windows, users need a short .inf file to describe their device and include the device VID (vendor ID) and PID (product ID). Examples .inf files are in the CCS C Compiler examples directory. Every USB device is supposed to have a unique VID/PID and serial number. If users have two of the same devices plugged in, the VID/PID will match but they are differentiated by serial number. To get a VID users need to register with the USB standards organization.
USB is point to point and one point is a host and the other a device. Hub's can also be involved but that is beyond our concern for this article. The host initiates all activity. For example, a PC is a host and it will poll every device about once a millisecond and transfer any data needing transferring. When the device is first plugged into a port, there is a handshaking that takes place that involves the device identifying itself along with the protocol it will use and various parameters. For example, how much current it expects to draw off the bus. This handshake is called enumeration. In Windows users know it happened by the beep.
Some PIC24 parts have USB hardware that can also be a host. For example, this might be used to read a USB flash drive.
#include <16F1459.h>
#use delay(internal=48MHz,USB_FULL,ACT=USB)
#define USB_CON_SENSE_PIN PIN_A5 // Connected to USB +5V
#define USB_STRINGS_OVERWRITTEN
#define USB_CONFIG_VID 0x2405 // CCS VID
#define USB_CONFIG_PID 0x8001
#define USB_DESC_STRING_TYPE 3
#define USB_STRING(x) (sizeof(_UNICODE(x))+2), \
USB_DESC_STRING_TYPE,_UNICODE(x) #define USB_ENGLISH_STRING 4,USB_DESC_STRING_TYPE,0x09,0x04
//Microsoft Defined for US-English
char const USB_STRING_DESC[]={
USB_ENGLISH_STRING, //string 0 - language
USB_STRING("CCS"), //string 1 - manufacturer
USB_STRING("My-PIC-Device") //string 2 - product };
#include <usb_cdc.h>
USB Wizard
The wizard allows the use of one of three communication protocols:
Communication Device Class, or CDC, creates a virtual COM port on a PC. On Windows PCs, this is the legacy COM ports (such as COM1) which can be opened with legacy terminal software. The CCS CDC library allows the use of printf(), putc() and getc() to communicate to the host PC using the virtual COM port.
Human Interface Devices, or HID, is a simple protocol for things like Mice and Keyboards. This wizard allows the user to create a HID project using a vendor specific HID descriptor report. CCS also provides a PC program written in VB.NET to communicate with the PIC in this manner. CCS also provides HID keyboard and HID mouse examples for the PIC.
Bulk transfers is a method of reading/writing directly to the buffer endpoint. Using this method does require drivers for your operating system, but since XP Microsoft has been shipping WinUSB which is compatible with this mode. CCS does provide a VB.NET demo application to show how to use this driver to communicate with the PIC.
CCS IDE Project Wizard includes USB. The IDE requires a minimum operating system of Windows 95(or later) or Linux. | Tuesday 04 October, 2022 Expand to read article belowAdd Flow Control and Buffering to Serial Routines
A feature of the compiler is the powerful #use rs232() library that has added transmit buffering, receive buffering, and flow control. While the API for the serial library remains unchanged ( getc(), putc(), printf() ), existing code using this API allows for buffering and flow control by simply modifying the #use rs232() parameters. A user may specify:
* size of transmit buffer
* size of receive buffer
* interrupt usage or no interrupt usage
* pin for CTS and pin for RTS
Click through to: https://www.ccsinfo.com/Version5
to review a usage example of the #use rs232() and additional details on each new usage. Additional configurations and control options are also available.
Notifications From the Serial Library on Data Reception
The compiler provides an extremely flexibly serial library; it has the ability to use the hardware peripheral or bit bang the pins, to control and monitor flow control, to specify parity, to use a one wire bus, and more. One feature it has is the ability to specify a receive buffer, and the library will automatically use the receive interrupt to buffer incoming characters. Here is an example of creating a stream called STREAM_UART1 on the UART1 hardware peripheral with a 16 byte receive buffer:
#use rs232(UART1, baud=9600, receive_buffer=16, stream=STREAM_UART1)
Essentially the stream works like a file handle that can be used with C standard I/O functions like fputc, fgetc, etc. Using the stream created above, here is a simple loop that echoes data received on the UART back to the UART:
while (kbhit(STREAM_UART1))
{
fputc(fgetc(STREAM_UART1), STREAM_UART1); }
This example shows the flexibility of the #use rs232() library provided by CCS. The 'receive_buffer' option creates an interrupt on the UART receive to buffer incoming characters and kbhit() and fgetc() accesses that buffer, but if the 'receive_buffer' was removed from the #use rs232(), then kbhit() and fgetc() would instead check for any received data being held by the UART.
The 'receive_buffer' example as shown above has no way of notifying the users software that data is available, except by polling the receive buffer status with kbhit(). The 5.095 version of the CCS C Compiler adds a new option called 'callback' that allows the user to specify a function to be called when the receive buffer goes from empty to not empty. This could be used to mark a semaphore or enable a routine to start parsing data in the receive buffer. Here is an example of adding this new option:
#use rs232(UART1, baud=9600, receive_buffer=16, stream=STREAM_UART1, \
callback=Uart1OnRx)
As stated earlier, this example will call the 'Uart1OnRx' function whenever the receive buffer goes from empty to not empty. Here is how the earlier echo example can be changed to use an RTOS with a semaphore to mark when the receive buffer is ready:
#use rtos(timer=0)
int uart_sem = 0;
static void Uart1OnRx(void) {
rtos_signal(uart_sem); }
#task(rate=10ms)
static void Uart1Task(void) {
for(;;) {
rtos_wait(uart_sem);
while(kbhit(STREAM_UART1)) {
fputc(fgetc(STREAM_UART1), STREAM_UART1); } } }
Alternatively, a function for parsing data in the receive buffer can be queued for execution with the timeouts library:
#include <timeouts.c>
void Uart1OnxTimeout(void* pArgs) {
while(kbhit(STREAM_UART1)) {
putc(getc(STREAM_UART1), STREAM_UART1); } }
static void Uart1OnRx(void) {
TimeoutsAdd(Uart1OnxTimeout, NULL, 0); }
How can I use two or more ports on one PIC®?
The #USE RS232 (and I2C for that matter) is in effect for GETC, PUTC, PRINTF and KBHIT functions encountered until another #USE RS232 is found.
The #USE RS232 is not an executable line. It works much like a #DEFINE. The following is an example program to read from one port (A) and echo the data to both the first port (A) and a second port (B).
#USE RS232(BAUD=9600, XMIT=PIN_B0, RCV=PIN_B1)
void put_to_a( char c ) {
put(c); }
char get_from_a( ) {
return(getc()); }
#USE RS232(BAUD=9600, XMIT=PIN_B2,RCV=PIN_B3)
void put_to_b( char b ) {
putc(c); }
main() {
char c;
put_to_a("Online\n\r");
put_to_b("Online\n\r");
while(TRUE) {
c=get_from_a();
put_to_b(c);
put_to_a(c); } }
The following will do the same thing but is more readable and is the recommended method:
#USE RS232(BAUD=9600, XMIT=PIN_B0, RCV=PIN_B1, STREAM=COM_A)
#USE RS232(BAUD=9600, XMIT=PIN_B2, RCV=PIN_B3, STREAM=COM_B)
main() {
char c;
fprintf(COM_A,"Online\n\r");
fprintf(COM_B,"Online\n\r");
while(TRUE) {
c = fgetc(COM_A);
fputc(c, COM_A);
fputc(c, COM_B); } } | Tuesday 04 October, 2022 Expand to read article belowCCS sells a simple Ethernet network integration device which can be embedded into any product or into industrial equipment, appropriately named EZ Web Lynx. Essentially it connects products or equipment to an HTML programmable website with no other protocol language skills needed! The website will allow the user to view the equipment status and the user can receive emails from the device. The emails are triggered by changes in the state of the pins, which are configured through the easy-to-use EZ Web Lynx IDE.
EZ Web Lynx is available in either 3.3 or 5 volt, as well as a 3.3V Wi-Fi versions at a very low cost to make implementation to the Ethernet very affordable. EZ Web Lynx enables products and equipment to become Ethernet-ready without having to design a new circuit board which can drastically increase development time.
HTML is the only programming language needed to program the website. However, if you prefer to program in C, the CCS PCH or PCWH compiler is compatible with EZ Web Lynx. To find out more about this product and for detailed pricing please go to www.ezweblynx.com. | Tuesday 04 October, 2022 Expand to read article belowThe PIC16F1614 family of devices is currently the only PIC® MCU family that has a built in Math Accelerator with Proportional-Integral-Derivative (PID) Module. This module is a mathematics module that can perform a variety of operations, most prominently acting as a PID controller.
The module accomplishes the task of calculating the PID algorithm by utilizing user-provided coefficients along with a multiplier and accumulator. The main benefit of using the hardware module is for doing the PID calculation much faster than it can be done in software. For example when running the PIC® from the 32 MHz internal oscillator the built-in HW PID function was measured to take 12.8 us, compared to a software PID function which was measure to take 216 us. This is approximately 1/16 of the time.
The following are the built-in functions that have been added for the Math Accelerator with PID module:
* setup_pid() - used to setup the PID module and set the user-input coefficients.
* pid_get_result() - used to input the set point and feedback from the external system to the PID module, start the calculation, and to retrieve the result to input in the external system.
* pid_read() - used to read various PID module registers.
* pid_write() - used to write various PID module registers.
* pid_busy() - used to check if PID module is busy or not-busy, finished, with calculation.
The Math Accelerator with PID module can be setup for three basic functions PID calculation, 16-bit unsigned add and multiple and 16-bit signed add and multiple. Both of the add and multiple modes can also be setup to accumulate the output.
When setup for PID mode the user-input coefficients, K1, K2 and K3, are calculated from the three classic PID coefficients Kp, Ki and Kd with the following equations:

T is the sampling period.
The following is an example of how to setup and use the Math Accelerator with PID module in PID mode:
void main(void) {
pid_struct_t PIDOutput;
unsigned int16 ADCReading;
signed int16 K1 = 7, K2 = -6, K3 = 0;
unsigned int16 SetPoint = 500;
unsigned int16 PWMDuty;
setup_pid(PID_MODE_PID, K1, K2, K3);
//Setup ADC
setup_adc_ports(sAN3, VSS_VDD);
setup_adc(ADC_CLOCK_INTERNAL);
set_adc_channel(3);
//Setup PWM 3
setup_timer_4(T4_CLK_INTERNAL | T4_DIV_BY_32, 249, 1); //1ms period, from 32 MHz
set_pwm3_duty(0); //0% duty
setup_pwm3(PWM_ENABLED | PWM_OUTPUT | PWM_TIMER4);
while(TRUE) {
delay_ms(50);
ADCReading = read_adc();
pid_get_result(SetPoint, ADCReading, &PIDOutput);
PIDOutput.u &= 0x07;
if(PIDOutput.u >= 4) //PIDOutput is negative, set PWMDuty to Minimum
PWMDuty = 0; else if(PIDOutput.u != 0) //PIDOutput > Maximum, set PWMDuty to Maximum
PWMDuty = 1000; else if(PIDOutput.l > 1000) //PIDOutput > Maximum, set PWMDuty to Maximum
PWMDuty = 1000; else
PWMDuty = PIDOutput.l;
set_pwm3_duty(PWMDuty); } }
When the Math Accelerator with PID module is setup for one of the add and multiple mode the operation is preformed as follows:
OUTPUT = (A + B) * C
The multiple value, C, is set with the K1 option that is passed setup_pid() function, and the two add values, A and B, are passed as the set_point and input parameters to the pid_get_result() function. For example the following is how to setup the Math Accelerator with PID module in add and multiply mode:
int16 C = 100;
int16 A, B;
pid_struct_t Result;
//setup for add and multiple mode and set multipler
setup_pid(PID_MODE_UNSIGNED_ADD_MULTIPLY, C);
//get add and multiple result
pid_get_result(A, B, &Result);
The PIC16F1614 family of devices is available in the IDE compilers and the PCM command-line compilers starting with version 5.045. They come in 14 and 20 pin packages with flash memory of 4048 or 8192 instructions. Additionally they have four 8-bit timers, three 16-bit timers, 8 or 12 analog inputs, two CCP modules, two 10-bit PWM modules and 1 CWG module, which can be used in conjunction with other modules, CCP or PWM for example, to generate a Half-Bridge or Full-Bridge PWM. | Wednesday 13 July, 2022 Expand to read article belowMany newer Microchip PIC® microcontrollers have re-programmable peripheral pins (RP). These pins allow the user to dynamically allocate peripherals to these pins, such as external interrupts, input capture, PWM, serial, timers and more. This offers the designer great flexibility when designing a product since the functionality of these pins can be changed at run-time. The data sheet for a device will list he pin assignments and these pins are denoted as either RPxx or RPIxx, where xx is the RP pin number. Pins that are RPIxx can only be programmed as an input (timer input, serial input, interrupt input, etc), whereas RPxx pins can be programmed either as an input or output (PWM output, serial output, etc).
Static Assignments in C
The static method for assigning I/O pins to a peripheral is the #pin_select directive. The #pin_select directive is a preprocessor directive for assigning I/O pins to peripherals and is executed before main() starts. The syntax for this command is as follows:
#pin_select function=pin
A list of functions and pins that can be used with the #pin_select directive is located in the device's header file near the top of the file, opening the device's header file (like 18F25K42.h) and searching for #pin_select is the quickest way to find them. The following is an example of how to assign pins to the UART1 RX and TX pins:
#pin_select U1TX=PIN_C6
#pin_select U1RX=PIN_C7
When using RP pins with a peripheral library, such as #use rs232(), the #pin_select must come before the #use directive, for example:
#pin_select U1TX=PIN_C6
#pin_select U1RX=PIN_C7
#use rs232(UART1, baud=9600, stream=U1)
There is a special method for assigning the peripheral pins is inside the #use pwm and #use capture directives. Future compiler release may allow this in other #use directives as well. Here is an example:
#use pwm(CCP1, output=PIN_B0)
The above will make the assignment of PIN_B0 as the CCP1 output pin.
Dynamic Pin assignments
In addition to #pin_select the CCS C Compiler also provides the pin_select() function for assigning pins to a peripheral. The pin_select() function can be used to assign, reassign and unassign pins to/from a peripheral at run-time. This allows the flexibility of using the same pin for multiple peripherals or using pins as both peripheral pins and I/O pins. The basic pin_select() function uses the following syntax: pin_select("function", pin);. The functions and pins are the same as what is used with the #pin_select directive, the only difference being that the function is passed as a constant string. The following is an example of how to assign pins to the UART1 peripheral:
pin_select("U1TX", PIN_C6);
pin_select("U1RX", PIN_C7);
To unassign a pin from a peripheral depends on whether it an input peripheral or an output peripheral.
To unassign a pin from an output peripheral is done as follows:
pin_select("NULL", PIN_C6); //unassign PIN_C6 from output peripheral.
To unassign a pin from an input peripheral is done as follows:
pin_select("U1RX", FALSE); //unassign pin from U1RX input peripheral.
Because of how output peripherals are assigned to RP pins it is possible to assign multiple pins to the same output peripheral when using the pin_select() directive. For example the following will assign multiple pins to the CCP1 peripheral:
pin_select("CCP1OUT", PIN_B0);
pin_select("CCP1OUT", PIN_B1);
This method of tying several pins to the same output can only be performed with the pin_select() function, #pin_select cannot be used to do this.
A more advanced form of the pin_select() directive is as follows:
pin_select("function", pin, unlock, lock);
In order to change the pin assignments at run time the pins must be first specifically unlocked to prevent run away code from changing a pin assignment. The optional unlock and lock are used to specify whether to do or not to do the unlock and lock procedures, TRUE does the procedure and FALSE doesn't to the procedure. When the lock/unlock parameters are not specified in the function both are performed by default. These optional parameters are most useful when using the pin_select() function to assign multiple peripheral pins sequentially. For example the following is an example of how to assign the UART1 TX and RX pins at run time:
pin_select("U1TX", PIN_C6, TRUE, FALSE);
pin_select("U1RX", PIN_C7, FALSE, TRUE);
Alternate pin assignments
Before the RP pins came out some chips allowed select peripherals to have multiple (usually just two) pins that can be assigned. This is done either by a fuse (like CCP2B3 and CCP2C1) or using an internal register.
In the case the selection is by fuse the #fuse directive must be inserted in the code and then the compiler will treat that pin as a peripheral. For example:
#fuses CCP2C1
In the case that the register assignments are made by register the built in functions will have an option for the assignment. See the header file for the device. The UART assignments are made with the #use rs232 by specifying one of the alternate pins.
Like us on Facebook. Follow us on Twitter.
About CCS:
CCS is a leading worldwide supplier of embedded software development tools that enable companies to develop premium products based on Microchip PIC ® MCU and dsPIC ® DSC devices. Complete proven tool chains from CCS include a code optimizing C compiler, application specific hardware platforms and software development kits. CCS' products accelerate development of energy saving industrial automation, wireless and wired communication, automotive, medical device and consumer product applications. Established in 1992, CCS is a Microchip Premier 3rd Party Partner. For more information, please visit https://www.ccsinfo.com.
PIC ® MCU, MPLAB ® IDE, MPLAB ® ICD2, MPLAB ® ICD3 and dsPIC ® are registered trademarks of Microchip Technology Inc. in the U.S. and other countries. | Wednesday 13 July, 2022 Expand to read article belowThe CCS IDE has an easy way to make a copy of a project. The feature is accessed by using FILE > COPY PROJECT. The dialog box looks like this:
The lower selection allows you to select an entire group of files or expand the group and select specific files. The references source files include files supplied with the compiler like stdlib.h. The supporting output files are files used by the IDE and not usually opened outside the IDE. Documentation files are any files attached to the project using the navigation bar.
The location entry in the center allows you to specify the destination. Use the folder icon on the right to browse and/or create a new location.
The functions are as follows:
Copy files to a new directory tree
This simply makes a copy of the designated files to a new directory location. The .ccspjt file usually references the project files using a relative path so the project should compile right in the new location.
Create a ZIP file with the files
This makes a zip file with the designated files. This is ideal for a archive or if you need to send a project to CCS for analysis.
Copy .HEX file
Despite the name this function can quickly make a copy of any combination of files to another location. For example you may need to make a copy of the project hex file to a public location where others can use it for testing or production. By default just the project hex file is selected however if you select some other files that now becomes the default so you can quickly use this function to make the same copy when needed.
Like us on Facebook. Follow us on Twitter.
About CCS:
CCS is a leading worldwide supplier of embedded software development tools that enable companies to develop premium products based on Microchip PIC ® MCU and dsPIC ® DSC devices. Complete proven tool chains from CCS include a code optimizing C compiler, application specific hardware platforms and software development kits. CCS' products accelerate development of energy saving industrial automation, wireless and wired communication, automotive, medical device and consumer product applications. Established in 1992, CCS is a Microchip Premier 3rd Party Partner. For more information, please visit https://www.ccsinfo.com.
PIC ® MCU, MPLAB ® IDE, MPLAB ® ICD2, MPLAB ® ICD3 and dsPIC ® are registered trademarks of Microchip Technology Inc. in the U.S. and other countries. | Wednesday 13 July, 2022 Expand to read article belowThe CCS tool suite has a number of features to help adding serial numbers to product firmware. In your source code the primary method uses the pre-procssor directive #serialize. The CCS device Adding Serial Numbers to Production Images. The CCS tool suite has a number of features to help adding serial numbers to product firmware. In your source code the primary method uses the pre-processor directive #serialize. The CCS device programmers must be used to take advantage of this feature. It works by adding a special comment line to the hex file.
Location of the serial number
The usual way to handle the location is to use a const declaration like one of the following:
const int32 serial_number;
const char sn[7];
Then in #serialize add something like id=serial_number.
You can also place the serial number in EEPROM by specifying an address like:
dataee=0x10
Format
Usually the format can be figured out from the const however you can specify it using one of:
binary=x - x is the number of bytes.
string=x - x is the number of characters.
unicode=n - If n is a 0, the string format is normal unicode.
For n>0 n indicates the string number in a USB descriptor.
Serial number source
There are four ways to specify where the serial number comes from:
file="filename.txt" - The file x is used to read the initial serial number from, and this file is updated by the ICD programmer. It is assumed this is a one line file with the serial number. The programmer will increment the serial number.
listfile="filename.txt" - The file x is used to read the initial serial number from, and this file is updated by the ICD programmer. It is assumed this is a file one serial number per line. The programmer will read the first line then delete that line from the file.
next="x" - The serial number X is used for the first load, then the hex file is updated to increment x by one.
prompt="text" - If specified the user will be prompted for a serial number on each load. If used with one of the above three options then the default value the user may use is picked according to the above rules.
Logging
log=xxx - A file may optionally be specified to keep a log of the date, time, hex file name and serial number each time the part is programmed. If no id=xxx is specified then this may be used as a simple log of all loads of the hex file.
Examples:
//Prompt user for serial number to be placed at address of serialNumA
//Default serial number = 200
int8 const serialNumA=100;
#serialize(id=serialNumA,next="200",prompt="Enter S/N")
//Adds serial number log in seriallog.txt
#serialize(id=serialNumA,next="200",prompt="Enter S/N",
log="seriallog.txt")
//Retrieves serial number from serials.txt
#serialize(id=serialNumA,listfile="serials.txt")
//Place serial number at EEPROM address 0, reserving 1 byte
#serialize(dataee=0,binary=1,next="45",prompt="Put in S/N")
//Place string serial number at EEPROM address 0, reserving 2 bytes
#serialize(dataee=0, string=2,next="AB",
prompt="Put in S/N")
USB_STRING_DESC is a table of USB strings and is read by the CCS C Compiler's USB stack when the host PC reads a string from the PIC ®. A value of 3 is passed to the Unicode parameter, to tell the #serialize that the serial number should be encoded as a Unicode string and that it should overwrite the 4th string in USB_STRING_DESC (the first string in USB_STRING_DESC is 0). In the device descriptor for your USB device, the field identifying which string to use for the serial number should also have been set to 3.
#serialize(id=USB_STRING_DESC, unicode=3, prompt="SN#")
Using a CCS device programmer but not the compiler?
The same features can be used by editing the hex file using the CCSload utility. Select the FILE page and then SERIAL NUMBERS and you can fill in manually the parameters:

Save or Save-As the hex file and the serial number works as it did with the compiler directives.
Like us on Facebook. Follow us on Twitter.
About CCS:
CCS is a leading worldwide supplier of embedded software development tools that enable companies to develop premium products based on Microchip PIC ® MCU and dsPIC ® DSC devices. Complete proven tool chains from CCS include a code optimizing C compiler, application specific hardware platforms and software development kits. CCS' products accelerate development of energy saving industrial automation, wireless and wired communication, automotive, medical device and consumer product applications. Established in 1992, CCS is a Microchip Premier 3rd Party Partner. For more information, please visit https://www.ccsinfo.com.
PIC ® MCU, MPLAB ® IDE, MPLAB ® ICD2, MPLAB ® ICD3 and dsPIC ® are registered trademarks of Microchip Technology Inc. in the U.S. and other countries. |
|