ReadWriteEvent example

This forum covers PCAN-Linux and Linux development issues concerning our products
Post Reply
Terje
Posts: 16
Joined: Sun 27. Sep 2020, 19:04

ReadWriteEvent example

Post by Terje » Wed 30. Dec 2020, 21:15

Hi.
There are a few code examples for C in the pcanbasic api package.
ReadEvent, WriteEvent. But I'm missing the ReadWriteEvent example.
I patched the examples together. The added code is not tested. But I have tested a similar code earlier with none working result.
The question is,, Is this the correct approach?
If not,, What is the correct approach?

The Idea is to send a message every 100mS and else receive messages.
Or should I run one thread with ReadEvent and one thread with WriteEvent?

I'm using Ubuntu 20.04.1 LTS 64 Bit
PeakBasic API 4.4.0
Peak-linux-driver-8.10.2
PcanDevice IPEH-004064

Regards
Terje.

Here is the code..

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <asm/types.h>
#include <unistd.h>

#define PCAN_DEVICE     PCAN_USBBUS1

#include "PCANBasic.h"

static int SendInterrupt=0;		//Set to 1 by an timer every 100mS.

static struct sigaction oldact;

static void signal_handler(int s)
{
	printf("Interrupted by SIG%u!\n", s);
}

static int setup_sig_handler(int signum, void (*f)(int))
{
	struct sigaction act;

	memset(&act, 0, sizeof act);
	act.sa_handler = f;

	// note: siagaction() is thread -safe
	return sigaction(signum, &act, &oldact);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>	Main entry-point for this application. </summary>
///
/// <remarks>	 </remarks>
///
/// <param name="argc">	The argc. </param>
/// <param name="argv">	[in,out] If non-null, the argv. </param>
///
/// <returns>	. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////

int main(int argc, char* argv[])
{
	TPCANStatus Status;
	unsigned int rx_count = 0;
	unsigned int tx_count = 0;
	unsigned int pcan_device = PCAN_DEVICE;


	// be INTRuptible by user
	setup_sig_handler(SIGINT, signal_handler);

	Status = CAN_Initialize(pcan_device, PCAN_BAUD_500K, 0, 0, 0);
	printf("CAN_Initialize(%xh): Status=0x%x\n", pcan_device, (int)Status);
	if (Status)
		goto lbl_exit;

	int fd;
	Status = CAN_GetValue(pcan_device, PCAN_RECEIVE_EVENT, &fd, sizeof fd);
	printf("CAN_GetValue(%xh): Status=0x%x\n", pcan_device, (int)Status);
	if (Status)
		goto lbl_close;

	fd_set fdsIn;
	fd_set fdsOut;


	FD_ZERO(&fdsIn);
	FD_SET(fd, &fdsIn);
	FD_ZERO(&fdsOut);
	FD_SET(fd, &fdsOut);

	TPCANMsg MessageRx,MessageTx;

	MessageTx.ID = 0x77;
	MessageTx.LEN = 8;
	MessageTx.MSGTYPE = PCAN_MESSAGE_STANDARD;
	memset(MessageTx.DATA, '\0', sizeof(MessageTx.DATA));

	// forever loop
	while (1) {

		// blocks on read descriptor
		int err = select(fd+1, &fdsIn, &fdsOut, NULL, NULL);
		if (err != 1 || !FD_ISSET(fd, &fdsIn)) {
			printf("select(%xh) failure: %d\n", pcan_device, err);
			break;
		}

		if(SendInterrupt == 1)
		{
			SendInterrupt = 0;		// Clear timer flag.

			Status = CAN_Write(pcan_device, &MessageTx);
			if (Status != PCAN_ERROR_OK) {
				printf("CAN_Write(%xh) failure 0x%x\n", pcan_device, (int) Status);
				break;
			}

			// increment data bytes
			for (int i = 0; i < 8; i++)
				if (++MessageTx.DATA[i])
					break;

			tx_count++;
			printf("  - S ID:%4x LEN:%1x DATA:%02x %02x %02x %02x %02x %02x %02x %02x\n",
					(int) MessageTx.ID, (int) MessageTx.LEN, (int) MessageTx.DATA[0],
					(int) MessageTx.DATA[1], (int) MessageTx.DATA[2],
					(int) MessageTx.DATA[3], (int) MessageTx.DATA[4],
					(int) MessageTx.DATA[5], (int) MessageTx.DATA[6],
					(int) MessageTx.DATA[7]);

		}

		Status = CAN_Read(pcan_device, &MessageRx, NULL);

		if (Status == PCAN_ERROR_QRCVEMPTY) {
			printf("CAN_Read(%xh) Abnormal PCAN_ERROR_QRCVEMPTY status. Wating 1s before looping (^C to stop)...\n", pcan_device);

			if (usleep(1000000))
				break;

			continue;
		}

		if (Status != PCAN_ERROR_OK) {
			printf("CAN_Read(%xh) failure 0x%x\n", pcan_device, (int) Status);
			break;
		}



		rx_count++;
		printf("  - R ID:%4x LEN:%1x DATA:%02x %02x %02x %02x %02x %02x %02x %02x\n",
				(int) MessageRx.ID, (int) MessageRx.LEN, (int) MessageRx.DATA[0],
				(int) MessageRx.DATA[1], (int) MessageRx.DATA[2],
				(int) MessageRx.DATA[3], (int) MessageRx.DATA[4],
				(int) MessageRx.DATA[5], (int) MessageRx.DATA[6],
				(int) MessageRx.DATA[7]);

	}


	printf("pcaneventread(%xh): received %u message(s)\n", pcan_device, rx_count);
	printf("pcaneventwrite(%xh): sent %u message(s)\n", pcan_device, tx_count);

lbl_close:
	CAN_Uninitialize(pcan_device);

lbl_exit:

	return 0;
}
Last edited by M.Heidemann on Thu 31. Dec 2020, 11:19, edited 1 time in total.
Reason: Please use the Code-Tags for better readability.

M.Heidemann
Sales & Support
Sales & Support
Posts: 503
Joined: Fri 20. Sep 2019, 13:31

Re: ReadWriteEvent example

Post by M.Heidemann » Thu 31. Dec 2020, 11:47

Hello,

As for writing the messages a timer based solution should be sufficent,
however a seperate thread for reading messages with events is recommended, as
you do need to use an interrupt every time you want to write a message.

You could then use your timer to simply call your write routine, without
having the read-thread interrupted.

Adjust you code accordingly and give us feedback, if this
solution was the right solution for you.

Best Regards

Marvin

Terje
Posts: 16
Joined: Sun 27. Sep 2020, 19:04

Re: ReadWriteEvent example

Post by Terje » Thu 31. Dec 2020, 15:47

Thank you for your advice.

I have now made an example of EventRead & Write to CAN.
The code is hopefully easy to follow.
I did not use a interrupt. I think it is good enough just to loop with a 100mS delay. I think the tolerance will be +/- 1% of the delay.
I also used fork instead of threads and a shared memory variable.

Please check and comment if I have done any serious errors.

Regards
Terje
(Wish you all a Happy new Year !!)

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <asm/types.h>
#include <unistd.h>
#include <sys/mman.h>

#define PCAN_DEVICE     PCAN_USBBUS1

#include "PCANBasic.h"

char *sharedmem;

void Transmit_Process(int pcan_device);
void Reveive_Process(int pcan_device);

int main(int argc, char* argv[])
{
	TPCANStatus Status;
	unsigned int pcan_device = PCAN_DEVICE;

	Status = CAN_Initialize(pcan_device, PCAN_BAUD_500K, 0, 0, 0);
	printf("CAN_Initialize(%xh): Status=0x%x\n", pcan_device, (int)Status);
	if (Status)
	{
		puts("Failed to initalize device");
		exit(0);
	}

	sharedmem = mmap(NULL,1,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
	*sharedmem=0;

	pid_t pid;

	for(int proc=0;proc<2;proc++)
	{
		pid = fork();
		if(pid==-1)
		{
			puts("Fork error");
			return 0;
		}
		if(pid==0)
		{
			//Child
			if(proc==0)
			{
				Transmit_Process(pcan_device);
				return 0;
			}
			if(proc==1)
			{
				Reveive_Process(pcan_device);
			}
			return 0;
		}
	}

	sleep(10);		// Run program for 10 seconds, then exit.
	*sharedmem=1;	// End Transmit_Process/Reveive_Process
	sleep(1);

	CAN_Uninitialize(pcan_device);
	return 0;
}


void Transmit_Process(int pcan_device)
{
	TPCANMsg MessageTx1;

	MessageTx1.ID = 0x77;
	MessageTx1.LEN = 8;
	MessageTx1.MSGTYPE = PCAN_MESSAGE_STANDARD;
	memset(MessageTx1.DATA, '\0', sizeof(MessageTx1.DATA));

	TPCANStatus Status;

	// forever loop until shared memory is set to 1.
	while (*sharedmem!=1)
	{
		// increment data bytes
		for (int i = 0; i < 8; i++)
			if (++MessageTx1.DATA[i])
				break;

		printf("  - TX ID:%4x LEN:%1x DATA:%02x %02x %02x %02x %02x %02x %02x %02x\n",
				(int) MessageTx1.ID, (int) MessageTx1.LEN, (int) MessageTx1.DATA[0],
				(int) MessageTx1.DATA[1], (int) MessageTx1.DATA[2],
				(int) MessageTx1.DATA[3], (int) MessageTx1.DATA[4],
				(int) MessageTx1.DATA[5], (int) MessageTx1.DATA[6],
				(int) MessageTx1.DATA[7]);

		Status = CAN_Write(pcan_device, &MessageTx1);
		if (Status != PCAN_ERROR_OK)
		{
			printf("CAN_Write(%xh) failure 0x%x\n", pcan_device, (int) Status);
		}

		usleep(100000);		// Delay instead of using intterupt.
	}
}

void Reveive_Process(int pcan_device)
{
	TPCANMsg Message;
	TPCANStatus Status;

	int fd;
	Status = CAN_GetValue(pcan_device, PCAN_RECEIVE_EVENT, &fd, sizeof fd);
	printf("CAN_GetValue(%xh): Status=0x%x\n", pcan_device, (int)Status);
	if (Status)
	{
		puts("Unable to get pcan receive event");
		return;
	}

	fd_set fds;

	FD_ZERO(&fds);
	FD_SET(fd, &fds);

	// forever loop until shared memory is set to 1.
	while (*sharedmem!=1)
	{
		// blocks on read descriptor
		int err = select(fd+1, &fds, NULL, NULL, NULL);
		if (err != 1 || !FD_ISSET(fd, &fds)) {
			printf("select(%xh) failure: %d\n", pcan_device, err);
			break;
		}

		Status = CAN_Read(pcan_device, &Message, NULL);

		if (Status == PCAN_ERROR_QRCVEMPTY) {
			printf("CAN_Read(%xh) Abnormal PCAN_ERROR_QRCVEMPTY status. Wating 1s before looping (^C to stop)...\n", pcan_device);

			if (usleep(100000))
				break;

			continue;
		}

		if (Status != PCAN_ERROR_OK) {
			printf("CAN_Read(%xh) failure 0x%x\n", pcan_device, (int) Status);
			break;
		}

		printf("  - RX ID:%4x LEN:%1x DATA:%02x %02x %02x %02x %02x %02x %02x %02x\n",
				(int) Message.ID, (int) Message.LEN, (int) Message.DATA[0],
				(int) Message.DATA[1], (int) Message.DATA[2],
				(int) Message.DATA[3], (int) Message.DATA[4],
				(int) Message.DATA[5], (int) Message.DATA[6],
				(int) Message.DATA[7]);
	}
}

Terje
Posts: 16
Joined: Sun 27. Sep 2020, 19:04

Re: ReadWriteEvent example

Post by Terje » Sun 3. Jan 2021, 12:56

Hi again.
Just want to add in that I have tested a lot and can't et an reliabile 100mS(+/-5mS) message.
The solution in the code works well for a few hours. Then I start to gett Status 0x80 TxQueueFull ??
The receiving computer is holding the queues empty, and also measure the time between every message to verify the time spec.

My code flow is:
Send Message
Check for IPC-Mesages.
Wait 100mS
And start over.

Check IPC-Mesages was also disabled to check if this was the problem.
I also see that If I do things in the OS, like loading a program or so, then I get more timing issues.

Next to test was interrupts.
I only send the message in the interrupt. Nothing else, every 100mS.
Everything became worse. Lots of timing errors..

What is the next step? Writing a Module and run parallel with the kernel? Don't know how to do that using PCanBasic.

Does any have any suggestions? A different Linux distro? Which is suitable? Maybe a clean basic Linux distro.

Regards
Terje.

M.Heidemann
Sales & Support
Sales & Support
Posts: 503
Joined: Fri 20. Sep 2019, 13:31

Re: ReadWriteEvent example

Post by M.Heidemann » Mon 4. Jan 2021, 09:04

Hello,

Like mentioned before a timer based solution would be a good solution for handling the transmission of CAN-data, you currently - as far as i can tell - only suspend the thread (by using usleep, which can lead to unreliable timing, especially with multiple processes running), which is not the same as executing a function on a timer interval, which i have recommended for the tranmission of messages.

The TxQueue would not be full as long as the messages are transmitted succesfully and are acknowleged, especially with that large of a time margin (100ms) between each tranmission.

The best way to troubleshoot this would be to trace/log the CAN-Traffic (including error frames) and see what exactly happens during the run of your applications which eventually leads to the TxQueue being full.


To exclude physical error from your troubleshooting, rig up an small application
which only tranmits messages on timer-intervals and see if you run into the same issue eventuall. If this is the case, you may wanna investigate your physical setup (missing termination, wrong baudrate, etc)

Please report back to me with your findings.

Best Regards

Marvin

Terje
Posts: 16
Joined: Sun 27. Sep 2020, 19:04

Re: ReadWriteEvent example

Post by Terje » Tue 5. Jan 2021, 19:32

Hi Heidemann.

More tests are done.
I got the interrupt working. But I have not done any long tests and timing checking yet.

But I do still have an issue.
I'm not familiar with using sigaction. I need to figure out how to send in a pointer (* user_data) so I can avoid using global variables.
Do you have any idea how to solve it?

Regards
Terje.

Replace the Transmit_Process-function in the code in Post 1 with the code below.

Code: Select all

TPCANMsg MessageTx;
TPCANStatus Status;

void timer_handler(int signo)
{
	Status = CAN_Write(PCAN_DEVICE, &MessageTx);
}

void init_sigaction(void)
{
	struct sigaction tact;
	tact.sa_handler = timer_handler;
	tact.sa_flags = 0;
	sigemptyset(&tact.sa_mask);
	sigaction(SIGALRM, &tact, NULL);
}

void init_timer()
{
	struct itimerval value;
	value.it_value.tv_sec = 0;
	value.it_value.tv_usec = 100000;
	value.it_interval = value.it_value;
	setitimer(ITIMER_REAL, &value, NULL);
}

void Transmit_Process(int pcan_device)
{
	MessageTx.ID = 0x77;
	MessageTx.LEN = 8;
	MessageTx.MSGTYPE = PCAN_MESSAGE_STANDARD;
	memset(MessageTx.DATA, '\0', sizeof(MessageTx.DATA));

	//Interrupt
	init_sigaction();
	init_timer();

	// forever loop until shared memory is set to 1.
	while (*sharedmem!=1)
	{
		sleep(1);
	}
}

M.Heidemann
Sales & Support
Sales & Support
Posts: 503
Joined: Fri 20. Sep 2019, 13:31

Re: ReadWriteEvent example

Post by M.Heidemann » Wed 6. Jan 2021, 11:51

Hello,

Firstly, is the original issue resolved (TxQueueFull) and are you unable to reproduce it?

Secondly, did you find any physical issue which may have caused this behaviours, like mentioned in my previous post?
But I do still have an issue.
Are you refering to the original problem or to the
usage of sigaction?

In case you still have the original issue at hand you may want to take a look at
the pcanfdtst test application(in the test directory) provided with the PCAN-Linux driver package,
it allows you to send frames periodically, as for example:

Code: Select all

pcanfdtst tx -p 100000 -i 0x123 -l 8 -b 500k /dev/pcan32


This will send CAN-ID 123h every 100ms on a USB-device 500kbit/s.

Code: Select all

lspcan -t -T -i
Will give you feedback on the current bus-state.

Start with testing if the issue described originally will return at any point by
sending messages continuously. If the issues arises again, check for physical
factors such as if the proper termination is present at the bus, that the used Bitrate is correct and if there are damages on the bus
itself. Such an issue is also reflected in a bus-state such as "passive".

You can also take a look at the source-code in the "src" directory to see how this was implemented, in case you
need additional hints for your own implementation.

Please give us feedback regarding this

Best Regards

Marvin

Post Reply