Problems with PIC A/D conversion - c

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.

Related

how can I make multiple ultrasonic sensors work at the same time using atmega32

I am working on an auto-parking car robot and I am using 8 (hc-sr04) ultrasonic sensors (2 at each side) but the problem is that I am using atmega32 which has limited resources only 3 external interrupts and 3 timers (and even if using interrupts somehow works I might run into risk to have two interrupts triggered at the same time).
I am using this sensor : http://ram-e-shop.com/oscmax/catalog/product_info.php?products_id=907
I've tried using digital I/O pins with polling procedure but it didn't work.
here is the code for polling procedure:
unsigned int read_sonar(){
int dist_in_cm = 0;
init_sonar(); // Setup pins and ports
trigger_sonar(); // send a 10us high pulse
while(!(ECHO_PIN & (1<<ECHO_BIT))){ // while echo pin is still low
USART_Message("echo pin low\r\n");
trig_counter++;
uint32_t max_response_time = SONAR_TIMEOUT;
if (trig_counter > max_response_time){ // SONAR_TIMEOUT
return TRIG_ERROR;
}
}
TCNT1=0; // reset timer
TCCR1B |= (1<<CS10); // start 16 bit timer with no prescaler
TIMSK |= (1<<TOIE1); // enable overflow interrupt on timer1
overFlowCounter=0; // reset overflow counter
sei(); // enable global interrupts
while((ECHO_PIN & (1<<ECHO_BIT))){ // while echo pin is still high
USART_Message("echo pin high\r\n");
if (((overFlowCounter*TIMER_MAX)+TCNT1) > SONAR_TIMEOUT){
USART_Message("timeout");
return ECHO_ERROR; // No echo within sonar range
}
};
TCCR1B = 0x00; // stop 16 bit timer with no prescaler
cli(); // disable global interrupts
no_of_ticks = ((overFlowCounter*TIMER_MAX)+TCNT1); // counter count
dist_in_cm = (no_of_ticks/(CONVERT_TO_CM*CYCLES_PER_US)); // distance in cm
return (dist_in_cm );}
This method doesn't work if I want to read all sensors at the same time, because it gets stuck in the loop for a while.
I also tried using freeRTOS to build a task that checks the state of pins like every 1msec but this won't be a time accurate.
any help?
Assuming that You use internal clock which is 8MHz I would try to handle this inside timer overflow interrupt and would use whole port to connect the sensors.
Use Timer in normal mode or CTC mode (which I find quite intuitive) to ensure periodical interrupts. Set the appropriate period. Remember that the clock has pretty low frequency so don't exaggerate (I think that 0,25 ms will fit).
Connect the sensors to one port, e.g. PORTB. This is a nice situation because ATmega32 has 4 ports with pins numbered from 0-7 and you use 8 sensors so the register for the specific port can cover all of the pins and You can use one read to get states of all of the pins.
Implement the logic:
volatile uint8_t sensors_states;
volatile uint8_t read_flag = 0;
ISR(TIMER0_OVF_vect)
{
sensors_states = PORTB;
read_flag = 1;
}
int main()
{
// Initialize peripherals ...
// You must assume on your own how much time could the pin be held
// in the same state. This is important because the number must not
// be bigger than max value for the type of the array
uint8_t states_time[8] = {0, 0, 0, 0, 0, 0, 0, 0};
uint8_t prev_sensors_states = PORTB;
while(1)
{
// Wait until the flag will be set in the ISR
if(read_flag)
{
for(uint8_t i = 0, mask = 0x80 ; i < 8 ; i ++, mask >>= 1)
{
states_time[i]++;
// Compare the previous state and present state on each pin
uint8_t state = mask & sensors_states;
if((mask & prev_sensors_states) != state)
{
// Here you can use the state of the pin and the duration of that state.
// Remember that when 'state' is > 0 it means that previous state of the
// pin was '0' and if if 'state' is == 0 then the previous pin state
// was '1' (negation).
do_something_with_pin_change(states_time[i], state);
states_time[i] = 0;
}
}
// Save the previous states of the pins
prev_sensors_states = sensors_states;
// Clear the flag to await next data update
read_flag = 0;
}
}
}
If You will try to use FreeRTOS You could use ulTaskNotifyTake and vTaskNotifyGiveFromISR, instead of using read_flag, to implement a simple mechanism which will notify a task from the interrupt that the port has been read. The processor will go into idle state for a while and you could then invoke a sleep function to minimize power consumption.
I don't know what You want to do with this data so I've invoked do_something_with_pin_change function to indicate the point where You can use the data.
To sum up for this solution You would only use one interrupt and of course 8 pins.

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

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.

STM8 interrupt serial receive

I am new to STM8, and trying to use a STM8S103F3, using IAR Embedded Workbench.
Using C, I like to use the registers directly.
I need serial on 14400 baud, 8N2, and getting the UART transmit is easy, as there are numerous good tutorials and examples on the net.
Then the need is to have the UART receive on interrupt, nothing else will do.
That is the problem.
According to iostm8s103f3.h (IAR) there are 5 interrupts on 0x14 vector
UART1_R_IDLE, UART1_R_LBDF, UART1_R_OR, UART1_R_PE, UART1_R_RXNE
According to Silverlight Developer: Registers on the STM8,
Vector 19 (0x13) = UART_RX
According to ST Microelectronics STM8S.h
#define UART1_BaseAddress 0x5230
#define UART1_SR_RXNE ((u8)0x20) /*!< Read Data Register Not Empty mask */
#if defined(STM8S208) ||defined(STM8S207) ||defined(STM8S103) ||defined(STM8S903)
#define UART1 ((UART1_TypeDef *) UART1_BaseAddress)
#endif /* (STM8S208) ||(STM8S207) || (STM8S103) || (STM8S903) */
According to STM8S Reference manual RM0016
The RXNE flag (Rx buffer not empty) is set on the last sampling clock edge,
when the data is transferred from the shift register to the Rx buffer.
It indicates that a data is ready to be read from the SPI_DR register.
Rx buffer not empty (RXNE)
When set, this flag indicates that there is a valid received data in the Rx buffer.
This flag is reset when SPI_DR is read.
Then I wrote:
#pragma vector = UART1_R_RXNE_vector //as iostm8s103f3 is used, that means 0x14
__interrupt void UART1_IRQHandler(void)
{ unsigned character recd;
recd = UART1_SR;
if(1 == UART1_SR_RXNE) recd = UART1_DR;
etc.
No good, I continually get interrupts, UART1_SR_RXNE is set, but UART1_DR
is empty, and no UART receive has happened. I have disabled all other interrupts
I can see that can vector to this, and still no good.
The SPI also sets this flag, presumably the the UART and SPI cannot be used
together.
I sorely need to get this serial receive interrupt going. Please help.
Thank you
The problem was one bit incorrectly set in the UART1 setup.
The complete setup for the UART1 in the STM8S103F3 is now(IAR):
void InitialiseUART()
{
unsigned char tmp = UART1_SR;
tmp = UART1_DR;
// Reset the UART registers to the reset values.
UART1_CR1 = 0;
UART1_CR2 = 0;
UART1_CR4 = 0;
UART1_CR3 = 0;
UART1_CR5 = 0;
UART1_GTR = 0;
UART1_PSCR = 0;
// Set up the port to 14400,n,8,2.
UART1_CR1_M = 0; // 8 Data bits.
UART1_CR1_PCEN = 0; // Disable parity.
UART1_CR3 = 0x20; // 2 stop bits
UART1_BRR2 = 0x07; // Set the baud rate registers to 14400
UART1_BRR1 = 0x45; // based upon a 16 MHz system clock.
// Disable the transmitter and receiver.
UART1_CR2_TEN = 0; // Disable transmit.
UART1_CR2_REN = 0; // Disable receive.
// Set the clock polarity, clock phase and last bit clock pulse.
UART1_CR3_CPOL = 0;
UART1_CR3_CPHA = 0;
UART1_CR3_LBCL = 0;
// Set the Receive Interrupt RM0016 p358,362
UART1_CR2_TIEN = 0; // Transmitter interrupt enable
UART1_CR2_TCIEN = 0; // Transmission complete interrupt enable
UART1_CR2_RIEN = 1; // Receiver interrupt enable
UART1_CR2_ILIEN = 0; // IDLE Line interrupt enable
// Turn on the UART transmit, receive and the UART clock.
UART1_CR2_TEN = 1;
UART1_CR2_REN = 1;
UART1_CR1_PIEN = 0;
UART1_CR4_LBDIEN = 0;
}
//-----------------------------
#pragma vector = UART1_R_RXNE_vector
__interrupt void UART1_IRQHandler(void)
{
byte recd;
recd = UART1_DR;
//send the byte to circular buffer;
}
You forget to add global interrupt flag
asm("rim") ; //Enable global interrupt
It happens at non isolated connections whenever you connect your board's ground with other source's ground (USB<->TTL converter connected to PC etc.), In this case microcontroller is getting noise due to high value SMPS's Y capacitor etc.
Simply connect your RX and TX line's via 1K resistor and put 1nF (can be deceased for high speed) capacitors on these lines and to ground (micro controller side) to suppress noises.

Why would comparisons on my Xmega work when a breakpoint is set on that line, but not when running normally?

I am using an Xmega384C3, and all I am trying to do is send a signal down an output port and read it on an input port. I have PORTC set as an output port, and PORTA set as an input port. The corresponding pins on each port are shorted together (PortA pin 0 to PortC pin 0, etc).
My issue appears in the following code:
uint8_t i;
int count = 0;
for(i=2; i<8; i++) {
PORTC.OUT = (1 << i);
if (PORTA.IN == PORTC.OUT) {
count++;
}
}
if (count == 6) {
//success
}
I basically just want to read that when I send a logic high down a pin on PORTC, that I can read it on the corresponding pin on PORTA. When I let the code run normally, it does not find any matches and therefore never increments count. However if I add a break point on the line where the if comparison occurs, it then evaluates to true and increments count as expected. Additionally, I can see the ports have the correct values in Atmel Studio's I/O view feature. Any ideas?
It was a timing issue of not allowing long enough for the value on the pin to reflect what I set it as in the code. The issue was resolved by adding a 1 micro second delay using the delay function _delay_us(1) from the delay.h library.
See Any XMEGA Manual > I/O Ports > Overview > General I/O Pin Functionality
Before the IN register there is a synchronizer so the input signal does not instantly appear in the input register. A couple of clock cycles would be enough to wait.

I2c bit banging Programming using C

I am trying to write a c code for I2c Using bit banging. I have revised the Code of wiki(http://en.wikipedia.org/wiki/I%C2%B2C). But i am unable to get a result. As per my understanding the code in the wiki is not correct. Many changes i made, but one of the major change i made,where wiki failed tell correctly is tagged/label that line with /TBN/. My code is below,
// Hardware-specific support functions that MUST be customized:
#define I2CSPEED 135
#define SCL P0_0
#define SDA P0_1
void I2C_delay() { volatile int v; int i; for (i=0; i < I2CSPEED/2; i++) v; }
bool read_SCL(void); // Set SCL as input and return current level of line, 0 or 1
bool read_SDA(void); // Set SDA as input and return current level of line, 0 or 1
void clear_SCL(void); // Actively drive SCL signal low
void clear_SDA(void); // Actively drive SDA signal low
void Set_SDA(void); // Actively drive SDA signal High;
void Set_SCL(void); // Actively drive SCL signal High
void Set_SCL(void)
{
//make P0_0 as OutputPort
SCL = 1;
}
void Set_SDA(void)
{
//make P0_1 as OutputPort
SDA = 1;
}
void clear_SCL(void)
{
//make P0_0 as OutputPort
SCL = 0;
}
void clear_SDA(void)
{
//make P0_1 as OutputPort
SDA = 0;
}
bool read_SCL(void)
{
//make P0_0 as InputPort
return SCL;
}
bool read_SDA(void)
{
//make P0_0 as InputPort
return SDA;
}
void i2c_start_cond(void) {
// set SDA to 1
Set_SDA();/*TBN*/
set_SCL();
// SCL is high, set SDA from 1 to 0.
I2C_delay();
clear_SDA();
I2C_delay();
I2C_delay();
clear_SCL();//make SCL Low for data transmission
started = true;
}
void i2c_stop_cond(void){
// set SDA to 0
clear_SDA();
I2C_delay();
// SCL is high, set SDA from 0 to 1
Set_SCL();/*TBN*/
I2C_delay();
Set_SDA();
}
// Write a bit to I2C bus
void i2c_write_bit(bool bit) {
if (bit) {
Set_SDA();/*TBN*/
} else {
clear_SDA();
}
I2C_delay();
clear_SCL();
}
// Read a bit from I2C bus
bool i2c_read_bit(void) {
bool bit;
// Let the slave drive data
read_SDA();
I2C_delay();
// SCL is high, now data is valid
bit = read_SDA();
I2C_delay();
clear_SCL();
return bit;
}
// Write a byte to I2C bus. Return 0 if ack by the slave.
bool i2c_write_byte(bool send_start,
bool send_stop,
unsigned char byte) {
unsigned bit;
bool nack;
if (send_start) {
i2c_start_cond();
}
for (bit = 0; bit < 8; bit++) {
i2c_write_bit((byte & 0x80) != 0);
byte <<= 1;
}
nack = i2c_read_bit();
if (send_stop) {
i2c_stop_cond();
}
return nack;
}
// Read a byte from I2C bus
unsigned char i2c_read_byte(bool nack, bool send_stop) {
unsigned char byte = 0;
unsigned bit;
for (bit = 0; bit < 8; bit++) {
byte = (byte << 1) | i2c_read_bit();
}
i2c_write_bit(nack);
if (send_stop) {
i2c_stop_cond();
}
return byte;
}
This code is for one master in a bus. I request experts to review my code and let me know my mistakes.
There's a lot of flat-out wrong information in the comments.
First of all, your read_bit() function never toggles the clock. That's probably your problem, along with #user3629249's comment that the master sends an ACK bit after every 8 bits from the slave. You'll have to address this in your read_byte() function.
Second: I2C does not care about clock jitter; it defines only that the data must be stable when the clock falls. There will be some nanoseconds on either side of the clock edge where the data must not change but that is not the issue here. I2C slaves don't "lock on" to the clock. In fact the I2C specification doesn't define a lower limit to high or low SCL times. You could conceivably wait days or years between clock ticks. SMB does define a timeout, but that's not your problem here either.
Lastly: oversampling doesn't really apply to I2C. You can read the bit a few times to make sure it hasn't changed but a properly functioning I2C slave will not be changing the data until sometime after the rising edge of the SDA signal, which is many thousands of nanoseconds away from the critical falling edge. I2C is not asynchronous like your usual serial port/UART.
First bit banging I2C is way more complicated than bit banging SPI.
So first I would take an SPI EEPROM like the 25AA1024 and try my bit banging skills.
Then try a simple Adadfruit I2C device like this one
https://www.adafruit.com/products/1855
It only requires I2C out and you easily port the Arduino code to C or C#.
I know that the USB device Nusbio implement I2C and SPI bit banging written in C#.
See their source code at https://github.com/madeintheusb/Nusbio.Samples
You need to implement P0_0 and P0_1, somehow. The calls you marked finally involve them and I am not seeing them in your code nohow.
If you are developing this on a real hardware then in order to affect corresponding pins in your hardware you need implement P0_0 and P0_1 macros with code accessing the particular control registers to control the logical levels at these two lines.
I stripped down the wikipedia code for my special-purpose application,
but I only got it working after I fixed the Stop condition. It never
raises the SCL so I added the following to that function:
// Stop bit setup time, minimum 4us
set_SCL(); // added this line
I2C_delay();
I am verifying the correctness now, and if so I'll update wikipedia
myself.

Resources