Interfacing a 16x2 LCD with SPI, Getting Characters into the Second Row - c

I'm using an MSP430 MCU to read analog signals and display the results on an LCD with a SPI connection. The LCD is a 16x2 that is connected according to the SPI connection details on the Datasheet and uses a Hitachi HD44780 driver. I can fill up the 16 characters of the first row no problem. When I go over 16, the last character does not display(as expected) even if I extend the char array that holds the string that I want to print. The problem is that the second row never displays anything. When there are no characters in a position in the first row, there is still a faint background in all positions, but the second row is always continuously blank. Below are the functions that are used in printing. What am I doing wrong?
I know the wiring is correct and the LCD is functional. To test these, I wired the display to an arduino to test, since the code is much easier and I was able to display characters in bow rows. The non-descriptive variables are defined by the MSP430 source files and include registers, buffers, and other controls to put the device in SPI communication mode.
void AlignLaserTarget()
{
int i,k, j;
struct testResults *ptestResults;
char mess1[17]; //changed from 8 to hold 16 characters
ptestResults=getTestResults();
// reset global vars
timeI1=0;
timeA=0;
i=starResults.ch1Amplitude; //analog integer value to be printed on LCD
j=starResults.ch2Amplitude; //same
k=starResults.ch3Amplitude; //same, but should go in second row
sprintf(mess1,"1:%i 2:%i", i, j);
stringTo_lcd8( mess1);
}
void stringTo_lcd8( char* lcdString )
{
int i;
LCD_COMMAND_MODE; // display code
timer_us(20);
write_lcd8( 0x01); // clear display
timer_ms(2);
LCD_DATA_MODE; //enable data sending pin
for ( i=0; *lcdString !=0 ; ++i)
{
write_lcd8( *lcdString);
++lcdString;
} // end of display code
timer_us(10000); // 10ms delay . should not be needed as normal interval between counts is at least 75 ms or 12 in. at 800ft/min rate
}
//*******************************************************
void write_lcd8( unsigned char lcdbyte)
{
UCA0IFG &= ~UCTXIFG; // CLEAR FLAG
UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
UCA0CTL0 = UCMST+UCSYNC+ UCMSB+ UCCKPH;
UCA0BR0 = 0x80; // /hex80
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
timer_us(100);
LCD_CHIP_ENABLE; // LCD enable pin output
timer_us(20); // added trp
UCA0TXBUF =lcdbyte;
timer_us(150);
while (!(UCA0IFG&UCTXIFG));
UCA0IFG &= ~UCTXIFG; // CLEAR FLAG
LCD_CHIP_DISABLE;
timer_us(30);
UCA0CTL1 |= UCSWRST; // **Put state machine in reset**
UCA0CTL0 |= UCMST+UCSYNC+ UCMSB+ UCCKPH;
UCA0BR0 = 0x02; // /2
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
}

I think you have to design some more driver function before using for application logic. I have taken sample code to set cursor position
void Lcd8_Set_Cursor(char line, char col)
{
if(line == 1)
Lcd8_Cmd(0x80 + col);
else if(line == 2)
Lcd8_Cmd(0xC0 + col);
}
Then use same in your printing logic. When ever length exceeding 16 you can switch line and start writing.

The HD44780 is expecting 40 characters per row, so I just added spaces to fill up the first row and then wrote the characters for the next row. I'm sure there is a better solution but this was fast and it worked. I also had to change the initialization specifications for a two row configuration.

Related

Sending Data From TI TM4C123GH6PM to an HD4478 LCD with a PCF8574T I2C Backpack

I am trying to write a program that will enable me to send data from my TI microcontroller to the very common HD4478 LCD. Rather than utilizing a parallel pin setup, I thought it would be a good idea to try and use a serial setup, so the LCD has a PCF8574T I2C I/O expander backpack. I am fairly new to writing embedded programs, and this is my first time using any real serial wiring communication protocols (I2C/SPI) so I'm struggling a bit to get this to work. Before I explain my confusion, here are the datasheets for the 3 respective components:
Microcontroller Data Sheet: https://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf
LCD data sheet: https://circuitdigest.com/sites/default/files/HD44...
I/O Expander Data Sheet: https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF...
My main problem is, I have no real idea what I'm doing wrong, but I am assuming it's something with the way I am initializing the LCD to 4-bit mode. I am a bit confused by this initialization walkthrough on the LCD data sheet:
And then This latter explanation that further explains how to initialize it to 4 bit mode, which is what I'll be using with my I/O expander:
I don't think I quite grasp whether to send the initial function set Command to 4-bit mode in 8-bit mode or 4-bit mode, and when the exact changeover to 4-bit mode occurs in the initialization process.
The way I transfer data over the data line is the 4-bit mode, so I am sending the upper nibble. and then lower nibble. This goes for both data bytes and command bytes. Below is my code. Currently flashing it and running simply has no effect on the LCD. I know that I am operating at the correct slave address since I do not get any flags present after the initial slave address transmit that would indicate any error. However, all of my commands in I2C_LCD_Enable and the latter command in main() that attempts to send a singular character to the LCD have no effect as well.
#include "TM4C123.h" // Device header
#include "RTE_Components.h" // Component selection
//data pin should be open drain in i2c modules
#define DATA 1
#define COMMAND 0
//I can think of each Pin connected to command/control as a singular bit
void GPIOE_enable(void);
void I2C_enable(void);
void I2C_LCD_enable(void);
void I2C_transfer_byte(char byte,int mode);
void delay_50_ms(void);
uint32_t address_transfer_value;
uint32_t data_value;
#define E 0x04 //bit 2
int main(){
GPIOE_enable();
I2C_enable();
I2C_LCD_enable();
I2C_transfer_byte(0x01,COMMAND);
I2C_transfer_byte(0x80,COMMAND); //set cursor to first row
delay_50_ms();
I2C_transfer_byte('a',DATA);
}
//port E, pins pe4 and pe5, have the alternative function as acting the clock/data lines for I2C module 2
void GPIOE_enable(void){
SYSCTL->RCGCGPIO |= 0x10; //enable port E
GPIOE->DIR |= 0x10 | 0x20;
GPIOE->DEN |= 0x10 | 0x20; //enable pins pe4 and pe5
GPIOE->AFSEL = 0x10 | 0x20; //enable pins Pe4 and Pe5 for their alternate function
GPIOE->ODR |= 0x20; //pe5 is data pin, must be set to open drain
GPIOE->PCTL |= (3 << 16) | (3 << 20);
}
void I2C_enable(void){
SYSCTL->RCGCI2C |= 0x04;
I2C2->MCR |= 0x10; //initialize I2C master
GPIOE->PUR |= 0x10 | 0x20; //I pulled up the Clock and Data Lines because they were low: Not pulling them up won't allow them to transfer from their high to low states when transmission begins and the I2C->MCS & 0x01 condition hangs forever
I2C2->MTPR = 0x09; //see data sheet: This initializes SCL speed to 100k kbps
I2C2->MSA = (0x27 << 1); //see data sheet: This sets slave address and sets mode to TRANSMIT
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
}
//HD775 data sheet explains the initialization process on pages 24 and 42
void I2C_LCD_enable(void){
//not sure how to initialize quite yet...
/*I2C2->MDR = 0x28; //this initializes 4-bit mode. This command, AND THIS COMMAND ONLY, takes only one write since it's still in 8-bit mode
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (1 << E); //set ENABLE to high
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = ~(1<<E); //set ENABLE to low
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();*/
I2C_transfer_byte(0x28,COMMAND);
I2C_transfer_byte(0x06,COMMAND); //Move cursor right
I2C_transfer_byte(0x01,COMMAND); //clear screen
I2C_transfer_byte(0x0F,COMMAND); //turn display on
}
//the upper 4 bits are the data pins : D4, D5, D6, D7 (bits 0-3)
//the lower 3 bits are: RS, RW, E
//to send a command, we should set RS to 0 to select "Command Mode" on LCD
//if mode is 0, or COMMAND, do a logical OR with 0, which will set RS, bit 0, to 0
//if mode is 1, or DATA, do a logical or with 1, so RS, bit 0, is set to 1
//we also need to pulse E, or enable to make sure any of these data/commands actually are executed
//The E pin corresponds to bit 2, so I'll send each data byte with first E enabled, and then E set to to low, to pulse Enable
//send upper nibble, pulse enable, send lower nibble, pulse enable
void I2C_transfer_byte(char byte,int mode){
char byte_shifted;
char byte_upper_nibble;
char byte_lower_nibble;
byte_shifted = byte << 4;
byte_upper_nibble = byte & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) | E; //set command to be most significant bit, and enable E
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01){
data_value = I2C2->MBMON;
}
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) & ~E; //set command to be most significant bit, and disable E (pulsing E enables the command/data)
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
byte_lower_nibble = byte_shifted & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) | E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) & ~E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
}
//clock frequency is 1,000,000 cycles/second
void delay_50_ms(void){
SYSCTL->RCGCTIMER |= 0x01;
TIMER0->CTL = 0;
TIMER0->CFG |= 0x04;
TIMER0->TAMR |= 0x01 | 0x10; //single shot mode, enable interrupt
TIMER0->TAILR = 20000; //1,000,000 / 20,0000 = 50
TIMER0->CTL = 0x01;
while ((TIMER0->RIS & 0x01) == 0);
}
I am using the on-board 3.3V power supply from the TI board as my VCC supply.
I think before discussing that the LCD is not working properly, I want to know whether I2C is working properly and whether the Address PIN define.
The address pin of I/O Expander IC is all connected to HIGH, so your Slave address is 0x27
(I just want to make sure whether this part is okay because if there is a problem, there will be problems in the subsequent tests)
If I2C communication work normally, the I/O Expander IC control should be correct.
( Send some commands to see if the IC outputs according to your commands)
If the I/O expander IC work abnormally, maybe you can check the I2C signal use a logic analyzer or oscilloscope to check whether the I2C signal of the MCU is correct. (Slave Address, ACK...etc)
If the above of them is correct, we can start to check the LCD part!
Reminder: Your hyperlink is failed for the LCD and I/O Expander IC Data Sheet.
For the part of Initializing by internal reset circuit
It means that when you supply power to the LCD, it will perform the actions 1~4 below.
During this period, you can use an oscilloscope or logic analyzer to measure the BF pin. It should be HIGH, always When your VCC rises to 4.5V, BF will continue for another 10ms before pulling LOW.
But if VCC does not rise to 4.5V, it must be initialized through MPU.
I saw at the end of the article
I am using the on-board 3.3V power supply from the TI board as my VCC
supply.
Does your LCD use 3.3V as the power supply? If yes, you shall initialize LCD by MPU.
(But I suggest using the 5V to supply that can reduce the debug time.)
If using the internal initialize function, the LCD setting shall be like below:
8-bit interface
1-line display
5x8 dot character font
So if you want to change to 4-bit mode, need to use the Function set Instruction to set again. (Datasheet P.28)
The signal transmission part must be tested according to the timing diagram
It takes 4 steps to send a command
Transfer upper 4-bit data.
Busy flag check (BF=1)
Busy flag check (BF=0)
Transfer lower 4-bit data
Use the Example of the HD44780U Datasheet for testing (Datasheet P.42)
Step 6, need to be divided into 2 times to send.
And it will show the 'H' on the LCD.

potentiometer adc practice in atmega128

I'm a beginner in this field. My goal is to change the output of 8 LEDs (which are connected to PORTA) according to the potentiometer. I have connected the middle line of the potentiometer to PF0, which is ADC0. I also connected the other two lines to the 5V and ground.
I know there's no problem with the chip or connection because the LEDs are working just fine.
But no matter how I change the code below (what I mean by changing is by slightly changing the ADMUX and ADCSRA registers) no output is shown!
I am using atmega128 with 16MHZ clock. Below is the code that I'm trying to solve.
#include <asf.h>
#include <avr/io.h>
#define F_CPU 16000000L
int init_board(void)
{
DDRA=0xff;
PORTA=0x01;
}
int ADC_init(void)
{
//ADCSRA
ADCSRA = 0b10000111;
//ADMUX
ADMUX = 0b01100000; // middle line connected to ADC0
}
int main (void)
{
init_board();
ADC_init();
ADCSRA |= (ADSC >> 1);
while(1)
{
if(ADSC == 0)
{
uint8_t disp_value = ADCL;
PORTA = disp_value;
delay_ms(200);
ADCSRA |= (ADSC >> 1);
}
}
}
I have no idea why the code doesn't work.
I suppose it's because it didn't set my register correctly, but I've followed all the instructions on the atmega128 datasheet.
First issue is your bit shifting, it should be ADCSRA |= (1 << ADSC).
Next issue is results reading. You set fifth bit of ADMUX to 1, so ADLAR=1 and in that mode result is left adjusted so you should read ADCH.
Moreover when you switch to 10-bit resolution, i.e. you start working with multi-byte results, be aware that reading only ADCL is not enough, see datasheet 23.3 for explanation: "Once ADCL is read, ADC access to data registers is blocked. This means that if ADCL has been read, and a conversion completes before ADCH is read, neither register is updated and the result from the conversion is lost. When ADCH is read, ADC access to the ADCH and ADCL Registers is re-enabled."
Lastly, using hardcoded delays for reading is not good practice especially when you change code later to read ADC as fast as possible. In such case after conversion start you should check if ADIF flag is set or react with interrup when ADEN is set. Refer to datasheet for details.

Multiplexing seven segment display on AVR Atmega324A

I've been stuck on this all day, I'm trying to create a count down timer using two seven segment displays. I want it to start at 20 and count down to zero. While 10< I only want to have the left display on(i.e no 0 in the tens place). I'm using an Atmega 324A. I have all of port C connected to the display segments and am using PIND0 to toggle between the two. Here is what I have so far.
#include <avr/io.h>
#include<util/delay.h>
int main(void) {
int prescale = (8000000/8)/1000-1;
int digit = 1;
uint8_t display;
int seven_seg = {0x3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0C07,0X7F,0X6F};
// Set OC1 to output
DDRD = (1<<0);
DDRC = 0xFF;
OCR1A = prescale;
//clear counter on compare match
TCCR1A = (0<<COM1A1) | (1<<COM1A0);
//Set Prescale and CTC Mode
TCCR1B = (0<<CS12) | (1<<CS11) | (0<<CS10) | (0<<WGM13) | (1<<WGM12);
while(1) {
display++;
if(display>50) display = 0;
for (i = 250; i>0; i--){
PORTD ^= 0<<PIND0;
PORTC = seven_seg[display%10];
PORTD ^= 1<<PIND0;
_delay_ms(100);
for (i = 250; i>0; i--){
PORTD ^= 1<<PIND0;
PORTC = seven_seg[display/10];
PORTD ^= 0<<PIND0;
_delay_ms(100);
}
}
while((TIFR1 & (1<<OCF1A)) == 0) {}
TIFR1 &= (1 << OCF1A);
}
}
All this does is set both displays to 0. Do I need another for loop to iterate through the seven_seg[] array while it's doing this? really not sure how to tackle this one. Any help would be great.
you make 2 big faults:
you don't use timer
you should separate display-driving-logic from value-generating-logic
best thing would be you split the tasks and plan how to implement this.
Task one: providing the data to display
Task two: transfering that data to a display friendly representation
Task three: the aktual displaying of that data
Task one is Easy. lets assume you want to display integers and you have three 7-seg-disps.
So task one is to provide some Data to display.
int16_t numberToDisplay = 234;
Task two is also not that hard. a display friendly representation would be one byte per display element.
#define NUM_7SEGS 3
volatile uint8_t dispData[NUM_7SEGS]; // volatile since it is be accassed by different contexts
now we need some mechanism that transfers the input value to the display data
void val2DispData(int16 val)
{
uint8_t i;
for(i=NUM_7SEGS; i; --i){
uint8_t r = (uint8_t)(val%10);
val /= 10
dispData[i-1] = seven_seg[r];
}
}
fine and now?
Task three is the most difficult one. we need someone who says the output what to do.
Since the want to multiplex the 3 display elements that means:
deactivate the current display element
put the data of the next digit to the outport
activate the next display element
wait a bit.
and this 4 steps we want to do very fast so that the observer does not recognize that only one element is active at a time.
since this is totally independent of the other program logic, we need to do this in "background".
your main program flow simply calls that function and the background timer ISR worrries about displaying.
So we have to set up a timer and call the switching of element data in its interrupt service routine. (for setting up timer and timer interupts please refer another tutorial)
// this have to be called cyclic from timer isr
// frequency is not that important but should be at
// least NUM_7SEGS * 200 Hz to not look ugly
void cyclicDisplayTask()
{
static uint8_t currentElement = 0;
// disable all elements
PORTD = 0;
// put data on the port
PORTC = dispData[currentElement]; // this is why the volatile is necessary. without the compiler would not notice that values may be changed by the main program flow
// enable next element
PORTD = (1<<currentElement);
++currentElement;
if(currentElement>=NUM_7SEGS){
currentElement = 0;
}
}
of course you have to adapt the enabling of specific display elements to your hardware.
please also note that you may youse a transistor to drive the element. an AVR port pin is strong enogh to drive a single segment but the other side that drives the common anodes/cathodes of the segment may be overload. This of course depends on the leds within the segments. if this are low current leds (~2mA) it is ok.

AVR Programming - How to read in consecutive button presses in C

Here's what I have to dp:
Consider an ATmega324A development board and a CSSE2010/CSSE7201 IO Board. Switches
S3 to S0 are connected to AVR port B pins 3 to 0. Push button B0 is connected to AVR port A
pin 0. LEDs L0 and L2 and connected to AVR port C pins 0 and 2 respectively.
LED L0 (red) is the “Locked” LED and should only be on when the lock is locked. LED L2
(green) is the “Unlocked” LED and should only be on when the lock is unlocked. The lock
initially starts in the locked state. The user enters the binary code for a digit on the switches (S3 to S0) and then presses and releases push button B0 to “enter” the first digit. The user then enters the binary code for the second digit on the switches and presses and releases push button B0 to “enter” the second digit. If the digits match the expected value (the last digit of your student number followed by the third digit of your student number) then the lock should be “unlocked”, otherwise it should stay in the locked state until the two digits are entered correctly.
Here is my code so far:
#include <avr/io.h>
/* Seven segment display values */
uint8_t seven_seg[16] = { 63,6,91,79,102,109,125,7,127,111,119,124,57,94,121,113};
int main(void) {
uint8_t digit;
uint8_t temp;
uint8_t digit2;
uint8_t code[2] = {6,3}
DDRA = 11111110; //port A is input (last bit)
DDRB = 0X00; //port B is input
DDRC = 0x0F; //port c is output
DDRD = 0XFF; //set port D to be output
while(1) {
/* Read in a digit from lower half of port C pins */
/* We read the whole byte and mask out upper bits */
PORTB = 1; //Led is red
clock = PINA & 00000001; //read in last bit of port A
temp = PINB & 0X0F; //read in lower half of port b
/* Checks to see the first digit is correct */
if(temp == code[0] && clock == 1) {
digit = temp;
PORTD = seven_seg[temp];
} else {
PORTD = 0;
}
}
I'm getting stuck at the point where I have to read in the second digit. Would I be doing this inside a nested loop of the first? Or how would I go about reading in two digits from my switches, which is clocked in each time from the press of a button?
To make a variable program that can be used for longer number sequences, simply use a loop. for(uint8_t i=0; i<NUMBER_OF_DIGITS; i++). The port reading needs to be inside the loop.
However, you cannot read buttons the way you do. All buttons have an electro-mechanical signal bounce, which you need to filter out to prevent false readings. You must do this on any kind of embedded system.
The simplest way to do this is to sample the button once, save the result, wait a few milliseconds, then sample it again. If the samples compare equal, accept it as result (pressed or not pressed).
Alternatively, you can trigger an interrupt on the edge of the button signal, from there start a timer, and then when the timer runs out, read the port.
More advanced methods use some form of median filters.
So if i get you right you wanna do something like this
Start:
Wait for pushbutton
Wait for first digit
Wait for pushbutton
Wait for second digit
If digits they are the same
Turn off Red LED
Turn on Green LED
Else
Goto start
Can you confirm this? It is always a good thing to do your code in pseudo first. It gives very good overview of what you want to do.

Problems with PIC A/D conversion

I am trying to read analogic signal for a sort of mouse with a pic18f14k50 controller. Here the simple circuit: http://dl.dropbox.com/u/14663091/schematiconew.pdf . I have to read analogic signal from AN9 circuit port. Main function reads from the port, and blinks 30 time if threshold is reached:
void main(void) {
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1) {
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
if(!HIDTxHandleBusy(lastTransmission))
{
int readed = myReadADC2(); //Here i tried both myReadADC2() or myReadADC1()
if(readed>40) { //If read threshold > 40, blink led 30 times
int i;
for(i=0; i<30; i++) {
Delay1KTCYx(0);
mLED_1_On();
Delay1KTCYx(0);
mLED_1_Off();
}
}
lastTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
}//end while
}//end main
I used two method to read from the AN9 port, myReadADC() that uses OpenADC() API method:
int myReadADC(void) {
#define ADC_REF_VDD_VDD_X 0b11110011
OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH9 & ADC_INT_OFF, ADC_REF_VDD_VDD_X & ADC_REF_VDD_VSS, 0b00000010); // channel 9
SetChanADC(ADC_CH9);
ConvertADC(); // Start conversion
while(BusyADC()); // Wait for completion
return ReadADC(); // Read result
}
and myReadADC2(), that implements manual read from the port.
int myReadADC2() {
int iRet;
OSCCON=0x70; // Select 16 MHz internal clock
ANSEL = 0b00000010; // Set PORT AN9 to analog input
ANSELH = 0; // Set other PORTS as Digital I/O
/* Init ADC */
ADCON0=0b00100101; // ADC port channel 9 (AN9), Enable ADC
ADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b10101011; // Right justify result, 12 TAD, Select the FRC for 16 MHz
iRet=100;
ADCON0bits.GO=1;
while (ADCON0bits.GO); // Wait conversion done
iRet=ADRESL; // Get the 8 bit LSB result
iRet += (ADRESH << 8); // Get the 2 bit MSB result
return iDelay;
}
Both cases doesn't works, i touch (sending analogic signal) port AN9 but when I set high threshold (~50) led don't blinks, with low threshold (~0) it blinks immidiatly when i provide power to the PIC. Maybe i'm using wrong port? I'm actually passing AN9 as reading port? Or maybe threshold is wrong? How can i found the right value? Thank you
Here the MPLAB C18 Apis http://dl.dropbox.com/u/14663091/API%20microchip%20C18.pdf .
Regarding function myReadADC2(): you need to switch ANSEL and ANSELH configs as RC7/AN9 is configured in bit 1 of ANSELH. Also call me paranoid but for the line
iRet += (ADRESH << 8);
I always like to either save it a temporary variable first or cast explicitly the value ADRESH before shifting it up:
iRet += (((UINT) ADRESH) << 8);
That way I know for sure the bits won't get lost when shifting up which has bitten me before.
Regarding function myReadADC():
OpenADC() only takes two parameters. I presume that bitfield in the third parameter field is for the analog enable (ADRESH/ADRES). I'm assuming that's handled by SetChanADC() but you may have to set ADRESH/ADRES manually. It may help to set a breakpoint in the debugger and stop after configuration is complete to make sure your registers are set appropriatley.

Resources