How do I open a ttyMFD device on the Intel-Edison [C++]? - c++

I have a /dev/ttyUSB device and a /dev/ttyMFD device that I need to stream to logfiles. For the USB device I could use termios and configure it through that. This was pretty straight forward and there was a bit of documentation for this as well.
I can't seem to find any for the MFD though. Some places call it a MultiFuctionDevice and others call it the Medfield High Speed UART device.
Which is correct in the first place?
And secondly, can I open it in the same way that I open up a regular ttyUSB device?
Here is the snippit I use to open USB devices.
int fd = open(USBDEVICE0, O_RDWR);
struct termios io;
memset(&io, 0, sizeof(io));
io.c_iflag = 0;
io.c_oflag = 0;
io.c_cflag = CS8|CREAD|CLOCAL; // 8n1, see termios.h for more information
io.c_lflag = 0;
// TODO -- Since we are operating in non-blocking mode; confirm VMIN and VTIME settings have no effect on duration of the read() call.
io.c_cc[VMIN] = 1;
io.c_cc[VTIME] = 5;
speed_t speedSymbol = B921600;
cfsetospeed(&io, speedSymbol);
cfsetispeed(&io, speedSymbol);
int retVal;
retVal = tcsetattr(fd, TCSANOW, &io);
tcflush(fd, TCIOFLUSH);
usleep(100);
EDIT
For anyone who comes across this, there is one caveat. You must open the device in raw mode and dump everything into a log file. Parsing must be done post. Everything will come out as raw data but if you try to do any kind of configuration, the devices buffer will not be able to capture all the data, hold it, and process it in time before more data comes along.

MFD in Linux kernel is common abbreviation to Multi-Functional Device, Legacy serial driver for Edison abuses that and uses it's own interpretation as you mentioned: Medfield. In the upstream kernel the abbreviation MID is used regarding to Mobile Internet Device. In particular the serial driver is called drivers/tty/serial/8250_mid.c. See https://en.wikipedia.org/wiki/Mobile_Internet_device.
Yes, you may do the same operations as you did on top of /dev/ttyUSBx.

Related

Communicating with a serial port in a bit bang state

My question is largely a clarification question, I am trying to communicate through a serial port in a bit bang sort of sense. Whereas I want to output out of the serial port exactly what I write to it..
If I write 0-0-0-1, I would want the output on the wire to be 0-0-0-1.
The reason I ask this question is because it seems like using the DCB object to configure the port, I have to set all sorts of settings like Stop Bits and Parity?
Is there a way to configure this port as 'raw' as possible? I don't want anything being sent that I don't send.. if that makes sense..
//Open the serial port
hComm = CreateFile( "\\\\.\\COM1", // Name of the Port to be Opened
GENERIC_WRITE, // Write Access
0, // No Sharing, ports cant be shared
NULL, // No Security
OPEN_EXISTING, // Open existing port only
0, // Non Overlapped I/O
NULL); // Null for Comm Devices
DCB dcbSerialParams = { 0 }; // Initializing DCB structure
dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
//retreives the current settings
Status = GetCommState(hComm, &dcbSerialParams);
if(!Status){
//Error in GetCommState!
}
dcbSerialParams.BaudRate = 10000; // Setting BaudRate = 10000
dcbSerialParams.ByteSize = 8; // Setting ByteSize = 8
dcbSerialParams.StopBits = ONESTOPBIT; // Setting StopBits = 1
dcbSerialParams.Parity = ODDPARITY;
I wasn't quite sure from the question article what you were having problems with.
Are you worried about the bit order of the MSB/LSB or the endianness of 4-byte integers? Or will data be added or deleted or changed by operating as a terminal?
Please add the exact content.
Bit order is a hardware standard specification issue so you don't have to worry about it.
The endian problem seems to be lacking in your understanding.
Serial port communication is basically byte-by-byte communication.
You need to set the data in a byte array and send it, and the received data is a byte array.
It is a problem on the application program side to give them meanings such as 4-byte integers and single/double precision floating point numbers.
Then, looking at your source code, it seems that you are calling the Windows Win32 API directly.
Communications Functions
The Win32 API and device drivers only have raw mode.
Unlike Unix tty devices, it doesn't have any features like character completion/conversion/newline waiting buffering or control code equivalents. (Unless you explicitly specify XON / XOFF flow control)
On Windows, those features aren't APIs, but libraries/framworks that run on them.
Basically, each setting such as DCB and timeout must be done according to the specifications of the device with which you are communicating. Even if you say "all settings", the number of items will fit on one page.
DCB structure (winbase.h)

How to use IOServiceOpenAsFileDescriptor?

I have a USB device attached. I decided to try to write some code where I open the USB device as a FileDescriptor like on Linux where I can use ioctl function on the fd to send commands.
I ended up with the following:
CFMutableDictionaryRef matches = IOServiceMatching(kIOUSBDeviceClassName);
if (!matches)
{
std::cerr<<human_error_string(kIOReturnError)<<"\n";
return;
}
io_iterator_t deviceIterator;
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matches, &deviceIterator);
if (kr != kIOReturnSuccess)
{
std::cerr<<human_error_string(kr)<<"\n";
return;
}
io_service_t service = IO_OBJECT_NULL;
while((service = IOIteratorNext(deviceIterator)))
{
int32_t fd = IOServiceOpenAsFileDescriptor(service, O_RDWR) //doesn't work! Returns: -1
IOObjectRelease(service);
}
IOObjectRelease(deviceIterator);
However, the FD is always -1.. Any ideas why?
As far as I'm aware, there isn't any kind of file descriptor based API for USB on macOS. You need to go through the IOUSB based APIs. There's some example code here.
If you want to share as much code as possible between Linux an macOS, you can use libusb which wraps both Apple's IOKit API and Linux's ioctl based USB API.
I have to admit I'm not exactly sure what IOServiceOpenAsFileDescriptor is intended for. The source code in IOKitLib is not terribly enlightening, as it simply connects to an XPC service and the file descriptor is expected in the XPC reply. I assume the service is implemented in a system daemon that isn't open source or documented.
Perhaps IOServiceOpenAsFileDescriptor is for accessing block devices or serial ports, which have both an IOKit object and a device node file in /dev/. General USB devices do not have a node in the file system.

input delay with PortAudio callback and ASIO sdk

I'm trying to get the input from my guitar to be played through my computer using the portaudio library and the ASIO sdk.
I have been following some of the tutorials on the official website to get the basics set up. Currently I got it working so that portaudio is listening to the right input and output device and I have the callback setup to just output the input and do nothing with it like this:
static int paTestCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData)
{
float *out = (float*)outputBuffer;
float* in = (float*)inputBuffer;
for (int i = 0; i<framesPerBuffer; i++)
{
*out++ = *in++; /* left */
*out++ = *in++; /* right */
}
return 0;
}
This callback is setup by calling this:
PaError error = Pa_OpenDefaultStream(&stream, 2, 2, paFloat32, 44100, paFramesPerBufferUnspecified, paTestCallback, &data);
Pa_StartStream(stream);
Now, this does work but I have a lot of delay (about 0.5s) when I strike a string on my guitar and when I hear it through the monitors.
Is there a way to solve this delay? Do I need to rewrite the callback method?
EDIT:
So, I got the delay to be a lot better using this code instead of the basic Pa_OpenDefaultStream()
int defaultIn = Pa_GetDefaultInputDevice();
int defaultOut = Pa_GetDefaultOutputDevice();
PaStreamParameters *inParam = new PaStreamParameters();
inParam->channelCount = 2;
inParam->device = defaultIn;
inParam->sampleFormat = paFloat32;
inParam->suggestedLatency = 0.05;
PaStreamParameters *outParam = new PaStreamParameters();
outParam->channelCount = 2;
outParam->device = defaultOut;
outParam->sampleFormat = paFloat32;
outParam->suggestedLatency = 0;
error = Pa_OpenStream(&stream, inParam, outParam, 44100, paFramesPerBufferUnspecified, paNoFlag, paTestCallback, &data);
if (error != paNoError) {
Logger::log("[PortAudioManager] Could not open default stream. Exiting function...");
return;
}
Pa_StartStream(stream);
There is still a little bit of delay though, mostly noticeable when playing more then just a single note.
EDIT:
I figured out with the help of Ross-Bencina that the windows default input device and output device doesn't change anything to the index of the host api's in PortAudio. I seemed to be using MME all this time. I did the following to get the right index for the ASIO device:
int hostNr = Pa_GetHostApiCount();
std::vector<const PaHostApiInfo*> infoVertex;
for (int t = 0; t < hostNr; ++t) {
infoVertex.push_back(Pa_GetHostApiInfo(t));
}
Then I just checked which is the one with ASIO and set the suggestedLatency in both PaStreamParameters to 0 and the delay is now gone and sound is good (although it's mono for now).
You are on the right track using paFramesPerBufferUnspecified.
The ASIO latency behavior depends on the driver. There are two possibilities:
The ASIO driver lets the code (i.e. PortAudio) request a latency (possibly with some constraints). PortAudio finds to best match between a supported driver buffer size and the latency that you request.
The other possibility is that your audio interface does not provide programmatic control over latency settings. Instead, latency is only selectable from the driver's ASIO control panel UI (and the driver will force a fixed buffer size on PortAudio). In this case, you should investigate the driver control panel UI to set the lowest workable latency.
In either case, your approach with Pa_OpenStream is close to optimal, but you should request zero latency for both input and output (in your edit you're requesting 50ms input latency, zero output latency). The end result will be that PortAudio selects the lowest available ASIO buffer size. If this turns out to be unstable (audio glitches) then you'll need to increase the requested latency.
include/pa_asio.h exposes a host-API-specific interface for querying the ASIO buffer sizes allowed by the driver (be aware that this can change if you change settings in the control panel). It also provides a function to display the driver's control panel UI.
EDIT: Note that Pa_GetDefaultInputDevice() and Pa_GetDefaultOutputDevice() will only return ASIO devices if you built PortAudio for ASIO only. If you included any other more common APIs in the build (e.g. WMME or DirectSound) they will be given priority as the (lowest common denominator) default device. You could add a check that you are actually accessing the ASIO device:
assert(Pa_GetHostApiInfo(Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->hostApi)->type == paASIO);
If PortAudio is compiled with support for multiple host APIs: To get the default ASIO device: enumerate host APIs using Pa_GetHostApiCount and Pa_GetHostApiInfo to find the ASIO host API. Then pull the default ASIO device indices from the returned PaHostApiInfo struct.

New to linux: C++ opening and closing usb port issues

New developer, Linux, C++, USB - Serial Adapter.
I've completed a program where I am able to write to the USB port. However, if I change my code, make, log back in as root, and try to write to the port again, it doesn't go through. It'll only work if I remove the USB cable from the computer and reseat it before attempting sending data again. If you need more info let me know.
I'm on two different computers and have no way of copying and pasting but here is the gist of what I'm doing.
int fd = 0;
int iOut = 0;
char *ComPort = "/dev/ttyUSB0";
fd=open(ComPort, O_CREAT | O_RDWR | O_NOCTTY | O_NDELAY);
if(fd == -1)
cout << "unable to open" << endl;
// blah blah getting data ready to be sent
// create a block of 50 hex characters to be sent : DB
iOut = write(fd, $DB, sizeof(DB));
// blah blah error checking
close(fd);
return(0);
#Surt #alexfarber I had a talk with a coworker on this and we concluded that this is most likely a hardware issue with my display or usb to serial adapter. I believe the only way this can work with this particular adapter is by turning off the power to it and turning it back on in order to reflect what it would see when being removed and reseated manually. I dont believe this is possible but I'll start another thread with anything I may run into. I appreciate you all taking the time to help with this, I did learn a number of other things I didn't know before hand so this was still very helpful. Thank you once again.
Take at look at chapter 3.2 here http://www.tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html
add some of the error checking first so you can see where if fails. The perror line will help there.
if (fd <0) {perror(ComPort ); exit(-1); } // note the exit which your code doesn't have.
This should now tell you some more info and add
if (errno) {perror(ComPort ); exit(-1); }
after all operations, read, write and set things on the fd.
now add the newtio part of 3.2 to your program in case some handshake failed. You must change it so it conforms with the display.
The final version of your program might be more like 3.3.

Use Arduino signals as input for PC?

I recently got my hands on an Arduino (Uno), and I was wondering something.
I've got no external volume changer for my speakers, so I thought, maybe hook up a potentiometer to the Arduino, and then use that to control the volume (in Windows). But is that possible?
To read a value of an Arduino pin using maybe Visual C, C++ (or some more 'multi-platform' language)? And then using that to set the volume level in Windows (and if it's possible also in Linux).
I thought it might be possible, because if you use:
Serial.println(analogRead([pin with potentiometer]));
You can get the values of the potentiometer to the pc (via USB). So is there any way to read those values in C or C++?
I know how to set the volume in Windows via C or C++, I only need to know if there is a way to read out the values of a potentiometer in a (Visual) C or C++ script.
Definitely. And using exactly the same method: serial communication. Since I'm not a great Windows expert, I can't write you a complete Windows example, but here are some snippets that may get you started on a Unix (Linux, OS X, etc.):
Code on the Arduino:
#define POT_PIN 1
void setup()
{
Serial.begin(9600); // 9600 baud is more than enough
}
void loop()
{
int pot = analogRead(POT_PIN);
unsigned char byte = (pot + 2) >> 2; // round and divide by 4
Serial.write(pot);
delay(100); // or do something useful
}
Code on the computer:
#include <termios.h>
struct termios tio;
memset(&tio, 0, sizeof(tio));
// Open serial port in mode `8N1', non-blocking
tio.c_cflag = CS8 | CREAD | CLOCAL;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 5;
int fd = open(device, O_RDONLY);
cfsetospeed(&tio, B9600);
cfsetispeed(&tio, B9600);
tcsetattr(fd, TCSANOW, &tio);
while (1) {
unsigned char byte;
read(fd, &byte, 1);
use_some_library_to_set_volume(byte);
}
There are some things to consider.
You want the PC to listen to your "potentiometer Arduino" only. You don't want it to listen to any random USB data coming at any port. There is a need to create a data protocol which is somewhat unique to your device. If you just spam analog readings you'll get some raw data like "123 128 133 145", which could mean anything and come from any kind of device.
When you type Serial.whatever, the Arduino is likely spitting out an UART signal. I don't know a thing about Arduino, but hopefully it has either RS-232 or USB circuitry on board. You will need a matching port on the PC, the traditional RS-232 9 pin d-sub connector is getting increasingly rare on modern computers. You might need a RS-232 to USB converter.
You need a mean to determine which COM port the device is connected to. USB ports and traditional RS-232 ports work the same, as far as the PC is concerned. Either let the user pick the port manually, or design a more intelligent algorithm to find the port where your device is connected.
Study the Windows API for all the serial port routines. Documentation and examples at MSDN.