CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Failed to read SPI register on PIC16F15356

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
songdaegon



Joined: 17 Jun 2025
Posts: 8

View user's profile Send private message Send e-mail

Failed to read SPI register on PIC16F15356
PostPosted: Wed Jul 16, 2025 6:50 pm     Reply with quote

Hello,

I hope this message finds you well.

We are currently experiencing an issue with SPI communication between the PIC16F15356 and the ADS8689.

Although we are receiving raw ADC values, the readings appear to be incorrect. For example, with a 4mA / 1V input, we are seeing approximately 25,000 as the raw value at 8mA input.

We initially thought adjusting the ADS8689 register settings would resolve the issue, but it seems the register values are not being updated at all.

Below is the code we’re using to attempt the register write:

Code:

ADS8689_WriteRegister(0x10, 0x7D08);  // DATAOUT_CTL_REG
delay_ms(2);
ADS8689_WriteRegister(0x14, 0x000B);  // RANGE_SEL_REG (0~5.12V)
delay_ms(2);



When reading register 0x14 of the ADS8689, the value keeps alternating like this:

0x0008
0x0009
0x0008
0x0009

It continuously changes in this pattern.




main.c
Code:

#include "main.h"
#include "ads8689.h"
#include "usart.h"
#include "modbus.h"

// ---------------- Timer ISR ----------------
#INT_TIMER0
void TIM0_ISR(void)
{
   CLEAR_INTERRUPT(INT_TIMER0); // Prevent repeated interrupts
   SET_TIMER0(TIM0_1ms);        // Set timer to trigger again after 1ms
   Sys_ms++;                    // System time counter in milliseconds
   UART_Rx.ms++;                // Used to check UART receive timeout
}

void PORT_Init(void)
{
  OUTPUT_LOW(PIN_C3);  // Set CLK pin LOW
  OUTPUT_LOW(PIN_C5);  // Set MOSI pin LOW
  INPUT(PIN_C4);       // Set MISO pin as input
  delay_ms(10);
 
  // 2. RST LOW → HIGH (Release reset)
  output_low(ADS8689_RST);
  delay_ms(2);
  output_high(ADS8689_RST);
  delay_ms(10);
 
  PORT_C_PULLUPS(0xC0);  // Enable pull-ups for RC6 (RX) and RC7 (TX)
}

// System initialization
void MCU_Init(void)
{
   PORT_Init();
   UART_Init();
   
   // Use internal clock, divide by 2, use 8-bit timer
   // SET_TIMER0() will be used to maintain 1ms timing
   SETUP_TIMER_0(RTCC_INTERNAL | RTCC_DIV_2 | RTCC_8_BIT);
   ENABLE_INTERRUPTS(INT_TIMER0);         // Enable Timer0 interrupt
   ENABLE_INTERRUPTS(PERIPH | GLOBAL);    // Enable peripheral and global interrupts
}

// Modbus 0x0100, 0x0200 command branch handler
void DoCommand() {
   Modbus_Branch_Process();
}

void main()
{
   MCU_Init();                 // Initialize MCU and peripherals
   ADS8689_Init();             // Initialize ADS8689 ADC
   modbus_init_slave(1);       // Initialize Modbus slave with ID 1
   ADC_Coeff_Init();           // Initialize ADC coefficient (e.g. gain, offset)

   while (TRUE)
   {
      ADC_Scan();              // Perform ADC scan
      DoCommand();             // Handle Modbus commands

//!      uint16 reg = ADS8689_ReadRegister(0x14);
//!      printf("REG[0x14] = 0x%04X\r\n", reg);
//!      delay_ms(250);
   }
}



ads8689.h
Code:

#define CH_MAX             8 // Number of channels

// ADC to uA
// #define ADC_to_uA       0.0003125   // (5.120V / 65536 resolution / 250Ω)
#define ADC_to_uA         0.3125       // Simplified scale factor (5.120V / 65536 / 250Ω)

// uA to ADC
#define uA_to_ADC         3.2768       // 65536 max ADC value / 20000 uA (20mA max current)

uint16 ref_buff[2][CH_MAX];    // Calibration reference values: stores expected ADC values for 4mA and 20mA
uint16 meas_buff[2][CH_MAX];   // Calibration measured values: actual ADC readings for 4mA and 20mA

uint16 FBuff_mA[CH_MAX];       // Calibrated current values in uA (for Modbus transmission)
uint16 FBuff_ADC[CH_MAX];      // Converted 0–65535 ADC scale values (Modbus-compatible)

float  Coeff_uA[CH_MAX];       // Calibration gain coefficients
float  Offset_uA[CH_MAX];      // Calibration offsets

/////////////////////////////// test ///////////////////////////////////////////

uint16 ADS8689_ReadRegister(uint8 reg_addr)
{
   uint8 cmd1, cmd2;
   uint8 rx[4];
   uint16 reg_value;

   // Frame 1: Send READ command
   cmd1 = 0xC0 | ((reg_addr & 0x3F) >> 1);   // READ + high bits of address
   cmd2 = (reg_addr << 7);                  // low bits of address

   output_low(ADS8689_CS);
   spi_write(cmd1);
   spi_write(cmd2);
   spi_write(0x00);
   spi_write(0x00);
   output_high(ADS8689_CS);
   delay_us(2); // Wait for device response

   // Frame 2: Dummy write
   output_low(ADS8689_CS);
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   output_high(ADS8689_CS);
   delay_us(2);

   // Frame 3: Read actual response
   output_low(ADS8689_CS);
   rx[0] = spi_read(0x00);
   rx[1] = spi_read(0x00);
   rx[2] = spi_read(0x00);
   rx[3] = spi_read(0x00);
   output_high(ADS8689_CS);

   reg_value = ((uint16)rx[0] << 8) | rx[1];  // First 2 bytes are valid data
   return reg_value;
}

void ADS8689_Reset(void)
{
   // Toggle RST pin: LOW → delay → HIGH
   output_low(ADS8689_RST);   // Begin ADS8689 reset
   delay_ms(2);               // Minimum delay 1ms (per TI datasheet)
   output_high(ADS8689_RST);  // Release reset
   delay_ms(10);              // Wait for stabilization (a few µs minimum, safely using 10ms)
}

// MUX channel select function (0~7)
void MUX36S08_Select(uint8 ch)
{
   output_bit(MUX_A0, bit_test(ch, 0)); // Set A0
   output_bit(MUX_A1, bit_test(ch, 1)); // Set A1
   output_bit(MUX_A2, bit_test(ch, 2)); // Set A2
}

uint16 ADS8689_ReadADC(void)
{
   uint8 rx[4];
   uint32 value;

   // 1. Frame 1: Send dummy READ command (starts conversion)
   output_low(ADS8689_CS);
   spi_write(0xC0);     // READ command
   spi_write(0x00);     // address (0x00 or ignored)
   spi_write(0x00);     // dummy
   spi_write(0x00);     // dummy
   output_high(ADS8689_CS);
   delay_us(2);         // Short delay

   // 2. Frame 2: Receive 4-byte data
   output_low(ADS8689_CS);
   rx[0] = spi_read(0x00);
   rx[1] = spi_read(0x00);
   rx[2] = spi_read(0x00);
   rx[3] = spi_read(0x00);
   output_high(ADS8689_CS);

   // Return top 16 bits
   value  = ((uint32)rx[0] << 24);
   value |= ((uint32)rx[1] << 16);
   value |= ((uint32)rx[2] << 8);
   value |= ((uint32)rx[3]);

   return (uint16)(value >> 8);  // Equivalent to ((uint16)rx[1] << 8) | rx[2]
}

void ADS8689_WriteRegister(uint8 reg_addr, uint16 value)
{
   uint8 cmd1, cmd2, data_h, data_l;
   uint8 dummy;

   cmd1 = 0xD0 | ((reg_addr & 0x3F) >> 1);  // WRITE HWORD command
   cmd2 = (reg_addr << 7);
   data_h = (uint8)(value >> 8);
   data_l = (uint8)(value & 0xFF);

   output_low(ADS8689_CS); delay_us(1);
   spi_write(cmd1);
   spi_write(cmd2);
   spi_write(data_h);
   spi_write(data_l);
   delay_us(1);
   output_high(ADS8689_CS);
   delay_us(2);

   // Dummy Read Frame
   output_low(ADS8689_CS); delay_us(1);
   dummy = spi_read(0x00);
   dummy = spi_read(0x00);
   dummy = spi_read(0x00);
   dummy = spi_read(0x00);
   delay_us(1);
   output_high(ADS8689_CS);
   delay_us(2);
}

void ADC_Coeff_Init()
{
   uint16 ch; // Channel index (0–7)
   uint16 ref0, ref1, meas0, meas1;
   float coeff, offset;

   for(ch = 0; ch < CH_MAX; ch++)
   {
      ref0 = ref_buff[0][ch];
      ref1 = ref_buff[1][ch];
      meas0 = meas_buff[0][ch];
      meas1 = meas_buff[1][ch];

      Coeff_uA[ch] = 1.0;
      Offset_uA[ch] = 0.0;

      if((ref1 > ref0) && (meas1 > meas0))
      {
         coeff = (float)(ref1 - ref0) / (float)(meas1 - meas0);
         offset = (float)ref0 - (float)meas0 * coeff;

         Coeff_uA[ch] = coeff;
         Offset_uA[ch] = offset;
      }
   }
}

void ADS8689_Init(void)
{
   MUX36S08_Select(0);
   delay_ms(10);  // Wait for power stabilization (important)

   // 1. Dummy Frame (NOP)
   output_low(ADS8689_CS);
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   spi_write(0x00);
   output_high(ADS8689_CS);
   delay_ms(2);

   // Write registers using exact 16-bit format
   ADS8689_WriteRegister(0x10, 0x7D08);  // DATAOUT_CTL_REG
   delay_ms(2);
   ADS8689_WriteRegister(0x14, 0x000B);  // RANGE_SEL_REG (0–5.12V range)
   delay_ms(2);
   
   uint16 reg = ADS8689_ReadRegister(0x14);  // Confirm written value
   printf("REG[0x14] = 0x%04X\r\n", reg);    // Print to terminal
}

void Convert_ADC_to_mA(uint8 ch, uint16 adc_val)
{
   float uA = 0, val = 0;

   if (adc_val < 8000)  // Disconnected or invalid input
      uA = 0;
   else
   {
      val = (float)adc_val * ADC_to_uA;
      uA = val * Coeff_uA[ch] + Offset_uA[ch];
      // uA = (float)adc_val * COEF_ADC2uA_512V;  // Alternative without calibration
   }

   FBuff_mA[ch] = (uint16)uA;

   val = uA * uA_to_ADC;
   if (val > 65535) val = 65535;
   FBuff_ADC[ch] = (uint16)val;
}

void ADC_Scan(void)
{
   static uint16 prev_ms = 0;
   static uint8 ch = 0;
   static uint8 scan_count = 0;  // Track full scan cycles

   if(Sys_ms - prev_ms < 125) return;  // Sample each channel every 125ms
   prev_ms = Sys_ms;

   MUX36S08_Select(ch);
   delay_ms(2);
   ADS8689_ReadADC();  // Dummy read for conversion delay
   delay_ms(2);

   uint16 adc_val = ADS8689_ReadADC();
   Convert_ADC_to_mA(ch, adc_val);

   ch++;
   if (ch >= CH_MAX) {
      ch = 0;
      scan_count++;

      if (scan_count >= 5) {       // After 5 full scans
         scan_count = 0;

         // Blink LED6
         output_low(PIN_C1);  // LED ON
         delay_ms(10);
         output_high(PIN_C1); // LED OFF
      }
   }

   delay_ms(10);
}
songdaegon



Joined: 17 Jun 2025
Posts: 8

View user's profile Send private message Send e-mail

Regarding the SPI hardware:
PostPosted: Wed Jul 16, 2025 6:52 pm     Reply with quote

The pin configuration and wiring have all been checked.
Ttelmah



Joined: 11 Mar 2010
Posts: 19928

View user's profile Send private message

PostPosted: Thu Jul 17, 2025 5:41 am     Reply with quote

First, you should raise CS during the INIT. Otherwise it may not be high
on the first command.
However the big problem is your bytes being sent are wrong.
You are oring 7 bits of the register address withe the first byte sent, then
just sending the bottom bit of this as the top bit of the second byte.
Pretty much 100% wrong!.....
The write expects as it's first byte 0xD0, D2 or D4, with just the top bit of
a 9 bit address put into the bottom bit.
In fact it uses an 8 bit address, since the top bit is always zero.
So Your cmd1, for 16 bit write data is just 0xD0. Your OR there is creating
an invalid command, for anything but 0 as the register address. Wrong.
Then the reg_addr, does not want to be rotated to generate cmd1. That
if throwing away the top seven bits of the address, and just leaving the
low bit as the top bit in the address written. Again wrong.
cmd1, just wants the register address. No rotation involved at all.....
On your rotations for byte combination, just use make32. Faster, and
easier.
Then your data you send to the data out_ctl register is wrong. You
send 7D04, this would tell the converter to output all zeros. Why?.
100 for the low three bits.

Sad
songdaegon



Joined: 17 Jun 2025
Posts: 8

View user's profile Send private message Send e-mail

PostPosted: Sun Jul 20, 2025 9:30 pm     Reply with quote

I'll try applying it and provide feedback.
Thank you.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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