How TTY "input speed" differs from "output speed"? - tty

There are two commands for setting "speed" - cfsetospeed and cfsetispeed.
But why only one "speed" is shown by stty?
According to bits/termios.h, c_ispeed and c_ospeed are "input speed" and "output speed".
I tried to set B4800 to "input speed" and "B57600" to "output speed", and vice versa at the other
end of serial channel. But data is corrupted. Why there are two separate speeds if it is impossible to set
them separately?
stty shows the speed which was set by cfsetospeed or cfsetispeed - whichever was called last.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Moreover, while B0 is set, I can receive and transmit data with whatever speed was
active before B0 was set. Is it documented somewhere? Is it undefined behavior or it is in POSIX?
EDIT:
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:
Ordinary serial port uses 9600 "speed" if we set B0, whereas
usb serial port uses whatever speed was selected before B0 was set.
(OS: Linux)

Why there are two separate speeds if it is impossible to set them separately?
Some (older) UARTs (e.g. the ubiquitous 8250 and its successors) actually have a crystal input for the transmitter and another clock input for the receiver. Hence the input baudrate can be different from the output baudrate on such UARTs.
But most (if not all) boards feed the transmit clock to the receive clock to negate this feature (e.g. the typical 8250/165x0 datasheet will show the RCLK input driven by the BAUDOUT output).
The separate termios speed elements simply reflects this obscure hardware capability (that is rarely actually available).
Also most UARTs (that are not 8250-based) in SoCs have a common clock input for transmit and receive, so the baudrate setting has to apply to both.
So specifying "separate" baudrates is typically a useless configuration.
Besides, B0 setting takes effect only with cfsetospeed. Is it documented somewhere?
Code is the documentation.
Most serial port drivers call uart_get_baud_rate() in drivers/tty/serial/serial_core.c to decode the baudrate settings from the termios structure.
/**
* uart_get_baud_rate - return baud rate for a particular port
* #port: uart_port structure describing the port in question.
* #termios: desired termios settings.
* #old: old termios (or NULL)
* #min: minimum acceptable baud rate
* #max: maximum acceptable baud rate
*
* Decode the termios structure into a numeric baud rate,
* taking account of the magic 38400 baud rate (with spd_*
* flags), and mapping the %B0 rate to 9600 baud.
*
* If the new baud rate is invalid, try the old termios setting.
* If it's still invalid, we try 9600 baud.
*
* Update the #termios structure to reflect the baud rate
* we're actually going to be using. Don't do this for the case
* where B0 is requested ("hang up").
*/
Note the special handling mentioned for B0.
I conducted the same tests on ordinary serial port (i.e., no usb) and obtained the following curious difference with usb serial port:...
The list of drivers that use uart_get_baud_rate() does not seem to include any USB serial port adapters: https://elixir.free-electrons.com/linux/latest/ident/uart_get_baud_rate
You may have to inspect the driver for your specific USB serial port adapter for its handling of the termios baudrate specifers.

Related

ESP8266 analogRead() microphone Input into playable audio

My goal is to record audio using an electret microphone hooked into the analog pin of an esp8266 (12E) and then be able to play this audio on another device. My circuit is:
In order to check the output of the microphone I connected the circuit to the oscilloscope and got this:
In the "gif" above you can see the waves made by my voice when talking to microphone.
here is my code on esp8266:
void loop() {
sensorValue = analogRead(sensorPin);
Serial.print(sensorValue);
Serial.print(" ");
}
I would like to play the audio on the "Audacity" software in order to have an understanding of the result. Therefore, I copied the numbers from the serial monitor and paste it into the python code that maps the data to (-1,1) interval:
def mapPoint(value, currentMin, currentMax, targetMin, targetMax):
currentInterval = currentMax - currentMin
targetInterval = targetMax - targetMin
valueScaled = float(value - currentMin) / float(currentInterval)
return round(targetMin + (valueScaled * targetInterval),5)
class mapper():
def __init__(self,raws):
self.raws=raws.split(" ")
self.raws=[float(i) for i in self.raws]
def mapAll(self):
self.mappeds=[mapPoint(i,min(self.raws),max(self.raws),-1,1) for i in self.raws ]
self.strmappeds=str(self.mappeds).replace(",","").replace("]","").replace("[","")
return self.strmappeds
Which takes the string of numbers, map them on the target interval (-1 ,+1) and return a space (" ") separated string of data ready to import into Audacity software. (Tools>Sample Data Import and then select the text file including the data). The result of importing data from almost 5 seconds voice:
which is about half a second and when I play I hear unintelligible noise. I also tried lower frequencies but there was only noise there, too.
The suspected causes for the problem are:
1- Esp8266 has not the capability to read the analog pin fast enough to return meaningful data (which is probably not the case since it's clock speed is around 100MHz).
2- The way software is gathering the data and outputs it is not the most optimized way (In the loop, Serial.print, etc.)
3- The microphone circuit output is too noisy. (which might be, but as observed from the oscilloscope test, my voice has to make a difference in the output audio. Which was not audible from the audacity)
4- The way I mapped and prepared the data for the Audacity.
Is there something else I could try?
Are there similar projects out there? (which to my surprise I couldn't find anything which was done transparently!)
What can be the right way to do this? (since it can be a very useful and economic method for recording, transmitting and analyzing audio.)
There are many issues with your project:
You do not set a bias voltage on A0. The ADC can only measure voltages between Ground and VCC. When removing the microphone from the circuit, the voltage at A0 should be close to VCC/2. This is usually achieved by adding a voltage divider between VCC and GND made of 2 resistors, and connected directly to A0. Between the cap and A0.
Also, your circuit looks weird... Is the 47uF cap connected directly to the 3.3V ? If that's the case, you should connect it to pin 2 of the microphone instead. This would also indicate that right now your ADC is only recording noise (no bias voltage will do that).
You do not pace you input, meaning that you do not have a constant sampling rate. That is a very important issue. I suggest you set yourself a realistic target that is well within the limits of the ADC, and the limits of your serial port. The transfer rate in bytes/sec of a serial port is usually equal to baud-rate / 8. For 9600 bauds, that's only about 1200 bytes/sec, which means that once converted to text, you max transfer rate drops to about 400 samples per second. This issue needs to be addressed and the max calculated before you begin, as the max attainable overall sample rate is the maximum of the sample rate from the ADC and the transfer rate of the serial port.
The way to grab samples depends a lot on your needs and what you are trying to do with this project, your audio bandwidth, resolution and audio quality requirements for the application and the amount of work you can put into it. Reading from a loop as you are doing now may work with a fast enough serial port, but the quality will always be poor.
The way that is usually done is with a timer interrupt starting the ADC measurement and an ADC interrupt grabbing the result and storing it in a small FIFO, while the main loop transfers from this ADC fifo to the serial port, along the other tasks assigned to the chip. This cannot be done directly with the Arduino libraries, as you need to control the ADC directly to do that.
Here a short checklist of things to do:
Get the full ESP8266 datasheet from Expressif. Look up the actual specs of the ADC, mainly: the sample rates and resolutions available with your oscillator, and also its electrical constraints, at least its input voltage range and input impedance.
Once you know these numbers, set yourself some target, the math needed for successful project need input numbers. What is your application? Do you want to record audio or just detect a nondescript noise? What are the minimum requirements needed for things to work?
Look up in the Arduino documentartion how to set up a timer interrupt and an ADC interrupt.
Look up in the datasheet which registers you'll need to access to configure and run the ADC.
Fix the voltage bias issue on the ADC input. Nothing can work before that's done, and you do not want to destroy your processor.
Make sure the input AC voltage (the 'swing' voltage) is large enough to give you the results you want. It is not unusual to have to amplify a mic signal (with an opamp or a transistor), just for impedance matching.
Then you can start writing code.
This may sound awfully complex for such a small task, but that's what the average day of an embedded programmer looks like.
[EDIT] Your circuit would work a lot better if you simply replaced the 47uF DC blocking capacitor by a series resistor. Its value should be in the 2.2k to 7.6k range, to keep the circuit impedance within the 10k Ohms or so needed for the ADC. This would insure that the input voltage to A0 is within the operating limits of the ADC (GND-3.3V on the NodeMCU board, 0-1V with bare chip).
The signal may still be too weak for your application, though. What is the amplitude of the signal on your scope? How many bits of resolution does that range cover once converted by the ADC? Example, for a .1V peak to peak signal (SIG = 0.1), an ADC range of 0-3.3V (RNG = 3.3) and 10 bits of resolution (RES = 1024), you'll have
binary-range = RES * (SIG / RNG)
= 1024 * (0.1 / 3.3)
= 1024 * .03
= 31.03
A range of 31, which means around Log2(31) (~= 5) useful bits of resolution, is that enough for your application ?
As an aside note: The ADC will give you positive values, with a DC offset, You will probably need to filter the digital output with a DC blocking filter before playback. https://manual.audacityteam.org/man/dc_offset.html

What's the maximum baud rate in MATLAB?

Ubuntu 16.04 & MATLAB R2017a.
I'm trying to set serial port like that:
s=serial_port('/dev/ttyUSB0','BaudRate',115200,'DataBits',8,'InputBufferSize',80000)
It's working fine, but when I try to change baud rate, say 1000000.
I got this message:
Open failed: BaudRate could not be set to the specified value.
So, I have 2 question:
1) Is it possible to set not common baud rates, say 2000000?
2) I found, that 1500000 and 3000000 is working for me.
Is there maximum speed?
** UPDATE**
I know how to change the baud rate in OS, in my case (Ubuntu 16.04)
setserial is not working, so I'm using sudo stty -F /dev/ttyUSB3 3500000 (not all speed is allowed) or via asm/termios.h> -- all speed is allowed.
So, I'm using second way.
After that, I can easily listen the port like that cu -l /dev/ttyUSB0
And at the same time I cant set the speed in matlab.. (Error above)
Although this link should provide you enough information about how to manage baud rates on Matlab side, as #Cris Luengo already stated in his command, I would like to elaborate a little bit on the hardware side of the problem.
Using the following command:
stty -F /dev/ttyUSB0
you should be able to retrieve the current baud rate of the targeted device. Alternatively, the following command also retrieves that value:
setserial -ag /dev/ttyUSB0
together with other important information:
/dev/ttyUSB0, Line ..., UART: ..., Port: ..., IRQ: ...
Baud_base: ..., close_delay: ..., divisor: ...
closing_wait: ..., closing_wait2: ...
Flags: ...
OS side, you can play with the baud rate of some devices but if you want to avoid problems, you always have to set a coherent value when establishing a connection. Generally speaking, devices have a tolerance level (of, I think, no more than ±5%) on overspeed and underspeed concerning baud rate deviance... so you can try to force an arbitrary baud rate different from the current one, but you don't want to go too far from it.

Baud rate of inb and outb commands - linux

I want to pass binary data from my PC to my micro-controller through the real serial port in my computer. At this time, I'm thinking of using INB and OUTB functions so I don't have to put up with linux tty character overrides.
Question is, how do I configure the baud rate of the serial port, if I use its address with the INB and OUTB functions? For example, if I want to write to COM1 (0x3F8), how will I be able to determine the baud rate data is being written at?
Does stty still have some sort of background control with INB and OUTB or setserial or what?
At this time, I'm thinking of using INB and OUTB functions so I don't have to put up with linux tty character overrides.
This is a terrible, no good, very bad idea, and it won't work. The existing serial drivers will get very confused if you try to change the port configuration from under them.
Use standard UNIX APIs to interact with the serial port. It's not hard.
Duskwuff is right, of course. Why go low-level and not use the API?
See http://www.tldp.org/HOWTO/text/IO-Port-Programming section 6.3:
If the device you're talking to supports something resembling RS-232,
you should be able to use the serial port to talk to it. The Linux
serial driver should be enough for almost all applications (you
shouldn't have to program the serial port directly, and you'd probably
have to write a kernel driver to do it); it is quite versatile, so
using non-standard bps rates and so on shouldn't be a problem.
To illustrate this, here is the answer to the original question "how to set the baud rate":
In addition to the port (e.g. 0x3F8 for COM1), the next few port numbers are used for various controlling purposes. For setting the baud rate, first calculate the divisor 115200 / [desired baud rate]. E.g. if you want 38400 baud, the divisor is 3. Then:
Set the highest bit of PORT+3
Send the least significant byte of the divisor to PORT
Send the most significant byte of the divisor to PORT+1
Clear the highest bit of PORT+3 (at the same time, the lower bits can be used to set the parity, stop bits etc.)
In code:
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
outb(PORT + 1, 0x00); // (hi byte)
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
Source is here: http://wiki.osdev.org/Serial_Ports

Linux set higher baud rate and associated settings

Input hardware: BeagleBone Black, with some GNU/Linux distro running on it.
What I want to achieve: I want to set some UART peripheral to 921600 baud value, and be able to set the other serial-associated settings (e.g. start/stop bits, parity, data bits, hw flow control, etc).
By far, in Linux, I have found at least three ways of configuring this parameters:
Using struct termios form termios.h header file.
Using struct termios2 from asm/termios.h header file.
Using the stty(1) GNU/Linux utility.
What's the problem:
With the first method I can't use the 921600 baud rate value (there's no define for such higher value, it only goes up to 230400 baud. So this method won't work.
The second method offers me one way to change custom baud rate values, but is also tricky because it doesn't offer some functions like tcgetattr(), tcsendbreak(), tcflush(), and so one. This functions are present in the first-described method, and I can't include both header files termios.h and asm/termios.h because of (1).
The last method also don't work, or at least it doesn't work for all the settings I want to make. This is the current method I'm using, I'm opening the targeted file, I get one file descriptor, to that file descriptor I set the communication parameters (baud (first i set one lower value), parity, start/stop bits, etc) using the first method, and then I use stty(1) utility to change (override) the baud rate value to 921600 (I make a system(...) function call to perform this).
This method won't work if I want to change the HW flow control for example (it won't override that setting, just like happens with the baud value).
What are the solutions?
Is it ok to mix two methods of setting parameters to a UART-communication link like this?
You may use setserial console utility:
1) Get baud_base value from setserial output
setserial -a /dev/<...>
2) Calculate divisor = baud_base / desired_baud_rate
For example if baud_base = 3000000 (3MHz):
baud_rate = 115200 -> divisor = 26.04 (approximately 26)
baud_rate = 230400 -> divisor = 13.02 (approximately 13)
baud_rate = 921600 -> divisor = 3.26
3.26 is too much to set divisor=3 and too little to set divisor=4.
So, in this case you can't use baud_rate=921600 because of hardware limitations.
You may choose divisor=3 (baud_rate=1000000) or divisor=4 (baud_rate=750000). These baud rates are not standart, but possible.
stty -F /dev/<...> 9600 -icrnl -ixon -crtscts -parenb # desired UART settings
setserial /dev/<...> spd_cust # use custom value for divisor
setserial /dev/<...> divisor 3 # set custom value for divisor
stty -F /dev/<...> 38400 # activate setserial settings
# now baud_rate is (baud_base / divisor)
IMHO, using system and stty from a C or C++ program is not the way to go (system is known as a poor security practice at first).
After reading you related questions, my advice would be to only declare in you main module, namespace or class some wrapper functions for all the features you want to use from both termios.h and asm/termios.h.
Then you define them in two different compilation units (c or c++ source files), first dealing with termios.h, the latter with asm/termios.h
If that does not work, the last way would be to carefully merge declarations from termios.h and asm/termios.h in a custom local_termios.h managed in your own sources and include it. This of course leads to non portable code, but anyway as soon as you use asm/termios.h you loose compatibility.

How can I set a custom baud rate on Linux?

I want to communicate over my serial port on Linux to a device with a non-standard-baud rate that is not defined in termios.h.
I tried the "baud rate aliasing"-method from this post, but when I execute my C-program (I’ve named it "testprogram"), Linux says "testprogram sets custom speed on ttyS0. This is deprecated."
I did some search on Google, and it seems that there is another (newer?) method to change the baud rate to a non-standard-value: On http://sourceware.org/ml/libc-help/2009-06/msg00016.html the author says that the c_flag of struct termios must be OR’d with BOTHER (=CBAUDEX | B0).
With this method the baud rates are set directly in the c_ispeed and c_ospeed-members of the struct termios. However, I don’t know how I use this method in my C program. Like the author said, there is no BOTHER defined/available when I include termios.h, so what should be done to set the baud rate this way?
How can I set the baud rate to a non-standard-value without changing the kernel?
I noticed the same thing about BOTHER not being defined. Like Jamey Sharp said, you can find it in <asm/termios.h>. Just a forewarning, I think I ran into problems including both it and the regular <termios.h> file at the same time.
Aside from that, I found with the glibc I have, it still didn't work because glibc's tcsetattr was doing the ioctl for the old-style version of struct termios which doesn't pay attention to the speed setting. I was able to set a custom speed by manually doing an ioctl with the new style termios2 struct, which should also be available by including <asm/termios.h>:
struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = 12345;
tio.c_ospeed = 12345;
ioctl(fd, TCSETS2, &tio);
You can set a custom baud rate using the stty command on Linux. For example, to set a custom baud rate of 567890 on your serial port /dev/ttyX0, use the command:
stty -F /dev/ttyX0 567890
dougg3 has this pretty much (I can't comment there). The main additional thing you need to know is the headers which don't conflict with each other but do provide the correct prototypes. The answer is
#include <stropts.h>
#include <asm/termios.h>
After that you can use dougg3's code, preferably with error checking round the ioctl() calls. You will probably need to put this in a separate .c file to the rest of your serial port code which uses the normal termios to set other parameters. Doing POSIX manipulations first, then this to set the custom speed, works fine on the built-in UART of the Raspberry Pi to get a 250k baud rate.
BOTHER appears to be available from <asm/termios.h> on Linux. Pulling the definition from there is going to be wildly non-portable, but I assume this API is non-portable anyway, so it's probably no big loss.
For Mac users (possibly also for some Linux distributions)
stty ospeed 999999
stty ispeed 999999
You can just use the normal termios header and normal termios structure (it's the same as the termios2 when using header asm/termios).
So, you open the device using open() and get a file descriptor, then use it in tcgetattr() to fill your termios structure.
Then clear CBAUD and set CBAUDEX on c_cflag.
CBAUDEX has the same value as BOTHER.
After setting this, you can set a custom baud rate using normal functions, like cfsetspeed(), specifying the desired baud rate as an integer.
There is an serial I/O chip on your motherboard's CPU (16650 UART).
This chip uses 8-bit port as control and data bus, and thus you can issue a command to it through writing to this chip through the control and data bus.
Usually, an application did the following steps on the serial port
Set baud rate, parity, encoding, flow control, and starting / ending sequence length during program start. This setup can be done via ioctl to the serial device or 'stty' command. In fact, the stty command uses ioctl to that serial device.
Write characters of data to the serial device and the driver will be writing data charaters to the UART chip through its 8-bit data bus.
In short, you can specify the baud rate only in the STTY command, and then all other options would be kept as default, and it should enough to connect to ohter devices.

Resources