trying to read light sensor using i2c & bcm2835 on RPI - TSL2561 - c

My brother and I have been trying to get this working for days now and we just can't figure out what we are doing wrong, we could really use some help please!
What we're trying to accomplish is reading in data from a light sensor on an RPI expansion board through our own program written in C, using the BCM2835 library.
This is the light sensor we are using: TSL2561
https://cdn-shop.adafruit.com/datasheets/TSL2561.pdf
We are using a raspberry pi B+ model with Raspbian installed on it (through noobs).
This is the C library we are using:
http://www.airspayce.com/mikem/bcm2835/
I activated I2c through the raspian configuration.
and I detected the light sensor through i2ctools and it shows up correctly with adress 0x29.
We get 0 values back with our reads, and we tested our commands with an osciloscope and it seems he does ACK the write commands. he just doesn't send anything back...
Can someone please help us ?
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint16_t clk_div = BCM2835_I2C_CLOCK_DIVIDER_148;
uint8_t slave_address = 0x29; //0101001 - this is the sensor address
uint64_t delay = 70000;
int main(int argc, char **argv)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
char buffer[10]={0};
char addr;
uint16_t data;
uint8_t data2;
int i =0;
char writeBuff[1] = {0x8C}; ////Address the Ch0 lower data register
char readBuff[10];
char writeBuff2[2] = {0x8D}; ////Address the Ch0 upper data register
char readBuff2[10];
char *wb_ptr,*r_ptr,*wb_ptr2,*r_ptr2;
wb_ptr = writeBuff;
wb_ptr2 = writeBuff2;
r_ptr = readBuff;
r_ptr2 = readBuff2;
printf("Running ... \n");
bcm2835_init():
bcm2835_i2c_begin();
bcm2835_i2c_setSlaveAddress(slave_address); //0x29
printf("Clock divider set to: %d\n", clk_div);
printf("Slave address set to: %d or %X\n",slave_address,slave_address);
//needed according to datasheet to read although unsure if it needs to be sent in two writes or in one ? 0x83 instead of 0x80 + 0x03 ?
bcm2835_i2c_write(0x80, 1); //command register
bcm2835_i2c_write(0x03, 1); //command itself
bcm2835_delayMicroseconds(delay);
//--------------------------
while (1)
{
printf("reading data from light sensor\n");
bcm2835_i2c_write(wb_ptr, 1); // 0x8C
bcm2835_delayMicroseconds(delay);
data = bcm2835_i2c_read(readBuff,1);
bcm2835_delayMicroseconds(delay);
printf("Read Result 1 = %d\n", data);
bcm2835_i2c_write(wb_ptr2, 1); //0x8D
bcm2835_delayMicroseconds(delay);
data2 = bcm2835_i2c_read(readBuff2,1);
bcm2835_delayMicroseconds(delay);
printf("Read Result 2 = %d\n", data);
bcm2835_delay(1000);
}
bcm2835_i2c_end();
bcm2835_close();
printf("... done\n");
return 0;
}
This is a Quick edit
#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint16_t clk_div = BCM2835_I2C_CLOCK_DIVIDER_148;
uint8_t slave_address = 0x29; //0101001 - this is the sensor address
uint64_t delay = 70000;
int main(int argc, char **argv)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
/*
enum bcm2835I2CReasonCodes { BCM2835_I2C_REASON_OK = 0x00, BCM2835_I2C_REASON_ERROR_NACK = 0x01, BCM2835_I2C_REASON_ERROR_CLKT = 0x02, BCM2835_I2C_REASON_ERROR_DATA = 0x04 }
*/
char buffer[10]={0};
char addr;
uint16_t data;
uint8_t data2;
uint8_t error = 0xff;
int i =0;
char writeBuff[1] = {0x8C}; ////Address the Ch0 lower data register
char readBuff[10];
char writeBuff2[2] = {0x8D}; ////Address the Ch0 upper data register
char readBuff2[10];
char writeBuff3[2] = {0x80};
char writeBuff4[2] = {0x03};
char *wb_ptr,*r_ptr,*wb_ptr2,*r_ptr2,*r_ptr3,*r_ptr4;
wb_ptr = writeBuff;
wb_ptr2 = writeBuff2;
r_ptr = readBuff;
r_ptr2 = readBuff2;
r_ptr3 = writeBuff3;
r_ptr4 = writeBuff4;
printf("Running ... \n");
bcm2835_init();
bcm2835_i2c_begin();
bcm2835_i2c_setSlaveAddress(slave_address); //0x29
printf("Clock divider set to: %d\n", clk_div);
printf("Slave address set to: %d or %X\n",slave_address,slave_address);
//needed according to datasheet to read although unsure if it needs to be sent in two writes or in one ? 0x83 instead of 0x80 + 0x03 ?
bcm2835_i2c_write(r_ptr3, sizeof(r_ptr3)); //command register
bcm2835_i2c_write(r_ptr4, sizeof(r_ptr4)); //command itself
bcm2835_delayMicroseconds(delay);
//--------------------------
// Blink
while (1)
{
printf("reading data from light sensor\n");
error = bcm2835_i2c_write(wb_ptr, sizeof(wb_ptr)); // 0x8C
bcm2835_delayMicroseconds(delay);
data = bcm2835_i2c_read(readBuff,sizeof(readBuff));
bcm2835_delayMicroseconds(delay);
printf("readbuff1 = 0x%02X \n",readBuff);
printf("error result = 0x%02X\n", error);
printf("Read Result 1 = 0x%02X\n", data);
error = bcm2835_i2c_write(wb_ptr2, sizeof(wb_ptr2)); //0x8D
bcm2835_delayMicroseconds(delay);
data2 = bcm2835_i2c_read(readBuff2,sizeof(readBuff2));
bcm2835_delayMicroseconds(delay);
printf("readbuff2 = 0x%02X \n",readBuff2);
printf("error result = 0x%02X\n", error);
printf("Read Result 2 = 0x%02X\n", data2);
bcm2835_delay(1000);
}
bcm2835_i2c_end();
bcm2835_close();
printf("... done\n");
return 0;
}

I managed to fix it, I had several problems , one of which was that I was adressing the wrong adress to send commands, it has to be 0xa0 instead of 0x80.
#include <bcm2835.h>
#include <stdio.h>
int main(void)
{
/*
DATA0LOW Ch 7:0 ADC channel 0 lower byte
DATA0HIGH Dh 7:0 ADC channel 0 upper byte
*/
/*
enum bcm2835I2CReasonCodes { BCM2835_I2C_REASON_OK = 0x00, BCM2835_I2C_REASON_ERROR_NACK = 0x01, BCM2835_I2C_REASON_ERROR_CLKT = 0x02, BCM2835_I2C_REASON_ERROR_DATA = 0x04 }
*/
if (!bcm2835_init())
return 1;
char uitgelezenTempWaarde[1];
int totalTemp[2];
int error =0;
bcm2835_i2c_begin(); // start i2c
bcm2835_i2c_setSlaveAddress(0x29); // slave address
bcm2835_i2c_set_baudrate(1000); // default
//----------- turn channels on for measurement ------
uitgelezenTempWaarde[0] = 0xa0;
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
uitgelezenTempWaarde[0] = 0x03;
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
bcm2835_delay(500);
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
//----------- read data ------
uitgelezenTempWaarde[0] = 0xac; //DATA0LOW Ch 7:0 ADC channel 0 lower byte
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
totalTemp[1]= (int)uitgelezenTempWaarde[0];
uitgelezenTempWaarde[0] = 0xad; //DATA0HIGH Dh 7:0 ADC channel 0 upper byte
error = bcm2835_i2c_write(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
error = bcm2835_i2c_read(uitgelezenTempWaarde,1);
if(error != 0x00)
{
printf("i2c error! : 0x%02X \n",error);
}
totalTemp[0] = (int)uitgelezenTempWaarde[0]; //
totalTemp[0] *= 256; //hex conversion for the highest byte so it is seen as a high number (16 bits)
printf("The light value is :%d\n",totalTemp[0]+totalTemp[1]);
bcm2835_i2c_end();
bcm2835_close();
return 0;
}

Related

not able retrieving data from Character in C but the data is there

I am using a i2c sensor on my basys mx3 board for measuring distance.
I've found a demo of this exact sensor with a library for this board and it works as it should.
There are 8 leds on the board adresses with LATA, these represent the distance in centimeters to the object it is pointing at.
The goal is to retrieve the data in centimeters and put it in any accessible variable to print it over UART.
I expected to be able to retrieve the data before it was send to the leds but this seemed beyond my knowledge unfortunatly.
i've tried:
-printing the result that goes into the lata directly to uart(result)
-printing the exact same code that goes to the lata directly to uart (result[1])
-converting the char to an int and then later on print the value of the integer
-copying the data from the result to another char using memcopy (memcpy(distance[1], result[1], 20);) and then printing distance or distance[1] to the uart cmd line.
-retrieving the size of the data and printing it in a char variable sprintf(distance,"%d",sizeof(result[1]));
I do not know what other options i have maybe i am not looking at it the right way.
the code for the I2C library that i am using:
unsigned char I2C_Read(unsigned char slaveAddress,
unsigned char* dataBuffer,
unsigned char bytesNumber)
{
unsigned char status = 0;
unsigned char acknowledge = 0;
unsigned char byte = 0;
int cnt_timeout;
I2C1CONbits.RSEN = 1; // Initiate a start condition
cnt_timeout = 0;
while(I2C1CONbits.RSEN && (++cnt_timeout < I2C_WAIT_TIMEOUT)); //Wait for start condition to complete
if(cnt_timeout >= I2C_WAIT_TIMEOUT)
{
return 0xFE; // timeout error
}
I2C1TRN = (slaveAddress << 1) + 1;
cnt_timeout = 0;
while(I2C1STATbits.TRSTAT && (++cnt_timeout < I2C_WAIT_TIMEOUT)); // Wait for reception to complete
if(cnt_timeout >= I2C_WAIT_TIMEOUT)
{
return 0xFE; // timeout error
}
acknowledge = I2C1STATbits.ACKSTAT;
if(acknowledge == 0) //Acknowledge was received
{
for(byte = 0; byte < bytesNumber; byte++)
{
I2C1CONbits.RCEN = 1; //Enable receive mode for I2C
if(byte == (bytesNumber - 1))
{
I2C1CONbits.ACKDT = 1;
}
else
{
I2C1CONbits.ACKDT = 0;
}
cnt_timeout = 0;
while(I2C1CONbits.RCEN && (++cnt_timeout < I2C_WAIT_TIMEOUT)); //Wait for reception to complete
if(cnt_timeout >= I2C_WAIT_TIMEOUT)
{
return 0xFE; // timeout error
}
dataBuffer[byte] = I2C1RCV;
I2C1CONbits.ACKEN = 1;
cnt_timeout = 0;
while(I2C1CONbits.ACKEN && (++cnt_timeout < I2C_WAIT_TIMEOUT));
if(cnt_timeout >= I2C_WAIT_TIMEOUT)
{
return 0xFE; // timeout error
}
}
}
else
{
status = 0xFF;
}
I2C1CONbits.ACKEN = 1; //Initiate Acknowledge sequence on SDAx and SCLx pins and transmit ACKDT data bit. Wait for Acknowledge sequence to complete
I2C1CONbits.PEN = 1; //Initiate a stop condition
cnt_timeout = 0;
while(I2C1CONbits.PEN && (++cnt_timeout < I2C_WAIT_TIMEOUT)); //Wait for stop condition to complete
if(cnt_timeout >= I2C_WAIT_TIMEOUT)
{
return 0xFE; // timeout error
}
return status;
}
the code i am using to read the sensor where LATA receives the right distance but any other variable isn't:
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <sys/attribs.h>
#include "config.h"
#include "pragma.h"
#include "i2c.h"
#define ADDRESS 0x70
int main (void)
{
TRISA = 0x0;
LATA = 0xF;
I2C_Init(100000);
delay();
char tx_buff[2] = {0x00, 0x51};
char reg_add[1] = {0x02};
char result[2] = {};
while(1)
{
I2C_Write(ADDRESS, tx_buff, 2, 0); // beginnen met meten
delay();// minimaal 65 ms
I2C_Write(ADDRESS, reg_add, 1, 0);
I2C_Read(ADDRESS, result, 2);
delay();
LATA = result[1];
UART_PutString("measured distance is ");
UART_PutString(result);
UART_PutString("cm\n");
}
}
void delay ()
{
_CP0_SET_COUNT(0);
while (_CP0_GET_COUNT() < 4000000)
{
}
}

Segmentation Failure with print and write instructions, in Implementation between Fortran and C

I'm trying to call a C function from Fortran, using the iso_c_binding interoperability. However, I am getting a SegFault error when trying to use print and write statements. Without the print and write statements the code works fine, but I need these statements to create an output file with the simulation data. Does anyone know how to solve this problem?
Note: I am using Ubuntu 20.04, GFortran, and GCC to compile the respective source codes.
gcc -c subroutine_in_c.c
gfortran -o exec main.f90 subroutine_in_c.o -lwiringPi
main.f90:
PROGRAM main
USE, INTRINSIC:: iso_c_binding, ONLY: C_FLOAT
IMPLICIT NONE
REAL(KIND = 4) :: leitura_sensor = 0.0
INTERFACE
SUBROUTINE ler_sensores(s1) BIND(C)
USE, INTRINSIC :: iso_c_binding, ONLY: C_FLOAT
IMPLICIT NONE
REAL(KIND=C_FLOAT) :: s1
END SUBROUTINE ler_sensores
END INTERFACE
!print*, 'Call subroutine in C language'
call ler_sensores(leitura_sensor)
!print*, 'Return to main.f90'
OPEN(UNIT=1, FILE='output.txt', STATUS='unknown')
WRITE(1,*) leitura_sensor
CLOSE(UNIT=1)
END PROGRAM main
subroutine_in_c.c:
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <wiringPiSPI.h>
#include <wiringPiI2C.h>
#define LCDADDR 0x27 //IIC LCD address
#define BLEN 1 //1--open backlight,0--close backlight
#define CHAN_CONFIG_SINGLE 8 //setup channel 0 as Single-ended input
#define SPICHANNEL 0 //MCP3008 connect to SPI0
#define ANALOGCHANNEL 0 //Potentiometer connect MCP3008 analog channel 0
#define ANALOGCHANNEL2 1
static int spifd;
static int i2cfd;
void
spiSetup (int spiChannel)
{
if ((spifd = wiringPiSPISetup (spiChannel, 10000)) < 0)
{
fprintf (stderr, "Can't open the SPI bus: %s\n", strerror (errno)) ;
exit (EXIT_FAILURE) ;
}
}
int
myAnalogRead(int spiChannel,int channelConfig,int analogChannel)
{
if (analogChannel<0 || analogChannel>7)
return -1;
unsigned char buffer[3] = {1}; // start bit
buffer[1] = (channelConfig+analogChannel) << 4;
wiringPiSPIDataRW(spiChannel, buffer, 3);
return ( (buffer[1] & 3 ) << 8 ) + buffer[2]; // get last 10 bits
}
//write a word to lcd
void
write_word(int data)
{
int temp = data;
if ( BLEN == 1 )
temp |= 0x08;
else
temp &= 0xF7;
wiringPiI2CWrite(i2cfd, temp);
}
//send command to lcd
void
send_command(int comm)
{
int buf;
// Send bit7-4 firstly
buf = comm & 0xF0;
buf |= 0x04; // RS = 0, RW = 0, EN = 1
write_word(buf);
delay(2);
buf &= 0xFB; // Make EN = 0
write_word(buf);
// Send bit3-0 secondly
buf = (comm & 0x0F) << 4;
buf |= 0x04; // RS = 0, RW = 0, EN = 1
write_word(buf);
delay(2);
buf &= 0xFB; // Make EN = 0
write_word(buf);
}
//send data to lcd
void
send_data(int data)
{
int buf;
// Send bit7-4 firstly
buf = data & 0xF0;
buf |= 0x05; // RS = 1, RW = 0, EN = 1
write_word(buf);
delay(2);
buf &= 0xFB; // Make EN = 0
write_word(buf);
// Send bit3-0 secondly
buf = (data & 0x0F) << 4;
buf |= 0x05; // RS = 1, RW = 0, EN = 1
write_word(buf);
delay(2);
buf &= 0xFB; // Make EN = 0
write_word(buf);
}
//initialize the lcd
void
init()
{
send_command(0x33); // Must initialize to 8-line mode at first
delay(5);
send_command(0x32); // Then initialize to 4-line mode
delay(5);
send_command(0x28); // 2 Lines & 5*7 dots
delay(5);
send_command(0x0C); // Enable display without cursor
delay(5);
send_command(0x01); // Clear Screen
wiringPiI2CWrite(i2cfd, 0x08);
}
//clear screen
void
clear()
{
send_command(0x01); //clear Screen
}
//Print the message on the lcd
void
write(int x, int y, char data[])
{
int addr, i;
int tmp;
if (x < 0) x = 0;
if (x > 15) x = 15;
if (y < 0) y = 0;
if (y > 1) y = 1;
// Move cursor
addr = 0x80 + 0x40 * y + x;
send_command(addr);
tmp = strlen(data);
for (i = 0; i < tmp; i++) {
send_data(data[i]);
}
}
void
ler_sensores(float *s1)
{
int adc;
int adc2;
int i;
float voltage;
float voltage2;
char buf[5];
if (wiringPiSetup() < 0)
{
fprintf(stderr,"Can't init wiringPi: %s\n",strerror(errno));
exit(EXIT_FAILURE);
}
spiSetup(SPICHANNEL);//init spi
i2cfd = wiringPiI2CSetup(LCDADDR); //init i2c
init(); //init LCD
clear(); //clear screen
for (i = 0; i <25; i++) {
adc = myAnalogRead(SPICHANNEL,CHAN_CONFIG_SINGLE,ANALOGCHANNEL);
adc2 = myAnalogRead(SPICHANNEL, CHAN_CONFIG_SINGLE, ANALOGCHANNEL2);
voltage = adc/1024.*20.0;
write(0,0,"Ch Linear:");
sprintf(buf,"%2.2f",voltage);//float change to string
write(10,0,buf);//print voltage on lcd
write(15,0,"V");//print unit
write(0,1,"Ch Logarit:");
voltage2 = adc2/1024.*20.0;
sprintf(buf,"%2.2f",voltage2);
write(11,1, buf);
write(16,1,"V");
delay(1000);
}
*s1 = voltage;
}
Thank you in advance to everyone who helps.
This question probably deserves an answer even if the reason for the problem is the obscure one, identified by Craig Estay.
Gfortran's runtime library, called when using the print and write statements, contains calls to write() and having another C function called write will cause the gfortran runtime to call a wrong function.
It can easily be tested in a simple program like this:
testwrite.c:
#include "stdio.h"
void write(){
puts("my C write");
}
testwrite.f90:
print *,"test print"
write(*,*) "test write"
end
When using gfortran testwrite.c testwrite.f90, the output is:
my C write
my C write
my C write
my C write
The same output appears when using icc testwrite.c -c -o c.o and ifort c.o testwrite.f90.

Issue in interfacing SPI e-ink display with PIC 18F46K22

I am using a PIC 18F46K22 in SPI master mode to communicate with a Waveshare 1.54" ePaper Module. The FOSC frequency is 8Mhz internal and SPI configuration is FOSC/4. So when I check the output on logic-analyzer some output bits are differ from expected. And there is some deviation in SCL.
#include <xc.h>
#include "config.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "main.h"
//#define _XTAL_FREQ 8000000
#define SPI1_DUMMY_DATA 0x0
#define SPI_RX_IN_PROGRESS 0x0
#define MY_BUFFER_SIZE 25
extern UBYTE EPD_Init(const unsigned char* lut);
unsigned char myWriteBuffer[100]="Hi I'm master..";
uint8_t myReadBuffer[100];
uint8_t total;
uint8_t temp;
uint8_t my_data = 0x58;
void UART_Init(void)
{
//69
SPBRG2 = 69;
TXSTA2bits.BRGH = 1;
BAUDCON2bits.BRG16 = 1; // Divisor at 8 bit
TRISDbits.TRISD6 = 0;
TRISDbits.TRISD7 = 1;
RCSTA2bits.SPEN = 1; // Enable serial port
TXSTA2bits.SYNC = 0; // Async operation
TXSTA2bits.TX9 = 0; // No tx of 9th bit
RCSTA2bits.RX9 = 0; // No rx of 9th bit
TXSTA2bits.TXEN = 1; // Enable transmitter
RCSTA2bits.CREN = 1; // Enable receiver
}
void UART_Putch(unsigned char bt)
{
while (!PIR3bits.TX2IF); // hold the program till TX buffer is free
TXREG2 = bt; //Load the transmitter buffer with the received value
}
void UART_Print(unsigned const char *ptr)
{
while (*ptr != 0)
{
UART_Putch(*ptr++);
}
}
unsigned char UART_getch() {
unsigned char temp;
if (RCSTA2bits.OERR) // check for Error
{
RCSTA2bits.CREN = 0; //If error -> Reset
//__delay_ms(10);
RCSTA2bits.CREN = 1; //If error -> Reset
}
while (!PIR3bits.RC2IF); // hold the program till RX buffer is free
temp = RCREG2;
return temp; //receive the value and send it to main function
}
void main()
{
ANSELA = 0;
ANSELB = 0;
ANSELC = 0;
ANSELD = 0;
TRISBbits.TRISB0 = 0; //RST Pin OUTPUT
TRISBbits.TRISB1 = 0; //DC Pin OUTPUT
TRISBbits.TRISB2 = 0; //CS Pin OUTPUT
TRISBbits.RB3 = 1; //BUSY Pin INPUT
// int i;
TRISD =0;/* PORT initialize as output */
EPD_RST_PIN = 0;
EPD_DC_PIN = 0;
//OSCCON = 0x72; /* Use internal osc. frequency 16 MHz */
OSCCONbits.SCS = 0b10; //Frequency & PLL SETUP
OSCCONbits.IRCF = 0b110; //8 MHz
while (!OSCCONbits.HFIOFS);
OSCTUNEbits.PLLEN = 0; //PLL disable
UART_Init();
SPI_Init_Master(); /* Initialize SPI communication as a master */
if(EPD_Init(lut_full_update) != 0) {
UART_Print("e-Paper init failed\r\n");
while(1);
}
UART_Print("e-Paper init\r\n");
for(uint8_t i = 0; i < 10; i++){
__delay_ms(10);
}
EPD_Clear();
UART_Print("e-Paper cleared\r\n");
for(uint8_t i = 0; i < 10; i++){
__delay_ms(50);
}
while(1)
{
// total = 0;
// //do
// //{
// LATAbits.LATA5=0;
// //total = SPI1_Exchange8bitBuffer(SPI1_DUMMY_DATA, MY_BUFFER_SIZE, &myReadBuffer[total]);
// total = SPI1_Exchange8bit(my_data);
//
// LATAbits.LATA5=1;
// __delay_ms(500);
// __delay_ms(500);
// // Do something else...
//
// //} while(total < MY_BUFFER_SIZE);
// //while(1);
//
// EPD_Clear();
//
// __delay_ms(500);
}
}
void SPI_Init_Master()
{
/* PORT definition for SPI pins*/
TRISCbits.TRISC4 = 1; /* RB0 as input(SDI) */
TRISCbits.TRISC3 = 0; /* RB1 as output(SCK) */
// TRISBbits.TRISB2 = 0; /* RA5 as a output(SS') */
TRISCbits.TRISC5 = 0; /* RC7 as output(SDO) */
/* To initialize SPI Communication configure following Register*/
EPD_CS_PIN = 1;
SSP1STAT=0x00; /* Data change on rising edge of clk , BF=0*/
SSP1CON1=0x20; /* Slave mode,Serial enable, idle state high for clk */
PIR1bits.SSP1IF=0;
/* Disable the ADC channel which are on for multiplexed pin
when used as an input */
ADCON0=0; /* This is for de-multiplexed the SCL
and SDI from analog pins*/
ADCON1=0x0F; /* This makes all pins as digital I/O */
}
uint8_t SPI1_Exchange8bit(uint8_t data)
{
// Clear the Write Collision flag, to allow writing
SSP1CON1bits.WCOL = 0;
SSP1BUF = data;
while(SSP1STATbits.BF == SPI_RX_IN_PROGRESS)
{
}
return (SSP1BUF);
}
uint8_t SPI1_Exchange8bitBuffer(uint8_t *dataIn, uint8_t bufLen, uint8_t *dataOut)
{
uint8_t bytesWritten = 0;
if(bufLen != 0)
{
if(dataIn != NULL)
{
while(bytesWritten < bufLen)
{
if(dataOut == NULL)
{
SPI1_Exchange8bit(dataIn[bytesWritten]);
}
else
{
dataOut[bytesWritten] = SPI1_Exchange8bit(dataIn[bytesWritten]);
}
bytesWritten++;
}
}
else
{
if(dataOut != NULL)
{
while(bytesWritten < bufLen )
{
temp = SPI1_Exchange8bit(SPI1_DUMMY_DATA);
if(temp!=SPI1_DUMMY_DATA)
{
UART_Putch(temp); //uart print
dataOut[bytesWritten] = temp;
bytesWritten++;
}
__delay_ms(5);
}
}
}
}
return bytesWritten;
}
Compare your logic analyser SCK and MOSI timing with that specified for the part at https://www.waveshare.com/wiki/1.54inch_e-Paper_Module:
Note that the MOSI (SDIN) state must be stable on the rising edge of SCK (SCLK). In your case the MOSI transitions are synchronous with the rising edge, and you have a clock transition before the MOSI has the correct D7=0 state. SPI timing is defined by both clock polarity and clock phase - giving four possible clock modes. Compare the Waveshare timing diagram with the 18F46K22 datasheet:
The Waveshare diagram suggests that either CKP=1/CKE=0, or CKP=0/CKE=1 may be used, you have:
SSP1STAT=0x00 ;
SSP1CON1=0x20 ;
Which is CKP=0/CKE=0 (which correlates with your logic analyser trace).
You need on of either:
SSP1STAT=0x20 ; // CKE=1
SSP1CON1=0x20 ; // CKP=0
or
SSP1STAT=0x00 ; // CKE=0
SSP1CON1=0x30 ; // CKP=1
Since idle state (controlled by CKP) of SCK is a don't-care, I suggest leaving that as-is and using the first suggestion - that seems more intuitive somehow.
Note also that your logic analyser must also be set to the same phase/polarity clock mode in order for its presentation of the data to be correct.

STM32 LwIP netconn_write problem transmitting structs

I am using LwIP stack with FreeRTOS in STM32F407Discovery board as TCP Client and I have a Linux computer as TCP Server. I faced a problem during transmission of a struct array, say struct EncoderData Encoder[2], through netconn API.
The thing is, when I print the elements of the struct array to Serial Terminal (with UART) the data stored in the struct array is correctly shown. However when I send the structs through netconn socket, the elements of the first struct, i.e. Encoder[0] is received correctly, whereas Encoder[1] is received as all zeroes.
Expected output in the Server side,
Encoder[0] ---> 0xAA 0x07 0x00 0x52 0x12 0xDC 0xAB 0xFA
Encoder[1] ---> 0xAA 0x07 0x01 0x52 0x42 0xBF 0xAA 0xFA
Resulting problematic output in the Server side
Encoder[0] ---> 0xAA 0x07 0x00 0x52 0x12 0xDC 0xAB 0xFA
Encoder[1] ---> 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Below, I am sharing both the server and the client side code, I would appreciate if you could help me out identifying the problem.
Server.c -- Linux Computer
#define MAX 9
#define PORT 8080
#define SA struct sockaddr
#define NUM_OF_ENCODERS 2
#define PREAMBLE 0xAA
#define LENGTH 0x07
#define CRC 0xFA
struct EncoderData {
volatile uint8_t encoderID;
volatile uint8_t byte0;
volatile uint8_t byte1;
volatile uint8_t byte2;
volatile uint8_t byte3;
volatile uint8_t byte4;
volatile uint32_t singleTurnValue;
volatile uint16_t multiTurnValue;
volatile uint16_t prevMultiTurnValue;
volatile uint8_t direction;
volatile float angle;
};
struct EncoderData Encoder[NUM_OF_ENCODERS];
// This function is called inside int main()
void func(int sockfd)
{
uint8_t buff[MAX];
uint32_t singleTurnValue = 0;
float angle = 0.0;
uint8_t channel;
uint8_t encoderID;
// infinite loop to read data continuously
for (;;) {
bzero(buff, MAX);
// read the message from client and copy it in buffer
read(sockfd, buff, sizeof(buff));
if(buff[0] == PREAMBLE && buff[1] == LENGTH && buff[8] == CRC)
{
encoderID = buff[2];
Encoder[encoderID].encoderID = encoderID;
Encoder[encoderID].byte0 = (uint8_t)buff[3];
Encoder[encoderID].byte1 = (uint8_t)buff[4];
Encoder[encoderID].byte2 = (uint8_t)buff[5];
Encoder[encoderID].byte3 = (uint8_t)buff[6];
Encoder[encoderID].byte4 = (uint8_t)buff[7];
Encoder[encoderID].singleTurnValue = (uint32_t)((buff[5] << 16)|(buff[6] << 8)|(buff[7]));
Encoder[encoderID].multiTurnValue = (uint16_t)((buff[3] << 8)|(buff[4]));
//printf("%d\n", encoderID);
bzero(buff, MAX);
}
// Here I print the struct elements, below printf shows data correctly
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\t\t",
Encoder[0].encoderID, Encoder[0].byte0, Encoder[0].byte1,
Encoder[0].byte2, Encoder[0].byte3, Encoder[0].byte4);
// However below printf results in only zeroes
printf("0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
Encoder[1].encoderID, Encoder[1].byte0, Encoder[1].byte1,
Encoder[1].byte2, Encoder[1].byte3, Encoder[1].byte4);
}
}
Client.c -- STM32F407Discovery
#define NUM_OF_ENCODERS 2
struct EncoderData Encoder[NUM_OF_ENCODERS];
void Encoder_Process(void)
{
//float angle = 0.0;
uint64_t encoderRawValue = 0;
uint32_t singleTurnValue = 0;
uint8_t channel = 0;
for(channel = 0; channel < NUM_OF_ENCODERS; channel++)
{
selectChannel(channel);
encoderRawValue = readEncoder();
singleTurnValue = getSingleTurn(encoderRawValue);
angle = calculateMotorAngle(singleTurnValue);
Encoder[channel].preamble = 0xAA;
Encoder[channel].length = 0x07;
Encoder[channel].encoderID = channel;
Encoder[channel].byte0 = (uint8_t)((encoderRawValue >> 32) & 0xFF);
Encoder[channel].byte1 = (uint8_t)((encoderRawValue >> 24) & 0xFF);
Encoder[channel].byte2 = (uint8_t)((encoderRawValue >> 16) & 0xFF);
Encoder[channel].byte3 = (uint8_t)((encoderRawValue >> 8) & 0xFF);
Encoder[channel].byte4 = (uint8_t)((encoderRawValue ) & 0xFF);
Encoder[channel].crc = 0xFA;
}
}
// Convert Encoder struct into buffer
uint8_t* prepareEncoderData(struct EncoderData enc)
{
static uint8_t buffer[9];
memset(buffer, '\0', 9);
buffer[0] = enc.preamble;
buffer[1] = enc.length;
buffer[2] = enc.encoderID;
buffer[3] = enc.byte0;
buffer[4] = enc.byte1;
buffer[5] = enc.byte2;
buffer[6] = enc.byte3;
buffer[7] = enc.byte4;
buffer[8] = enc.crc;
return buffer;
}
static void tcp_client_netconn_thread(void *arg)
{
struct netconn *conn; //, *newconn;
struct ip_addr remoteipaddr, localipaddr;
err_t err1, err2;
// portCHAR pagehits[100] = {0};
uint8_t channels;
uint8_t *transmitBuffer;
IP4_ADDR(&remoteipaddr, DEST_IP_ADDR0, DEST_IP_ADDR1, DEST_IP_ADDR2, DEST_IP_ADDR3);
IP4_ADDR(&localipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
/* Create a new TCP connection handle */
conn = netconn_new(NETCONN_TCP);
if (conn!= NULL)
{
err1 = netconn_bind(conn, &localipaddr, PORT_NUMBER);
err2 = netconn_connect(conn, &remoteipaddr, PORT_NUMBER);
if (err1 == ERR_OK && err2 == ERR_OK)
{
printf("STM32F4 connected to the server!\n");
while(1)
{
for(channels = 0; channels < NUM_OF_ENCODERS; channels++)
{
Encoder_Process();
transmitBuffer = prepareEncoderData(Encoder[channels]);
netconn_write(conn, transmitBuffer, strlen((char*)transmitBuffer), NETCONN_COPY);
}
vTaskDelay(10);
//netconn_delete(newconn);
}
}
else
{
printf("can not bind netconn");
printf("\terr code: %d", err1);
printf("\terr code: %d\n", err2);
}
}
else
{
printf("can not create netconn");
}
}

ARM: I2C address write waits idefenitly since I2C flag isn't set after receiving ACK

I am developing a bare-metal ARM project using LPC2138. I2C is configured for master transmitter mode. I have used the following code for writing data to the slave. I have chosen I2C polling method.
#define AA (0x04)
#define SI (0x08)
#define STO (0x10)
#define STA (0x20)
#define I2EN (0x40)
/* I2C0CONCLR bits */
#define AAC AA
#define SIC SI
#define STAC STA
#define I2ENC I2EN
void I2C0_init(unsigned char mode)
{
PINSEL0 = 0x50;
IO0DIR = 0x0C;
IO0SET = 0x0C;
I2C0CONCLR = AAC | SIC | STAC | I2ENC;
I2C0SCLL = 0x4B;
I2C0SCLH = 0x4B;
I2C0CONSET = I2EN;
__i2c0_mode = mode;
__i2c0_initialised = TRUE;
}
static bool I2C0_start()
{
I2C0CONSET = STA;
while(!(I2C0CONSET & SI));
if(I2C0STAT != 0x08)
{
return FALSE;
}
return TRUE;
}
static inline bool I2C0_slave_addr(unsigned char addr, bool rw)
{
addr = rw ? addr | BIT_MASK_RD : addr & BIT_MASK_WR;
I2C0DAT = addr;
I2C0CONCLR = SIC | STAC;
while(!(I2C0CONSET & SI)); // infinite loop here
I2C0CONCLR = SIC;
if(rw == I2C_WR && I2C0STAT != 0x18)
return FALSE;
else if(rw == I2C_RD && I2C0STAT != 0x40)
return FALSE;
return TRUE;
}
long I2C0_write(unsigned char addr, const char *buff, unsigned long buff_len)
{
unsigned long count = 0;
if(!__i2c0_initialised)
return I2C_ERROR_UNINITIALISED;
if(!buff_len)
return 0;
if(__i2c0_mode == I2C_MODE_MASTER)
{
I2C0_start();
I2C0_slave_addr(addr, I2C_WR);
..............................
..............................
}
}
I actually simulated the code using Proteus. I2C start condition and slave address are being sent properly and the address is successfully acknowledged by the slave.
After the slave acknowledgement, the SI flag should be set again automatically as per the LPC2138 reference manual. This is not happening and because of that, the code enters into an infinite loop.
I don't understand where I went wrong. Can someone please help?

Resources