PIC16F877A ambiguity

Status
Not open for further replies.

irilias

Newbie level 4
Joined
Aug 18, 2010
Messages
7
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Location
Algeria
Visit site
Activity points
1,338
Hi, i'm new to programming PIC microcontrollers and will be using one (pic16f877a) to develop a project using an MMC/SD card.

i found a piece of code that initializes an MMC card into SPI mode, sets the block length, and writes raw data into 512 bytes blocks.

here's the code :
Code:
/************************** MMC Init **************************************/ 
/* Initialises the MMC into SPI mode and sets block size, returns 0 on success */ 
int mmc_init() 
{ 
int i; 
SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4 | SPI_SS_DISABLED); 
*0x94 |= 0x40;		// set CKE = 1 - clock idle low 
*0x14 &= 0xEF; 		// set CKP = 0 - data valid on rising edge 
OUTPUT_HIGH(PIN_C2); 	// set SS = 1 (off) 
for(i=0;i<10;i++)  	// initialise the MMC card into SPI mode by sending clks on 
{ 
SPI_WRITE(0xFF); 
} 
OUTPUT_LOW(PIN_C2);  // set SS = 0 (on) tells card to go to spi mode when it receives reset 
SPI_WRITE(0x40);	 // send reset command 
SPI_WRITE(0x00);	 // all the arguments are 0x00 for the reset command 
SPI_WRITE(0x00); 
SPI_WRITE(0x00); 
SPI_WRITE(0x00); 
SPI_WRITE(0x95);	 // precalculated checksum as we are still in MMC mode 
puts("Sent go to SPI\n\r"); 
if(mmc_response(0x01)==1) return 1;  
// if = 1 then there was a timeout waiting for 0x01 from the mmc 
puts("Got response from MMC\n\r"); 
i = 0; 
 while((i < 255) && (mmc_response(0x00)==1))  // must keep sending command if response 
{ 
SPI_WRITE(0x41);     // send mmc command one to bring out of idle state 
SPI_WRITE(0x00);    // all the arguments are 0x00 for command one 
SPI_WRITE(0x00); 
SPI_WRITE(0x00); 
SPI_WRITE(0x00); 
SPI_WRITE(0xFF);   // checksum is no longer required but we always send 0xFF 
i++; 
} 
if(i >= 254) return 1;  // if >= 254 then there was a timeout waiting for 0x00 from the mmc 
puts("Got out of idle response from MMC\n\r"); 
OUTPUT_HIGH(PIN_C2);   // set SS = 1 (off) 
SPI_WRITE(0xFF);   // extra clocks to allow mmc to finish off what it is doing 
OUTPUT_LOW(PIN_C2);   // set SS = 0 (on) 
SPI_WRITE(0x50);    // send mmc command one to bring out of idle state 
SPI_WRITE(0x00); 
SPI_WRITE(0x00); 
SPI_WRITE(0x02);   // high block length bits - 512 bytes 
SPI_WRITE(0x00);  // low block length bits 
SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF 
if((mmc_response(0x00))==1) return 1; 
OUTPUT_HIGH(PIN_C2);  // set SS = 1 (off) 
puts("Got set block length response from MMC\n\r"); 
return 0; 
} 

/************************** MMC Write Block  *******************************/ 
int mmc_write_block(unsigned long block_number) 
{ 
unsigned long i; 
unsigned long varh,varl; 
char p,f,n; 
set_adc_channel(0); 
delay_us(10); 
f=read_adc(); 
n=(f*5)/256; 
p=(n*9375)/206; 
varl=((block_number&0x003F)<<9); 
varh=((block_number&0xFFC0)>>7); 
puts("Write block\n\r");   // block size has been set in mmc_init() 
OUTPUT_LOW(PIN_C2);   // set SS = 0 (on) 
SPI_WRITE(0x58);   // send mmc write block 
SPI_WRITE(varh); 
SPI_WRITE(varl); 
SPI_WRITE(0x00);  // always zero as mulitples of 512 
SPI_WRITE(0xFF); // checksum is no longer required but we always send 0xFF 
if((mmc_response(0x00))==1) return 1; 
puts("Got response to write block\n\r"); 
SPI_WRITE(0xFE);   // send data token 
for(i=0;i<512;i++) 
{ 
SPI_WRITE(p);   // send data 
} 
SPI_WRITE(0xFF);   // dummy CRC 
SPI_WRITE(0xFF); 
if((SPI_READ(0xFF)&0x0F)!=0x05) return 1; 
puts("Got data response to write block\n\r"); 
OUTPUT_HIGH(PIN_C2);  // set SS = 1 (off) 
return 0; 
} 

/************************** MMC get response  *******************************/ 
/**** Repeatedly reads the MMC until we get the response we want or timeout ****/ 
int mmc_response(unsigned char response) 
{ 
unsigned long count = 0xFFFF;
  // 16bit repeat, it may be possible to shrink this to 8 bit but there is not much point 
while(SPI_READ(0xFF) != response && --count > 0); 
if(count==0) return 1;  // loop was exited due to timeout 
else return 0;	 // loop was exited before timeout 
} 
/**************************************************************************/

my question concerns the "MMC Write Block" Function, as i understood from the pic16f877a datasheet, this pic has a 10 bits A/D converter, so why would the coder declare "f" as an 8bit variable and reads a 10bit value from the A/D ? and then he proceeds to calculate the quantum on a 256 range? ( n= (fx5)/256 )

i mean wouldn't it be correct if we declare "f" as a long variable (16bits) and put n= (fx5)/1024 ? am i missing something?

any help would be much appreciated, Thank you


Note: n represents the corresponding analog value to be stored (or displayed)
 

It is most probable that he left-justified the A/D result instead of using right-justified.

With left justified, the ADRESH contains the 8 significant bits of the 10-bit A/D, and the ADRESL contains the 2 least significant bits.

Your application code can still obtain the equivalent voltage by reading only the ADRESH and ignoring the ADRESL.

But the reading has lost 2 bits of accuracy. If you application can tolerate it, then its ok.

The resulting code, however, is simpler and faster since you only have to divide by 256 instead of 1024.

So its a trade-off.
 


Thanks a lot for your thoroughly answer and of course your time.
i just have one more question, do we have to configure any control bits in ADCON0 or ADCON1 to tell the PIC that we're only using the ADRESH?
 

Just one step: clear the ADFM bit (ADCON1<7>) to use the left-justified A/D result.

In your code, just read the ADRESH register.

adc_val = ADRESH;
voltage = (adc_val / 256.0) * 5.0;
 

Just one step: clear the ADFM bit (ADCON1<7>) to use the left-justified A/D result.

In your code, just read the ADRESH register.

adc_val = ADRESH;
voltage = (adc_val / 256.0) * 5.0;

i cleared the ADFM bit by putting this statement *0x9F &= 0xEF in the main function. now few more questions come in mind :

- you know, the program author didn't even bother clearing the ADFM bit, can someone do that?! i mean reading just an 8bit value without
clearing the ADFM bit?

- did i put the statement *0x9F &= 0xEF in the right place?

- if i set all the adc ports as analog, the Vref+ and Vref- would be set to +5V and GND by default, right? or should i connect the corresponding pins to +5V and GND? like shown in the schematic..



i'm using an 8Mhz xtal, in the PIC16F877A datasheet they've mentioned that the TAD required for the conversion has to be at least 16Tosc for 10Mhz xtal. so i should probably change the ADC_CLOCK_DIV_2 to ADC_CLOCK_DIV_16, right?

the last question is about acquisition time, the minimum TAQ is 19.8 us, now is the DELAYs of 10us (written in the code) after the built-in function set_adc_channel (in the MAIN function) suppose to be the acquisition time?

here's the code :

Code:
 #include<16f877a.h> 
#fuses hs,nowdt, noprotect, nolvp 
#use delay(clock=10000000) 
#include<math.h> 
#include<string.h> 
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7) 
#define lcd_e1 pin_d7 
#define lcd_rs1 pin_d6 
#define lcd_port output_b  
#define lcd_direction set_tris_b 
#define lcd_command set_tris_c 
int mmc_init(); 
int mmc_response(unsigned char response); 
int mmc_write_block(unsigned long block_number); 
void init_lcd();		 
void putdata_lcd(int valued); 
void erase_lcd(); 
void putcmd_lcd(int valuec); 
void goto_lcd(int x, int y); 
void put_string_lcd(char v[16]); 
/************************** MMC Init **************************************/ 
/* Initialises the MMC into SPI mode and sets block size, returns 0 on success */ 
int mmc_init() 
{ 
}
/************************** MMC Write Block  *******************************/ 
int mmc_write_block(unsigned long block_number) 
{ 
} 
/************************** MMC get response  *******************************/ 
/**** Repeatedly reads the MMC until we get the response we want or timeout ****/ 
int mmc_response(unsigned char response) { } 
/**************************************************************************/ 
/************************************ LCD ** *******************************/ 

void init_lcd() {} 
void putdata_lcd(int valued) {} 
void erase_lcd() { } 
void putcmd_lcd(int valuec) { } 
void goto_lcd(int x, int y) {} 
void put_string_lcd(char v[16]) { } 
void print() { } 
void delay() { } 
/**************************************************************************/
/********************************  MAIN  ***********************************/
void main() 
{
long sy; 
float s,f,p,n,m; 
char v[16]; 
sy=0; 
*0x9F &= 0xEF;  // CLEAR ADFM BIT
mmc_init(); 
lcd_direction(0x00); 
lcd_command(0x00); 
set_tris_d(0x00); 
setup_adc_ports( all_analog ); 
setup_adc(ADC_CLOCK_DIV_2);  // i should change it to ADC_CLOCK_DIV_16
//start deflate 
start: 
set_adc_channel(0); 
delay_us(10); 
s=read_adc(); 
if (s>25){ 
output_high(pin_d3); 
output_low(pin_d2); 
goto start;} 
output_low(pin_d3); 
delay_ms(100); 
print(); 
delay_ms(3000); 
//inflate state 
init_lcd(); 
goto_lcd(0,1); 
sprintf(v,"pressure="); 
put_string_lcd(v); 
inflate: 
set_adc_channel(0); 
delay_us(10); 
f=read_adc(); 
n=(f*5)/256; 
p=(n*9375)/206; 
goto_lcd(9,1); 
sprintf(v,"%fummHg",p); 
put_string_lcd(v); 
if (f<178){ 
output_high(pin_d2); 
goto inflate;} 
output_low(pin_d2); 
//deflate state 
deflate: 
set_adc_channel(1); 
delay_us(10); 
s=read_adc(); 
output_high(pin_d3); 
delay_ms(100); 
output_low(pin_d3); 
delay_ms(100); 
if (s>205){ 
sy++;} 
if (sy<4){ 
goto deflate;} 
set_adc_channel(0); 
delay_us(10); 
f=read_adc(); 
n=(f*5)/256; 
p=(n*9375)/206; 
mmc_write_block(1); 
init_lcd(); 
goto_lcd(0,1); 
sprintf(v,"Sys pres ="); 
put_string_lcd(v); 
goto_lcd(0,2); 
sprintf(v,"%fummHg",p); 
put_string_lcd(v); 
delay_ms(100); 
//diastole 
diastole: 
output_high(pin_d3); 
delay_ms(100); 
output_low(pin_d3); 
delay_ms(100); 
set_adc_channel(1); 
delay_us(10); 
s=read_adc(); 
if (s>128) 
{delay(); 
goto diastole;} 
set_adc_channel(0); 
delay_us(10); 
f=read_adc(); 
n=(f*5)/256; 
m=(n*9375)/206; 
mmc_write_block(10); 
init_lcd(); 
goto_lcd(0,1); 
sprintf(v,"Diast pres ="); 
put_string_lcd(v); 
set_adc_channel(0); 
goto_lcd(0,2); 
sprintf(v,"%fummHg",m); 
put_string_lcd(v); 
end: 
set_adc_channel(0); 
delay_us(10); 
s=read_adc(); 
if (s>25) 
{output_high(pin_d3); 
goto end;} 
output_low(pin_d3);}
 

i cleared the ADFM bit by putting this statement *0x9F &= 0xEF
Clears unused SFR 0x9F.4 rather than ADFM 0x9F.7

You are apparently using CCS C and it's built-in f7znction read_adc(). The compiler is expecting that the ADC is setup with the respective initialization functions. If you don't use it, you need to find out which ADC settings are made by default and which have to be done by direct register accesses. In case of doubt, you should inspect the generated code.

You'll find out that ADFM is cleared in CCS C unless you specify #device ADC=10 and use setup_adc().

In other words, this is a CCS specific rather than a general PIC16 related question.
 


thanks for the quick reply, i actually meant to use 7F instead of EF : *0x94 &= 0x7F ..
now, if i understand you correctly... the A2D will output an 8bit value by default unless #device ADC=10 was specified, am i right?!....
 

Here are my answers to each question:

- The ADFM is cleared by default, so the (lazy) author may not have bothered setting this particular bit.

- Im not exactly sure about that statement. Looks like FvM above gave the correct one. Im not familiar with CCS C anymore, though i used it a few times 6-7 years ago but I ditched it in favor of Hi-Tech C.

- Absolutely no need to connect the VDD and GND to VREF+ and VREF- pins, respectively. That's a bad circuit.

- ADC_DIV_16 is correct since you get a Tad = 2us, which is more than the 1.6us minimum.

- Time delay should be greater than 19.8us, so obviously 10us is wrong.
 

i can't thank you enough for your answers... (kind of what i expected, but it's good to hear it from someone who obviously knows)... the last question ( i know i asked a lot of them already but i just want to make sure) i don't need to add #device ADC=8 because it's the default ,right?
 

i don't need to add #device ADC=8 because it's the default ,right?
Apparently. My basic approach is: If you are using CCS built-in functions, use them completely unless you are aware of a bug needing a workaround. But in the latter case, you'll have already checked what each function call does (or should do when working correctly) and you can easily supplement the missing SFR actions.
 

Status
Not open for further replies.

Similar threads

Cookies are required to use this site. You must accept them to continue using the site. Learn more…