Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
Greetins,
I recently bought a temperature & humidity sensor (aosong am2302). I hooked it to a rasp pi 3 and it works like charm with the adafruit library. The problem comes when i try to make it work on another board (described here). I used this library for gpio reading. I modified the file of beaglebone for gpio mapping and that's it. I run the tests and they work, so basically the lib looks like it works. So after that, I code the sensor reader and it doesnt work and I dont know why.
After I run the sensor reader, If I check the file system, the gpio is exported.
The sensor is installed like this:
-Power to 5V (also tried with 3.3v)
-VCC to gpio
-Ground to GND.
and here the code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/wait.h>
#include <sys/time.h>
#include "libsoc_gpio.h" //library for gpio reading
#define MAXTIMINGS 10
#define SILENT 0
int bits[MAXTIMINGS+1],data[5];
int readDHT(int pin,int allowRetry);
int
main(int argc, char **argv)
{ int dhtpin;
if (argc>1)dhtpin=atoi(argv[1]);
else printf("Introduce pin");
if( SILENT < 1 ) {
printf("Using pin #%d\n", dhtpin);
}
readDHT(dhtpin,5);
return 0;
}
int
readDHT(int pin, int allowRetry)
{
int bitidx=0;
int counter = 0;
int i=0,j=0;
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
gpio *gpio_output = libsoc_gpio_request(pin, LS_SHARED); //export GPIO
if (gpio_output == NULL)
{
fprintf(stderr, "Failed to open GPIO %d\n", pin);
return -1;
}
libsoc_gpio_set_direction(gpio_output, OUTPUT);
if (libsoc_gpio_get_direction(gpio_output) != OUTPUT)
{
fprintf(stderr, "Failed to set direction to OUTPUT\n");
if (gpio_output)
{
libsoc_gpio_free(gpio_output);
}
return -1;
}
libsoc_gpio_set_level(gpio_output, LOW);
libsoc_gpio_set_level(gpio_output, HIGH);
libsoc_gpio_set_direction(gpio_output, INPUT);
/* Wait for pin to drop */
while (libsoc_gpio_get_level(gpio_output) == HIGH)
{
if(counter++>10000){
printf("ERROR: Pin never dropped\n");
return 1;
}
}
if (i<= MAXTIMINGS)
{
counter =0;
while (libsoc_gpio_get_level(gpio_output) == LOW){
if(counter++ == 1000)
break;
}
counter =0;
while(libsoc_gpio_get_level(gpio_output) == HIGH){
if (counter++==1000)
break;
}
bits[bitidx++] = counter;
i++;
}
/* read data */
for (i = 1; i < bitidx; i++) {
data[j / 8] <<= 1;
if(bits[i]>200){
data[j/8] |= 1;
}
j++;
}
if( SILENT < 1 ) {
printf("Data (%d): 0x%x 0x%x 0x%x 0x%x 0x%x\n", j, data[0], data[1], data[2], data[3], data[4]);
}
if ((j >= 39) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) ) {
float f, h;
h = data[0] * 256 + data[1];
h /= 10;
f = (data[2] & 0x7F)* 256 + data[3];
f /= 10.0;
if (data[2] & 0x80) f *= -1;
printf("CTemp: %.1f\nFTemp: %.1f\nHum: %.1f%\n", f, ((f*9)/5)+32, h);
} else if( allowRetry > 0 ) {
sleep(1);
if( SILENT < 1 ) {
printf( "Error getting information. Retrying\n" );
}
return readDHT(pin, --allowRetry );
} else {
if( SILENT < 1 ) {
printf( "Error getting information. Retries exhausted.\n" );
}
return 1;
}
return 0;
if (gpio_output)
{
libsoc_gpio_free(gpio_output);
}
/* Check we got all the data and checksum matches */
}
With this code I get "Pin never dropped", so pin never goes to 0 so it doesnt report data. So i decided to try with bash and see if pin drops to 0. I coded the same as on the previous code but in bash and see the value of the pin (always 1, not dropping). Comming to this point, I run out of options, the sensor works (it's not broken), the library works but the sensor on this machine no. Any clue or idea on how to approach to find a solution?
Thanks :)
Some things to try that might get you going....
First increase the counter "counter++>10000" to something much larger, It could be that the new processor you are running increments the counter at a much faster rate and you just timeout before the pin drops.
If that doesn't work remove the timeout counter and loop forever in your source, also remove the sensor, then physically pull the data line to ground with a piece of wire and see if your code/new processor catches the signal change, at least then you know your source/hardware is configured correctly so you can focus your efforts elsewhere.
Double check the voltage compatibility between your new processor 'high' level and what the sensors 'high' threshold is to ensure it is catching a 'one' on the data line.
Lettus know how you go!
Tony
Related
#include <unistd.h> //Needed for I2C port
#include <fcntl.h> //Needed for I2C port
#include <sys/ioctl.h> //Needed for I2C port
#include <linux/i2c-dev.h> //Needed for I2C port
#include <stdio.h>
#include <time.h>
int file_i2c;
int length;
unsigned char buffer[60] = {0};
unsigned char cmdbuffer[60] = {0};
//PRINTS (SENSOR STATUS) IN BINARY
void printBin(unsigned char value)
{
for (int i = sizeof(char) * 7; i >= 0; i--) {
printf("%d", (value & (1 << i )) >> i);
}
putc('\n', stdout);
}
//CREATES DELAY IN MS
void delay(int milli)
{
long pause;
clock_t now,then;
pause = milli * (CLOCKS_PER_SEC / 1000);
now = then = clock();
while ((now-then) < pause) {
now = clock();
}
}
//TIMESTAMPS OUTPUT
void timestamp()
{
time_t ltime;
ltime=time(NULL);
printf("%s", asctime(localtime(<ime)));
}
//PORT SELECT FOR TCA9548A Addresses range from 0x70-0x77
void portSelect(int port, int addressTCA)
{
if (port > 7 || port < 0)
return;
if (ioctl(file_i2c, I2C_SLAVE, addressTCA) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
return;
}
cmdbuffer[0] = 1 << port;
length = 1;
write(file_i2c, cmdbuffer, length);
}
int main()
{
//----- OPEN THE I2C BUS -----
char *filename = (char*)"/dev/i2c-1";
if ((file_i2c = open(filename, O_RDWR)) < 0) {
//ERROR HANDLING: you can check errno to see what went wrong
printf("Failed to open the i2c bus");
return 0;
}
portSelect(1, 0x70);
//CONFIGURE SLAVE AND ATTEMPT CONNECTION
if (ioctl(file_i2c, I2C_SLAVE, 0x28) < 0) {
printf("Failed to acquire bus access and/or talk to slave.\n");
return 0;
}
flag:
cmdbuffer[0] = 0xAA;
cmdbuffer[1] = 0x00;
cmdbuffer[2] = 0x00;
length = 3;
if (write(file_i2c, cmdbuffer, length) != length)
//write() returns the number of bytes actually written, if it doesn't match then an error occurred (e.g. no response from the device)
{
printf("Failed to write to the i2c bus.\n"); //FAILS HERE
return 0;
}
length = 7; //<<< Number of bytes to read
if (read(file_i2c, buffer, length) != length)
//read() returns the number of bytes actually read, if it doesn't match then an error occurred (e.g. no response from the device)
{
printf("Failed to read from the i2c bus.\n");
} else {
//timestamp();
printf("Status:\n");
printBin(buffer[0]);
int pressure = (buffer[1] << 16) + (buffer[2] << 8) + buffer[3];
int temperature = (buffer[4] << 16) + (buffer[5] << 8) + buffer[6];
printf("Pressure : %d\n", pressure);
printf("Temperature : %d\n\n", temperature);
}
delay(5);
goto flag;
return 0;
}
Current code shown above. I am trying to read from 8 different Honeywell pressure sensors but for the moment I am just trying to get one working. I am able to read/write just fine without the multiplexer. Documentation is unhelpful as it only references Python or Arduino. I have scanned with console and confirmed the addresses of the sensors and mux.
I am trying to get a temperature readout from a LM77 using I2C communication with and FTDI FT232H. I don't think it is a hardware problem since I have checked the connections along with multiple colleagues. The communication with the PC to the FT232H is okay, it is initializing and everything is good on that end. The FT232H sends a read setup byte and gets an ACK from the LM77. After that there is no more data. I expect the LM77 to send 10 bits of data for a temperature readout, but it is not. This is what the readout looks like on a logic probe.
I would expect to then see an additional two bytes come in after the ACK but am getting nothing. The code is pretty straightforward and I am using the libMPSSE I2C API. The address I am using 0x48 comes from the address given in the datasheet bit shifted right by 1. I do not understand why I am getting an ACK but no temperature readout after. The ftStatus for the read gives an error code FT_DEVICE_NOT_FOUND. I am not sure why it's giving this error code if there is an ACK.
#include <stdio.h>
#include <stdlib.h>
#include "libMPSSE_i2c.h"
#define DEVICE_ADDR 0x48
FT_STATUS ftStatus;
FT_HANDLE ftHandle;
int i2cInit(void)
{
int numChannels = 0;
FT_DEVICE_LIST_INFO_NODE chanInfo;
ChannelConfig chConfig = {.ClockRate = 100000,
.LatencyTimer = 255,
.Options = 0x0000};
ftStatus = I2C_GetNumChannels(&numChannels);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Number of channels: %d\n", numChannels);
}
ftStatus = I2C_GetChannelInfo(0, &chanInfo);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel info obtained\n");
}
ftStatus = I2C_OpenChannel(0, &ftHandle);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel opened\n");
}
ftStatus = I2C_InitChannel(ftHandle, &chConfig);
if (ftStatus != FT_OK)
{
return -1;
}
else
{
printf("Channel initialized\n");
}
}
int lm77ReadTemp()
{
unsigned char writeBuffer[20] = {0};
unsigned char readBuffer[20] = {0};
unsigned char bytesTransferred = 0;
unsigned int bytesRead = 0;
ftStatus = I2C_DeviceRead(ftHandle, DEVICE_ADDR, 2, readBuffer, &bytesRead, I2C_TRANSFER_OPTIONS_START_BIT | I2C_TRANSFER_OPTIONS_STOP_BIT);
if (ftStatus != FT_OK)
{
printf("Read failed status code %d\n", ftStatus);
}
}
int main(void)
{
i2cInit();
lm77ReadTemp();
return 0;
}
The problem was on the hardware side. The MPSSE engine does not have bidirectional pins. On the FTDI chip side, you need a separate pin for SDA_out and SDA_in. There is a diagram in the FTDI documentation that shows it. If you don't have the pins connected like this you won't receive any data from the I2C slave.
I´m currently developing basic routines in order to communicate with an IMU or Inertial Measurement Unit, the ICM20948 from TDK - InvenSense using the I2C protocol through a PCB I have develop myself, I currently have functions to communicate with it like:
int writeRegister(uint8 slaveAdress, uint8 registerAdress, uint8 data);
uint8 readRegisters(uint8 slaveAdress, uint8_t subAddress, uint8 count, uint8 dest);
int selectAutoClockSource(uint8 slaveAdress)
And some more which are working properly (I have read registers who tell me this is working) the point is that the ICM20948 datasheet device doesn´t specifies me the order to configurate it in order to read the acceleration data, I have found a code on a GitHub, more concretely https://github.com/dtornqvist/icm-20948-arduino-library which is written in Arduino, I tried to port it to c, the language I'm currently using on it, getting the current configuration function
int begin(uint8 slaveAdress){
if (changeUserBank_force(slaveAdress, USER_BANK_0) < 0) {
return -1;
}
if (selectAutoClockSource(slaveAdress) < 0) {
return -2;
}
// enable I2C master mode
if(enableI2cMaster(slaveAdress) < 0){
return -3;
}
if (powerDownMag(slaveAdress) < 0) {
return -4;
}
reset(slaveAdress);
CyDelay(10);
if (selectAutoClockSource(slaveAdress) < 0) {
return -6;
}
whoAreU(slaveAdress);
if(enableAccelGyro(slaveAdress, UB0_PWR_MGMNT_2, UB0_PWR_MGMNT_2_SEN_ENABLE) == 1){
} else {
UART_DEBUG_PutString("\r\n Error al habilitar el giroscopio y acelerometro");
return -7;
}
if(configAccel(slaveAdress, ACCEL_RANGE_2G, ACCEL_DLPF_BANDWIDTH_246HZ) != 1){
return -8;
}
if (setAccelSrd(slaveAdress,0) < 0) {
return -12;
}
return 1;
}
After that I call my:
int16_t getAccelX_mss(uint8 slaveAdress){
uint8 _axcountsH, _axcountsL;
int16_t _axcounts;
if (changeUserBank_force(slaveAdress, USER_BANK_0) < 0) {
return -1;
}
_axcountsH = readRegisters(slaveAdress, 0x45u, 1, _axcountsH);
_axcountsL = readRegisters(slaveAdress, 0x46u, 1, _axcountsL);
_axcounts = (((int16_t)_axcountsH) << 8) | _axcountsL;
sprintf(Rx_aux, " 0x%d ", _axcounts);
UART_DEBUG_PutString("\r\n Acce Read: ");
UART_DEBUG_PutString(Rx_aux);
return 1;
}
Function, which is supposed to return me trough UART the mix of 45u and 46u (acceleration) registers info, but I'm receiving the next output (I'm using Hterm 0.8.3 to capture the uart output)
UART ok...<\r><\n>
Begin.<\r><\n>
Begin with no errors.<\r><\n>
Acce Read: 0x0
I have no real clue about, even when I move my pcb, generating an acceleration the result keeps as 0x0. Have anyone worked with this class of software and can give me some advice about how to keep working? Or I have any important error on my code that I haven't seen?
Program attached:
Those codes are written by me based on the TI uart.c driver library.
while(1){
//check if message on Queue -> read or check UART input
if(uxQueueMessagesWaiting( UART_TASKQ ) != 0){ //may have bugs
// deQueue
xQueueReceive( UART_TASKQ, &UARTTaskHandle, 0x0A );
//do the task's mission using the data in the stucture(put by control
task)
//Print out the input data.
//**********debugging data
/*
testPointer = UARTTaskHandle->dataBuffer;
testAmount = UARTTaskHandle->dataSize;
while(testAmount){
if(*testPointer != 1){
error = error + 1;
}
if(*(testPointer + 1) != 2){
error = error + 1;
}
if(*(testPointer + 2) != 3){
error = error + 1;
}
if(*(testPointer + 3) != 4){
error = error + 1;
}
if(*(testPointer + 4) != 5){
error = error + 1;
}
if(*(testPointer + 5) != 6){
error = error + 1;
}
if(*(testPointer + 6) != 7){
error = error + 1;
}
if(*(testPointer + 7) != 8){
error = error + 1;
}
testPointer = testPointer + 8;
testAmount = testAmount - 8;
}
*/
if(UART_write(UART_Handle, UARTTaskHandle->dataBuffer, UARTTaskHandle->dataSize, 0xff ) >= 0){
UARTwriteCount = UARTwriteCount + 1;
}
//let control task take new command
//free allocated memory
free(UARTTaskHandle->dataBuffer);
free(UARTTaskHandle); // free memory space
//(above is code using UART)
//here are UART driver code:
unsigned long UARTStatus(unsigned long ulBase){
ASSERT(UARTBaseValid(ulBase));
return(HWREG(ulBase + UART_O_FR));
}
//*****************************************
//UART_ISR
//Interrupt service routine for
//the UART read and write process
//*****************************************
void UART_ISR(){
//read FIFO full or read time out
if(UARTIntStatus(UART_Handle->UART_PORT,false) & (UART_INT_RX | UART_INT_RT)){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT); //clear INT flag
while (!(UARTStatus(UART_Handle->UART_PORT) & UART_FR_RXFE)){
//data reading
*UART_Handle->pCurrentRead = UARTCharGet(UART_Handle->UART_PORT); //read autoly clear INT
UART_Handle->pCurrentRead++;
UART_Handle->ReadLength--;
//adjust code here:
if(UART_Handle->ReadLength == 0){
break;
}
}
//check if read certain bytes finished
if(UART_Handle->ReadLength == 0){
memcpy(UART_Handle->dataput, UART_Handle->pReadBuf,UART_Handle->ReadLengthcpy); // copy data back
xSemaphoreGiveFromISR( UART_Handle->UARTRead_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
}
}
//send FIFO empty
if(UARTIntStatus(UART_Handle->UART_PORT,false) & UART_INT_TX){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX); //clear INT flag
if(UART_Handle->WriteLength == BUFFEMPTY){
UART_Handle->UART_SendComplete = true;
xSemaphoreGiveFromISR( UART_Handle->UARTWrite_Semaphore, &xHigherPriorityTaskWoken );// release semaphore
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );//forcing context exchange
}
//putting data into send FIFO
if(UART_Handle->WriteLength > FIFOMAX){
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}else{
templength = UART_Handle->WriteLength;
for( Cindex = 0; Cindex < templength ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}
}
}
//*****************************************
//UART_write
//write certain length of data to UART port
//*****************************************
int32_t UART_write( UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out ){
while(!UART_Handle->UART_SendComplete); //debugging purpose
UART_Handle->UART_SendComplete = false;//debugging purpose
UART_Handle->WriteLength = length;
if(UART_Handle->WriteLength <= UART_Handle->WriteBufSize){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_TX); //clear INT flag
memcpy(UART_Handle->pWriteBuf,pData,UART_Handle->WriteLength); //copy data into writebuff
UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
//putting data into send FIFO
if(UART_Handle->WriteLength > FIFOMAX){
// if
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}else{
for( Cindex = 0 ; Cindex < FIFOMAX ;){
if(UARTCharPutNonBlocking(UART_Handle->UART_PORT, *(UART_Handle->pCurrentWrite))){//write autoly clear INT
(UART_Handle->pCurrentWrite) = (UART_Handle->pCurrentWrite) + 1;
(UART_Handle->WriteLength) = (UART_Handle->WriteLength) - 1;
Cindex = Cindex + 1;
UART_Handle->sentCount = UART_Handle->sentCount + 1;
}
}
}
//start sending
UARTEnable(UART_Handle->UART_PORT);
if(UART_Handle->UARTWrite_Semaphore != NULL ) {
if(xSemaphoreTake(UART_Handle->UARTWrite_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
reValue = WRITESUCCESS; //wait return till write complete
}else{
reValue = WRITETIMEOUT; // timeout (ms)
}
}else{
while(1); //no Semaphore
}
return reValue;
}else{
return FAILURE; //wrong length
}
}
//*****************************************
//UART_read
//read certain length of data from UART port
//*****************************************
int32_t UART_read(UART_STATE *UART_Handle, uint8_t *pData, uint32_t length, uint32_t time_out){
//later added part
UARTDisable(UART_Handle->UART_PORT); //clearUART
UARTFIFOEnable(UART_Handle->UART_PORT);
//
UART_Handle->ReadLength = length; // set readlength
UART_Handle->ReadLengthcpy = length;
if(UART_Handle->ReadLength <= UART_Handle->ReadBufSize){
UARTIntClear(UART_Handle->UART_PORT, UART_INT_RX | UART_INT_RT); //clear INT flag
UART_Handle->dataput = pData; //store the destination buffer address
UART_Handle->pCurrentRead = UART_Handle->pReadBuf; //set current read
UARTEnable(UART_Handle->UART_PORT); //start receiving
//suspend before read ISR finish whole process
if(UART_Handle->UARTRead_Semaphore != NULL ) {
if(xSemaphoreTake(UART_Handle->UARTRead_Semaphore, time_out/(portTICK_PERIOD_MS) ) == pdTRUE){
reValue = READSUCCESS; //wait return till write complete
}else{
reValue = READTIMEOUT; // timeout (ms)
}
}else{
while(1); //no Semaphore
}
return reValue;
}else{
return FAILURE; //wrong length
}
}
//*****************************************
//UART_open
//open UART for certain port and bandrate
//*****************************************
UART_HANDLE UART_open(uint32_t UART_port, uint32_t UART_portperiph, uint32_t UART_baudrate){
//initialize structure
UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));
UART_Handle->ReadBufSize = UARTBUFFERSIZE;
UART_Handle->WriteBufSize = UARTBUFFERSIZE;
UART_Handle->UART_PORT = UART_port;
UART_Handle->UART_PORTPERIPH = UART_portperiph;
UART_Handle->UART_BRATE = UART_baudrate;
UART_Handle->pWriteBuf = (uint8_t*)malloc(UART_Handle->WriteBufSize * sizeof(uint8_t));
UART_Handle->pReadBuf = (uint8_t*)malloc(UART_Handle->ReadBufSize * sizeof(uint8_t));
UART_Handle->pCurrentWrite = UART_Handle->pWriteBuf;
UART_Handle->pCurrentRead = UART_Handle->pReadBuf;
UART_Handle->UARTWrite_Semaphore = NULL;
UART_Handle->UARTRead_Semaphore = NULL;
UART_Handle->UARTprotect_Semaphore = NULL;
UART_Handle->UART_SendComplete = true;
UART_Handle->sentCount = 0;//debugging purpose
vSemaphoreCreateBinary( UART_Handle->UARTWrite_Semaphore ); //semaphore create
vSemaphoreCreateBinary( UART_Handle->UARTRead_Semaphore ); //semaphore create
// vSemaphoreCreateBinary( UART_Handle->UARTprotect_Semaphore ); //debugging purpose
xSemaphoreTake( UART_Handle->UARTRead_Semaphore, portMAX_DELAY ); //semaphore take
xSemaphoreTake( UART_Handle->UARTWrite_Semaphore, portMAX_DELAY ); //semaphore take
// Enable Peripheral Clocks
MAP_PRCMPeripheralClkEnable(UART_Handle->UART_PORTPERIPH, PRCM_RUN_MODE_CLK);
// Configure PIN_55 for UART0 UART0_TX
MAP_PinTypeUART(PIN_55, PIN_MODE_3);
// Configure PIN_57 for UART0 UART0_RX
MAP_PinTypeUART(PIN_57, PIN_MODE_3);
// configuration, 8 bits length data width, 1 stop bit, no parity check
UARTConfigSetExpClk(UART_Handle->UART_PORT,PRCMPeripheralClockGet( UART_Handle->UART_PORTPERIPH),
UART_Handle->UART_BRATE, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
// disable UART since function above contained UARTenable
UARTDisable(UART_Handle->UART_PORT);
UARTIntEnable(UART_Handle->UART_PORT, UART_INT_TX | UART_INT_RX | UART_INT_RT); // enable interrupt for send and receive and receive timeout
UARTIntRegister(UART_Handle->UART_PORT, UART_ISR); //hook ISR
UARTFIFOEnable(UART_Handle->UART_PORT); //enable FIFO for send and receive
UARTFIFOLevelSet(UART_Handle->UART_PORT, UART_FIFO_TX1_8, UART_FIFO_RX4_8); //Interrupt occur when 7 bytes send from FIFO or read in FIFO
return UART_Handle;
}
I am dealing with a multi-thread bug for long time. After many test, I aware that the bug most possibly related to the UART FIFO transmitting.
Here is my description of my program:
The program is based on Free-RTOS. It has two threads running in parallel.
one thread is reading from sensor by I2C driver. another is sending data from the reading task to the computer by UART driver. I have a multi-task frame to transfer data from reading task to sending task. I have a timer file to control real time measuring. My I2C driver and UART driver are all interrupt based.
Problem description:
My data reading is good only for 2 mins. after that, my data shift. When do testing, I blocked the I2C driver, only put constant data "1 2 3 4 5 6 7 8" into the I2C read buffer, and transfer them to sending task to do UART send. And after 2 mins, my data read out will become "8 1 2 3 4 5 6 7" and after the first change, later changes happen quickly, "7 8 1 2 3 4 5 6" and continue till the end of timing.
this is plot of one channel of my data, begin with 8 and changing
I set many test to my program already, and I am sure my multi-task frame (trans data from reading task to sending task) don't alter the data.
Important observations:
1.when I put read and send into one thread, the data is very good, without this bug.
2.I set counting in my UART driver, found that the amount of sending is correct, here the sending is putting bytes into the TX FIFO. However, in the Excel, I read by a JAVA UART program, data missed. And JAVA UART program should be ok, since it works well when I use the single thread testing.
3. no memory leak error pump up by CCS while debugging.
So I am thinking, under the multi-thread environment, something stopped my UART TX FIFO transmit after I put bytes into that. But I can't find out where is wrong in my codes, they all seems correct.
And another reasonable reason can be something altered my pointer in the interrupt, in linked picture the "pCurrentWrite" pointer. But that pointer only accessed by UART driver, and before the interrupt finished, the next UART write can't enter.
I include partial code in the link, where I want to show that the data is put into the TX FIFO, and the send count is correct.
I can't include all codes, I don't think anyone want to check those long codes. and without running it, it's hard to find out where is wrong.
So, I just want to describe the situation i met, and see if someone had similar bug before. And maybe someone know the problem of UART FIFO under multi-thread environment. If someone who can help do want to see the code, we can do further contact.
thanks (My new account can't include picture yet, picture linked below)
This code is part of my UART driver, in the sending interrupt. I use the PutCharNonBlocking to put byte, since it return 1 if success, return 0 if FIFO full, so in this way I make sure the program will send all my data into the FIFO, which turn out to be true, all data was put into the TX FIFO
Although you have included the use of semaphores, I would suggest further reading on mutual exclusion and shared resources.
The solution is overly complex and it would be pertinent to better abstract your code and simplify.
Note: you should not cast the result of malloc.
UART_Handle = (UART_HANDLE)malloc(sizeof(UART_STATE));
I've been working on a very simple SDL strobe light program which flashes two different colors on the screen at a variable rate. The rate and the two colors are specified on the command line,
ie: ./strobe 13 0xffffff 0x000000. specifies a 13hz strobe rate, and white and black as color1 and color2 respectively. ./strobe -h displays the usage info and some other information. I compiled on Lubuntu 14.10 using gcc like this:
gcc strobe.c $(sdl-config --cflags --libs) -lSDL -std=c99 -pedantic -o strobe
I used a "delta time" technique to limit the framerate to the specified value by computing the time required to render the frame at the end of the loop, and calling SDL_Delay() if the frame finished "early".
The only problem I really have is that the frame rate seemed a bit jerky and inconsistent, and I noticed some garbage in the upper right hand corner of the screen (upper right hand corner of the window when not in fullscreen). The duty cycle between the two colors seemed to vary slightly as well sometimes and I could find no reason for this looking at the code.
Perhaps the most puzzling part (to me) was this: I added a call to printf() right before the call to SDL_Delay() in order to print the value of t (number of milliseconds spent on that frame), to see if it varied wildly. Much to my surprise, this simple thing fixed the problem, no more garbage in the top corner of the screen, and the framerate seemed much more consistent! The only problem is that I cannot figure out how in the world this makes any difference, and I do not want to fill the terminal with the current value of t on every frame. I am asking this question here in the hopes that someone more knowledgeable of SDL than I (i am just getting acquainted with SDL myself) can elucidate this, and provide some insight as to the mechanism of this strange anomaly in my code.
I will post the code below, in it's entirety. Forgive me if I should have omitted the more irrelevant bits, i was tempted to do so but feel that it may be better to provide a complete picture.
The last few lines at the end of the outer while loop in main is where i think the problem may be.
#include <SDL/SDL.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#define HRES 640
#define VRES 480
#define NCOLORS 2
#define MAXFPS 400
Uint16 create_hicolor_pixel(SDL_PixelFormat * fmt, Uint8 red, Uint8 green,
Uint8 blue)
{
Uint16 value;
/* This series of bit shifts uses the information from the SDL_Format
* structure to correctly compose a 16-bit pixel value from 8-bit RGB */
value = ((red >> fmt->Rloss) << fmt->Rshift) +
((green >> fmt->Gloss) << fmt->Gshift) +
((blue >> fmt->Bloss) << fmt->Bshift);
return value;
}
int getargs(int argc, char **argv, int *fps, Uint32 *rgb_color)
{
int error = 0;
/* Command line args */
if (argc < 4) {
fprintf(stderr, "Too few parameters.\n");
error = 1;
} else if (argc > 4) {
fprintf(stderr, "Too many parameters.\n");
error = 1;
} else {
char *endptr;
*fps = (int) strtol(*++argv, &endptr, 0);
if (endptr == *(argv - 1) || (*fps < 1 || *fps > MAXFPS)) {
fprintf(stderr, "Bad frequency value. Try values between 1 - %d\n",
MAXFPS);
error = 1;
}
rgb_color[0] = (Uint32) strtol(*++argv, &endptr, 16);
if ((endptr == *(argv - 1)) || (rgb_color[0] < 0) ||
(rgb_color[0] > 0xffffff)) {
fprintf(stderr, "Bad color value. Must be valid 24bit RGB hex\n");
error = 1;
}
rgb_color[1] = (Uint32) strtol(*++argv, &endptr, 16);
if ((endptr == *(argv - 1)) || (rgb_color[1] < 0) ||
(rgb_color[1] > 0xffffff)) {
fprintf(stderr, "Bad color value. Must be valid 24bit RGB hex\n");
error = 1;
}
}
return error;
}
void showusageinfo(char *prgname)
{
printf("\nUsage: %s [freq] [color1] [color2]\n\n", prgname);
printf("Strobe is a simple program that uses the users monitor to implement a\n"
"strobe light effect. The user can set the flash frequency and the two\n"
"colors to flash between by passing these to strobe as command line\n"
"arguments. Strobe uses the SDL library, and currently displays in\n"
"hi-color (16bit) mode. User entered color values are automatically\n"
"composed into the correct hi-color format for the user's system.\n\n");
printf("The first parameter is the strobe rate (frequency) in hertz or cycles\n"
"per second. This parameter takes a decimal value from 1 to MAXFPS.\n"
"Flash rates higher than your monitors refresh rate will probably not\n"
"display correctly. You may also be limited by the speed of your machine's"
"\n\nCPU and video card.\n");
printf("The second two parameters shall be 24bit RGB hexadecimal values for\n"
"color1 and color2 respectively such as 0xffffff for white, and\n"
"0xffff00 for yellow. As of version 0.1 common names of colors are\n"
"not supported.\n\n");
printf("An example for running strobe at a 15hz strobe rate, with yellow and\n"
"a turquois shade of blue as color1 and color2, respectively:\n\n"
"strobe 15 0xffff00 0x0080ff\n\n");
printf("Pressing the 'ESC' key, or the 'q' key while strobe is running will\n"
"safely exit from the program.\n\n");
}
int main(int argc, char **argv)
{
SDL_Surface *screen;
SDL_Event event;
Uint16 *scr_pixels;
Uint16 hicolor_color[NCOLORS];
Uint32 rgb_color[NCOLORS];
int coloridx = 0;
int x, y, i;
int quit = 0;
int fps, t;
if (argc > 1 && strcmp(argv[1], "-h") == 0) {
showusageinfo(*argv);
exit(EXIT_SUCCESS);
}
if (getargs(argc, argv, &fps, rgb_color) != 0) {
printf("Usage: %s [freq] [color1] [color2]\n"
" %s -h to see help info for this program.\n\n",
*argv, *argv);
exit(EXIT_FAILURE);
}
/* Init SDL */
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
return 1;
}
atexit(SDL_Quit);
(void) SDL_ShowCursor(SDL_DISABLE);
SDL_WM_SetCaption("Strobe", "Strobe");
screen = SDL_SetVideoMode(HRES, VRES, 16, SDL_HWSURFACE |
SDL_DOUBLEBUF |
0 );//no fullscreen until fixed
if (screen == NULL) {
fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
exit(EXIT_FAILURE);
}
/*Initialize hicolor colors*/
for (i = 0; i < NCOLORS; ++i) {
hicolor_color[i] = create_hicolor_pixel(screen->format,
(rgb_color[i] & 0xff0000) >> 16,
(rgb_color[i] & 0xff00) >> 8,
(rgb_color[i] & 0xff) >> 0);
}
/* Get a pointer to the video surface's memory. */
scr_pixels = (Uint16*) screen->pixels;
while (!quit) {
t = SDL_GetTicks();
if (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
quit = 1;
break;
case SDL_KEYDOWN:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_q:
quit = 1;
break;
}
break;
}
}
SDL_LockSurface(screen);
coloridx ^= 1;
for(x = 0; x < HRES; x++) {
for(y = 0; y < VRES; y++) {
scr_pixels[(screen->pitch >> 1) * y + x] = hicolor_color[coloridx];
}
}
SDL_UnlockSurface(screen);
SDL_Flip(screen);
assert(SDL_GetTicks() > t);
if ((t = SDL_GetTicks() - t) < 1000 / fps) {
printf("%d\n", t); //why does this fix the framerate problem?
SDL_Delay((1000 / fps) - t);
}
}
exit(EXIT_SUCCESS);
}
for some reason that printf() call before the call to SDL_Delay() fixes the garbage ... but even more perplexing to me is the fact that when I tried replacing it with putchar('\0') the problem appears again so for some reason, only a call to printf() fixes it, and not putchar() which leads me to wonder what other calls might have this same effect, and what mechanism this effect may be caused by.
I started suspecting the AMD Catalyst drivers (Omega 14.12) so after adding a command line option to print the current frame number, frame execution time, and average framerate, I tried the program on another machine running Lubuntu using the default open source drivers. On this machine, everything worked as it should be expected.
Disabling the "tear free" option in Catalyst Control Center, fixed the screen corruption. Based on the results i've seen I believe that this issue may be originating from a lower level, probably involving the AMD video drivers.