-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should setting baud rate for serial communication be part of this hal? #79
Comments
One possible solution could be to introduce a Then i could auto implement the dynamixel interface for |
Hm, so you're using the I do agree that there're some uses for on-the-fly configuration changes, so I'd be very much in favour for adding this; however we'd need to check for implementation feasibility. There might be chips which only allow such changes through full re-initialisation which may require resources which are not owned but only borrowed. |
currently I've only implemented it for this serial trait. I've tested it with an ftdi like rs485 usb dongle. You could use a RS-485 transceiver directly connected to some uart, meaning it's no need for converting to RS-232. Since this is SW and we don't really care about things as differential-lines and voltage levels the only real difference is that with RS-485 you cannot read and write at the same time. |
@kjetilkjeka That only works with dedicated hardware. When using a MCU and a RS485 transceiver you will need a "Driver Enable" signal. Expecting the user to manage that manually is not very user friendly and some chips, e.g. STM32 MCUs have hardware support for it which is especially handy if one wants to use async IO. |
The simple way is to connect tx together with the enable line. Then you will get dominant 1s and recessive 0s. That is if the mcu doesn't drive the enable line for you. I would expect the write/read implementation to handle this. If they don't they're pretty much useless. I'm not suggesting such a thing. Edit: i dont expect the hal to assume what kind of hw you've connected to the uart. This mist be handled in hw or at the initialization. |
My point is that you'll need specific setup for that case. You can't just use the regular serial impl and it'll work out automagically for RS-485 as well. |
Is this simply an observation, an argument against adding the set baud method or an argument against using the serial traits for RS485? Perhaps I'm wrong, but i see the serial traits as a generic serial protocol. Much like the stdlib Read/Write traits can be used on network sockets, files, rs485, etc. I expect the serial traits to work on uart, usart, RS232, RS485, etc. Even for the simple uart case you will need "specific setup" as voltage levels might not be the same for the commuinicating units. |
It's a somewhat unrelated observation that the serial trait may be lacking more details than just the baudrate selection to be useful for various serial protocols. |
I'm in favor of keeping the base However, I can also see the need for changing the baud rate (and possibly other parameters) at runtime. I'm going to make the same suggestion we've talked about elsewhere: Add a separate, but composable trait (maybe trait Configure {
type Freq;
fn set_baud_rate<T>(&mut self, baud_rate: T) where T: Into<self::Freq>;
} This doesn't break existing implementations, and leaves it to implementers to decide whether such configuration makes sense for their use-case. |
In favor of what @austinglaser suggests with the following additions.
pub trait ConfigureBaud {
type BaudRate: TryFrom<u32>;
type Error;
fn set_baud_rate(&mut self, baud_rate: Self::BaudRate) -> Result<(), Self::Error>;
} Another alternative could be to distinguish between interfaces supporting weird and non-weird bauds: pub enum StandardBaudRate {
B1200,
B2400,
B4800,
B9600,
B19200,
B38400,
B57600,
B115200,
}
pub trait ConfigureBaud {
type BaudRate: From<StandardBaudRate> + TryFrom<u32>;
fn set_baud_rate(&mut self, baud_rate: Self::BaudRate);
}
pub trait TryConfigureBaud: ConfigureBaud {
type Error;
fn try_set_baud_rate(&mut self, baud_rate: u32) -> Result<(), Self::Error>;
} |
This is a rather messy topic and I don't have good solutions in my head at this time. Your "standard" baud rates are ending far too low, 115200 is rather pedestrian nowadays. Also even "standard" baud rates can very often not be represented exactly and also often this depends on the clock the UART or even the whole system is running at. For me this means it either should be fallible or there must be a way to determine whether a baud rate can be supported or what the next closest supported value would be. |
These are
How big of a problem is this? I would be happy with the "best effort" and "you should probably have your impl fail if it's to far away (2-3% or something)"
I guess the solution here is, If the implementation don't know what kind of freq the uart clock runs at this Trait should (read: can) not be implemented. This alternative would supporting getting an indication of how much off the #[derive(Debug)]
pub struct UnrepresentableBaud;
pub trait BaudRate: Into<f32>{
//// Try to crate a `BaudRate` with less than 2% error.
fn create_approximately(baud_rate: u32) -> Reult<(), UnrepresentableBaud>;
//// Create the closest `BaudRate` possible to `baud_rate`.
fn closest(baud_rate: u32) -> Self;
}
pub trait ConfigureBaud {
type BaudRate: BaudRate;
fn set_baud_rate(&mut self, baud_rate: Self::BaudRate);
} |
Huh?
Even the ominous ATMega328 officially supports 2 MBaud...
It's a big problem. I've seen people clock their MCUs specifically for proper serial communication and if this is all automagic then it will cause all sorts of surprises.
Well, often you pass in the relevant clocks when initialising the UART, e.g. from the
So the application sets up the clocks and passes it in so there's no way of knowing whether the baud rate is compatible with the current clocks and now coming back to this topic and my previous concern: If you reconfigure the baud rate on the fly you will most likely have to pass in the very same information again that was used during construction to make the proper calculation (and some chips actually require that you turn the peripheral off again to make such a change) but this is all implementation dependant... |
So it looks like the temporary spec sheet currently looks:
If this is the case, the reconfigurable baud rate should probably not be implemented (or handle these special concerns in software)
I guess what you mean is "no way of knowing statically"? As the interface could remember its clock frequency. Then you could simply ask the interface whether the new clock freuquency is compatible or not. For instance with: #[derive(Debug)]
pub struct UnrepresentableBaud;
pub trait ConfigureBaud {
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<(), UnrepresentableBaud>;
} |
The problem with "remembering" is, that the clock might actually change... At the moment, the most compatible and safest way to change the baud rate would be to drop and reinitialise the serial interface. |
If the clock changes the current baud rate would also change, therefore needing interaction with the serial implementation (hw driver), this should be used to update the remembered value. If, for some weird reason, the baud rate is controlled by unkown external factors the
Since initialization is not the responsibility of this hal this would make it impossible to implement the dynamixel protocol generically for devices supporting this hal. @therealprof if an implementation was made adhering to my "temp spec sheet" would that be something you would consider to include in the hal (and are there any other concerns that must be made), or would you prefer keeping baud rate configuration out of the hal at the current being? |
True.
As I mentioned before I think it would be useful to have and thus don't have any general objection to include it. However we need to ensure that the traits are designed in a way that they actually can be implemented in a safe and sane way on common hardware and at the moment I don't see that how that would be possible. |
Another relevant problem:More than one device that uses an UART interface supports changing the speed on the fly. Usually they start at a low baud rate, and then you can select a higher one by sending a command via the UART. After that you need to switch your end to the higher baud rate too. Currently there is no way to do this with an embedded-hal serial driver. |
Somewhat related: mpu9250 (accelerometer & gyroscope sensors) allows using SPI configured at 1Mhz to read/write configuration register, but up to 20Mhz to retrieve readings. To account for that, we've added ability reinit spi after initialization, but it could be easier if embedded-hal provided methods for updating configuration. |
@little-arhat How would you do this safely? A method on an |
@therealprof that what this issue is about, I guess: should embedded-hal include methods like |
@little-arhat And my question was: Are those methods even (safely) implementable? I have strong doubts that they are (in general at least)... |
@therealprof I don't know, but do they have to be safe? For example, this hal implementation has buncf of On the other hand, stm32 HAL doesn't provide such functions. |
@little-arhat Anyway my point is that for a "safe" implementation you actually need to have exclusive access to the resources you're changing to not stomp on someone elses feet. Usually that means that you have to provide mutable references to every resource required to set up the peripheral in the first place which is typically very unwieldy and thus not expected to happen in the middle of a running application but during initialisation. Also due to the very different nature of the vendor peripherals this is not something that can be easily abstracted over in the vendor independent way which makes it highly unsuitable for If someone can show a convincing demonstration that this can be generically defined in a trait and usefully implemented for any real hardware I'd be more than happy to help turn it into an extension trait. |
I'd like to bring this back up. I have a device that does auto baudrate detection, but only up to 115200 bps, so in order to run it with a higher baud rate, one would have to:
I would like this logic to be contained within the drive crate, thus i need a way to reconfigure the baud rate after initial initialization of the UART. |
How does a trait like this look? pub trait ConfigureBaud {
type BaudRate;
type Error;
fn set_baud_rate(&mut self, baud_rate: BaudRate) -> Result<(), self::Error>;
} Many of the current hal's have their own type for baudrates, so giving them the opportunity to use them makes sense and gives them the responsibility of supporting 'weird' rates. |
For e.g. HC-12 serial transceiver module, this would make it possible to change the baud rate (which the module requires for supporting all modes). |
Another case in point is using a USART as 1-wire Bus "master". There you need to frequently change the baud rate in order to generate the correct bit patterns. I think the trait proposal by @unizippro looks good -- note that the In addition, I suggest to make the function return |
pub trait ConfigureBaud {
type BaudRate;
type Error;
fn set_baud_rate(&mut self, baud_rate: BaudRate) -> Result<(), self::Error>;
} seems broadly good to me.
if you would like to contribute this i would suggest going forward with an |
I'd just make baudrate an Having an unconstrained |
I have to agree with @ryankurte that this should be fleshed out in a demonstrator PR. I still have strong doubts this is generally implementable without gnarly hacks but would love to be proven wrong. |
I implemented a similar trait as PoC based on the stm32f1xx-hal. I had to use the unconstrained (custom) BaudRate type to force the user to go through a function which calculates the correct register value from the current clocks. The Serial in stm32f1xx-hal is mostly a zero-sized type and does not hold a reference to the (frozen) clocks. It looks roughly like this: pub trait AsyncSetBaudRate {
type Error: Debug;
type BaudRate;
fn poll_set_baud_rate(
&'_ mut self,
rate: &Self::BaudRate,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>>;
}
pub trait BaudRateGenerator<T> {
fn calc_baud_rate(&self, bps: u32) -> T;
}
#[derive(Clone, Copy)]
pub struct BaudRateConfig {
config: serial::Config, // serial from stm32f1xx_hal
clocks: rcc::Clocks, // rcc from stm32f1xx_hal
}
pub struct BaudRateFromClocks<'x>(pub &'x rcc::Clocks);
impl<'x> usart::BaudRateGenerator<BaudRateConfig> for BaudRateFromClocks<'x> {
fn calc_baud_rate(&self, bps: u32) -> BaudRateConfig {
BaudRateConfig {
config: serial::Config::default().baudrate(bps.bps()),
clocks: *self.0,
}
}
} (sorry, I'm a bit in a hurry right now so no proper minimal test code) I'm not convinced this is the best design (for instance, this is not actually calculating the BRR register value and storing it in BaudRate, but instead only putting the necessary values to call To improve the implementation, I would've had to modify stm32f1xx_hal to support setting only the baud rate and to provide a wrapper type around the register's integer type in order to allow precalculating that value from frozen clocks. Without that, the trait would indeed force the Serial implementations to know about the current clock rates, which sounds painful at least. |
If calling the trait needs calling HAL-specific methods/types, it can't be used from HAL-independent drivers. With this design,
embassy-stm32 stores the current clocks in a global, so Uart/Spi/I2c/etc have access to it at all times. WIth that design it's possible (and not painful at all) to implement a trait like |
79: Update changelog r=ryankurte a=eldruin Co-authored-by: Diego Barrios Romero <[email protected]>
I believe some way to set baud rate for the serial traits is needed.
Relevant problem
Dynamixel servos comes with two different RS-485 protocols that have some difference in support of baud rate. And even if every servo on your RS-485 bus uses the same protocol there should be nothing stopping you from configuring them with different baud rates. This require setting baud rate before communicating with a servo.
This is currently the biggest blocker for having the dynamixel library work for every embedded-hal implementation out of the box (as it currently does with adapters supporing serialport)
The text was updated successfully, but these errors were encountered: