|
|
View previous topic :: View next topic |
Author |
Message |
ADD
Joined: 21 Oct 2005 Posts: 3
|
help required with programming PIC code |
Posted: Fri Oct 21, 2005 6:36 am |
|
|
hi there
I am trying to program a PIC16F873 device using another PIC16F873A device. i have been trying to follow the timing diagrams that are shown in the programming specification guides.
I am new to this compiler and have to tried to convert from the cc5x compiler. I have posted my code below. there is a function to put the device into program/verify mode, another to send a command, another to read data and another one to send data.
Can someone tell me if it looks correct.
All help will be greatly appreciated.
thanks
Code: |
#include <16F873.h>
#use delay(clock=4000000)
#fuses XT,NOWDT
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9)
#define PIC_CMD_LOADCONFIG 0x00
#define PIC_CMD_READPROGRAM 0x02
#define PIC_CMD_INCADDRESS 0x06
#define P_MCLR PIN_B0 //Pin MCLR
#define P_DATA PIN_B1 //pin RB7
#define P_CLOCK PIN_B2 //pin RB6
#ifndef WORD
#define WORD unsigned short
#endif
char Ch;
RDA_isr() {
putc('a');
Ch = getc();
}
void program_mode() //suppose to put device into program/verify mode
{
output_low(P_DATA);
output_low(P_CLOCK);
output_low(P_MCLR);
delay_us(0.1);
output_high(P_MCLR);
}
void send_command(unsigned char cmd) //sends out a command and the //clock cycles six times
{
char i;
unsigned char b;
output_high(P_MCLR);
delay_cycles(5);
for(i = 0; i< 6; i++)
{
output_high(P_CLOCK);
if( bit_test(cmd, i))
{
output_high(P_DATA);
}
else
{
output_low(P_DATA);
}
output_low(P_CLOCK);
//cmd >> = 1;
}
}
void sendData(unsigned int tempData)//sends out data word
{
char i;
unsigned char b;
for (i=0; i<16; i++)
{
output_high(P_CLOCK);
if( bit_test(tempData, i))
{
output_high(P_DATA);
}
else
{
output_low(P_DATA);
}
output_low(P_CLOCK);
}
}
unsigned int readData() //reads in a data word
{
char i;
unsigned int tempData = 0;
for (i = 0; i<16; i++)
{
output_high(P_CLOCK);
if(input(P_DATA))
{
tempData |= 0x8000;
}
else {
tempData |= 0x0000;
}
output_high(P_CLOCK);
tempData >>= 1;
}
tempData >>= 1;
tempData &= 0x3FFF;
return tempData;
}
WORD read_DeviceID() //suppose to read the device ID
{ int i;
unsigned int idData;
send_command(PIC_CMD_LOADCONFIG);
sendData(0);
for(i = 0; i<6; i++)
{
send_command(PIC_CMD_INCADDRESS);
}
send_command(PIC_CMD_READPROGRAM);
idData = readData();
return idData;
}
void main() {
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_CLOCK_DIV_2);
setup_spi(FALSE);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
enable_interrupts(INT_RDA);
enable_interrupts(global);
//send_command(PIC_INCADDRESS);
read_deviceID();
}
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 21, 2005 1:10 pm |
|
|
I'll go through your code and make comments.
What programmer are you using to program your PIC ?
Is it a Low Voltage Programmer such as TLVP ? If not, and if you're
using a standard programmer, such as ICD2, PicStart-Plus, Warp13a,
CCS ICD, and many more, then you should add NOLVP to the fuses.
Also, it wouldn't hurt to add BROWNOUT and PUT. Example:
Code: | #fuses XT, NOWDT, BROWNOUT, PUT, NOLVP |
Quote: |
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=9) |
Are you really using 9 bits ? That's not very common.
Also, you should add the ERRORS parameter, which will cause
the compiler to clear any receive overrun errors automatically.
Example:
Code: | #use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7, ERRORS) |
Quote: | #ifndef WORD
#define WORD unsigned short
#endif |
This is wrong. You need to study CCS data types. They are listed
on page 86 (in Acrobat reader) of the October 2005 manual.
http://www.ccsinfo.com/ccscmanual.zip
In CCS a "short" is a 1-bit integer. So if you really want "WORD"
to be a 16-bit unsigned integer, then do this:
Code: | typedef int16 WORD; |
Also, all integer data types in CCS are unsigned by default.
Quote: | RDA_isr() {
putc('a');
Ch = getc();
} |
You're missing the CCS directive #int_rda, which tells the compiler
that RDA_isr() is an isr. Example:
Code: | #int_rda
RDA_isr()
{
Ch = getc();
putc('a');
} |
This is wrong. No floating point arguments may be used with delay_us().
Read the manual. Download it at the link given above.
Also, you're trying to delay for 100 ns ? Your crystal is only 4 MHz.
One instruction cycle is 1 us. You can't do a 100 ns delay.
Quote: |
unsigned int tempData = 0;
tempData |= 0x8000;
|
You need to study the CCS data types. In CCS an "int" is an
8-bit unsigned integer. You want a 16-bit unsigned integer.
You need to declare it like this:
Code: | int16 tempData = 0; |
Quote: | void main() {
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_CLOCK_DIV_2);
setup_spi(FALSE);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
enable_interrupts(INT_RDA);
enable_interrupts(global);
//send_command(PIC_INCADDRESS);
read_deviceID();
} |
You're doing a whole bunch of unnecessary things here. I assume
you're using PCW, which tends to put in all these stuff ? But even
then, you're not using the A/D converter, so why setup the ADC clock ?
And, your crystal frequency is 4 MHz, and the data sheet says to use
"Divide by 8" for that. Example: ADC_CLOCK_DIV_8
For "read_deviceID();" that function returns a value. You're not
putting the return value into a variable. Just letting you know.
Finally, you're letting the program fall off the edge of main(). If you
do that, the PIC will execute a hidden SLEEP instruction that CCS puts
at the end of main. To avoid this, put a while(1); statement right before
the closing brace of main(). That's important. |
|
|
ADD
Joined: 21 Oct 2005 Posts: 3
|
|
Posted: Fri Oct 21, 2005 3:12 pm |
|
|
hi there
thanks for posting your reply. Actually i have to make my own programmer and in this programmer, there is one PIC that is programming another PIC. At the moment for testing purposes, i am using the ICD2.
ok i have added the NOLVP and the BROWNOUT and the PUT.
thanks for spotting out the 9 bits. i didnt realise it when i put the RS232 stuff in. i have changed it to 8 bits.
i tried using typedef int16 WORD; but it gave me an error saying expecting a basic type.
IS there anyway that i can add a delay of 100ns? the datasheets say that there should be a minimum delay of 100ns after the MCLR pin has been put to high so if i cant use 100ns would it still be ok if i used 1us?
yes i am using the PCW compiler.
the way that i have put the return value into a variable is as follows.
Code: |
void main()
{
int storage;
storage = read_DeviceID();
while(1);
}
|
Is that the correct way?
Does the rest of my code make sense? now there is one other thing that i want to ask you.
I have to write other functions in a similar manner such as erase, program, read and verify. these functions should be acted upon the target device. if i am not mistaken, they should all work in the same manner. So what i want to know is that for instance like i have written the read_deviceID function above. How exactly do i test the function to see if it is working? And another thing is that what exactly am i expected to see?
All help will be greatly appreciated.
thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 21, 2005 3:31 pm |
|
|
Quote: | i tried using typedef int16 WORD; but it gave me an error saying expecting a basic type. |
It works OK with the following test program, which was compiled
with PCM vs. 3.235:
Code: |
#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
typedef int16 WORD;
//======================================
main(void)
{
WORD value;
value = 0x1234;
while(1);
} |
Quote: | Is there anyway that i can add a delay of 100ns? |
No.
Quote: | the datasheets say that there should be a minimum delay of 100ns after the MCLR pin has been put to high so if i cant use 100ns would it still be ok if i used 1us? |
Yes.
Quote: |
The way that i have put the return value into a variable is as follows.
void main()
{
int storage;
storage = read_DeviceID();
while(1);
}
Is that the correct way? |
If read_Device() is supposed to return a 16-bit value, then it's wrong.
Remember that in CCS, an "int" is an unsigned 8-bit value. So you're
putting a 16-bit value into an 8-bit variable. I suggest that to avoid
confusion, you should use the special CCS names for unsigned integer
data types:
int1
int8
int16
int32
Quote: | How exactly do i test the function to see if it is working? |
You could make a small test program that calls a function continuously
in a while(1) loop. The function will be called at a sufficient rate, so
that you can sync on the signals with your oscilloscope. Of course, if
you have a logic analyzer, that's better. Then you can look at the
signals, which are basically just SPI-style signals, and compare them
to the timing diagrams given in the Microchip programming document.
This will tell you if you have the proper number of clocks, and if you
have the correct relationship between the clock edge and data, etc. |
|
|
ADD
Joined: 21 Oct 2005 Posts: 3
|
|
Posted: Fri Oct 21, 2005 3:54 pm |
|
|
ok thanks it works now. it was like really strange. i had it first right on top of my declaration list and it gave me an error but when it is after another declaration, it works for instance
Code: |
char Ch;
typedef int16 WORD;
|
so basically what you are saying is that an int is a unsigned value am I right? and for instance if it is
int1 then it is a 1 bit variable
int 8 - 8bit variable etc.
Quote: |
You could make a small test program that calls a function continuously
in a while(1) loop. The function will be called at a sufficient rate, so
that you can sync on the signals with your oscilloscope. Of course, if
you have a logic analyzer, that's better. Then you can look at the
signals, which are basically just SPI-style signals, and compare them
to the timing diagrams given in the Microchip programming document.
This will tell you if you have the proper number of clocks, and if you
have the correct relationship between the clock edge and data, etc.
|
ok cool. so this is what i have been doing so far. i program the device using my ICD2 and then connect the oscilloscope to the data and the clock pins and then while the PIC is being programmed, i check the signals. Ok i am getting the correct signals out from the oscilloscope as far as i can see.
now for instance i want to write a program that can read from the target device. I have been trying to follow the programming specification guide and also have looked at the source code for freeware such as WINPIC and have tried to use that same idea. Now how would i test if my code is actually reading from the device and what exactly would i see to confirm that the device is being read. Another thing is that when testing the code, do i have to connect the target PIC to the master PIC with its output pins? What would be the proper procedure?
thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Oct 21, 2005 4:45 pm |
|
|
Quote: | so basically what you are saying is that an int is a unsigned value am I right? and for instance if it is
int1 then it is a 1 bit variable
int 8 - 8bit variable etc. |
That's right.
Quote: | Now how would i test if my code is actually reading from the device and what exactly would i see to confirm that the device is being read. |
Make a test program that does one operation, and a simple one,
such as reading the Device ID. Use the "Read Data from Program
Memory" command to do this. Then display the result in a terminal
window on your PC with a printf statement.
Look at page 12 in the Programming Specification for the 16F873:
http://ww1.microchip.com/downloads/en/DeviceDoc/39025f.pdf
It has a table with the Device ID values for the 16F87x PICs.
The address of the Device ID word is given on page 4.
If everything is working correctly, when you read address 0x2006,
for the 16F873, then you should see (in binary format):
00 1001 011 x xxxx
The x's are the silicon revision. Microchip doesn't give the raw
silicon revision values for the 16F87x series in any place that I can find.
Let's just assume that Revision B5 has a code of 0 0101.
Then the word would be:
00 1001 011 0 0101
Group that into nibbles, and add two leading zeros, to get this:
0000 1001 0110 0101
Translate that to hex:
0x0965
You should see some number like that as the Device ID/ Silicon Revision
word at address 0x2006. |
|
|
ADD
Joined: 21 Oct 2005 Posts: 3
|
|
Posted: Sat Oct 22, 2005 4:31 am |
|
|
hi there
thanks for your reply. Ok this is what i have written in my code. Does it seem sort of right to test if i can read the deviceID? i am not exactly sure how these built-in functions were so just want to confirm.
Code: |
WORD read_DeviceID()
{ int i;
unsigned int idData;
send_command(PIC_CMD_LOADCONFIG);
sendData(0x3FFF);
for(i = 0; i<6; i++)
{
send_command(PIC_CMD_INCADDRESS);
}
idData = read_program_eeprom(i);
printf(" the device ID is %2X\r\n", idData);
//send_command(PIC_CMD_READPROGRAM);
//idData = readData();
return idData;
}
void main() {
int16 storage;
storage = read_deviceID();
while(1);
}
|
would that work? the other functions such as send_command are shown in the previous post. i am still trying to test to see if code would work. the only thing is that i cant seem to get my ICD2 work with the RS232 cable. it works with the USB. thats why i need to sort of have an idea if the code looks right.
just a thought though while posting this post. would it work if i use the ICD2 via the USB port to program the device on the demo board and then connect the demo board using a RS232 cable to the PC and then use hyperterminal to see if something is coming out?
Another thing is that i just need that one programmed PIC to test right now right? i dont need the target PIC to be connected to the programmed PIC right.
thanks |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Oct 22, 2005 10:04 am |
|
|
Quote: | would that work? the other functions such as send_command are
shown in the previous post. i am still trying to test to see if code would
work. the only thing is that i cant seem to get my ICD2 work with the
RS232 cable. it works with the USB. thats why i need to sort of have an
idea if the code looks right. |
You don't need it working with the RS232 cable. Use USB.
Quote: | just a thought though while posting this post. would it work if i use
the ICD2 via the USB port to program the device on the demo board and
then connect the demo board using a RS232 cable to the PC and then use
hyperterminal to see if something is coming out? |
That's what I meant.
Quote: |
Another thing is that i just need that one programmed PIC to test right
now right? i dont need the target PIC to be connected to the programmed
PIC right. |
It depends on what sort of testing you want to do.
Quote: |
this is what i have written in my code. Does it seem sort of right to test
if i can read the deviceID? i am not exactly sure how these built-in
functions were so just want to confirm. |
It doesn't look right to me. It doesn't fit the 16F87x Programming
Specification for the Read Data command. They don't show sending
the command 6 times, as you are doing. The Send_Command()
routine sends 6 bits. You need to look at your own code.
Also, you're using the read_program_eeprom() function which reads
program memory on the PIC on the Demo board. Your goal is
read data from the Target PIC, not the one on the Demo board.
You do this by toggling the clock and data lines going to the Target PIC,
not by reading program memory on the Demo board PIC.
Quote: | I also have looked at the source code for freeware such as WINPIC |
You must do the project. You have sample code. Follow it.
I don't want to approve every routine as you write them.
My goal in answering your post was to give you some tips on
translating code from CC5x to CCS. I basically have done that.
I don't want to write your routines or do your project for you. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|