-
Hello there! Recently I've decided to play with Arduino in Rust and found your awesome crate. Thank you! I have zero prior knowledge of Arduino and some Rust knowledge (maybe advanced beginner level?). That said probably it would have been wise to go with C to avoid getting stuck.. But I really wanted to do it in Rust to sharpen my Rust mojo. I am trying to build a small car and started with controlling just a single dc motor. I've managed to get everything working, however from the examples I am not sure what would be the right way to send some specific values to the digital pin to control speed. Here is the code I am using, where fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut ena = pins.d5.into_output();
let mut in1 = pins.d6.into_output();
let mut in2 = pins.d7.into_output();
in1.set_high();
in2.set_low();
// Here I would like to write some specific byte
ena.set_high();
arduino_hal::delay_ms(2000);
in1.set_low();
loop {
}
} My apologies if issue is the wrong way to ask this question. Didn't find any info about the proper channels in the readme and got no answer in official Rust discord. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
I assume by that you mean "working with Arduino C"? Can you share the working code if there is any?
This does not make any sense. A digital GPIO pin is just that - a single digital bit. It can be either 1 or 0, which means "on" or "off", also called HIGH or LOW. You are looking to encode more than one bit of information on a single wire - this is possible by sending bit-patterns over time. The exact way (=encoding) depends on what is connected, though, as the other side will need to "interpret" this bit-pattern. Assuming that you have some kind of transistor/amplifier connected to the Arduino pin which drives your motor, an easy way to "encode" information which translates to motor speed is by only keeping the signal HIGH for a percentage of the time. This is called "pulse width modulation" (PWM) and looks like this (x axis is time):
Because the motor cannot respond as fast as the signal is switching, it instead just runs at a lower speed as it keeps trying to accelerate and decelerate with the changing signal. The simplest way to archieve this would be to just write some naive code to do it: let pwm_percent = 50u8;
loop {
pin.set_high();
delay_us(pwm_percent);
pin.set_low();
delay_us(100u8 - pwm_percent);
} Note that this would produce a PWM frequency of 1/100us = 10kHz. This might not be ideal for your motor and driving amplifier circuitry. There is one problem with the code above, though: It constanly keeps the CPU busy for generating this signal - you cannot really do anything else in the meantime. Luckily, there is a solution: The MCU contains some hardware to generate PWM signals on its own - you just tell it the frequency and duty cycle (which is the "on percentage") and it'll do the rest. This is a bit more complicated to set up and as of now, As this question was asked before, I'm not going to reiterate the code here and instead just link you to #194 and #230 where some code examples are shown. If those go completely over your head, just let me know - I'll walk you through it if you are interested. |
Beta Was this translation helpful? Give feedback.
-
First of all thank you for such a detailed response! I really appreciate you explaining all this given my poorly (as I now understand) explained issue 😅
This was a miscommunication. What I meant is that I've been able to make a dc motor spin with the Rust code presented above. After your explanation it became clear to me that
Thank you for the links! I've seen similar code in examples with servo motors, but apparently got confused and thought it is not what I actually wanted. What I was expecting is to be able to just pass some number between 0 and 255 to a pin method. Something like Your explanation made me realize this confusion. Then more googling led me to learn about timer interrupts. You are right - those examples are a bit over my head now. However it feels they are making more and more sense as I read about timer interrupts. So my intention is to try and figure out everything by myself if possible first. Not sure if the issue should be open or closed for now. Anyway big thanks for your help! |
Beta Was this translation helpful? Give feedback.
-
Looks like I was able to figure everything out. Now I am able to control the speed of both of my motors using single timer. Does it look sane? #[arduino_hal::entry]
fn main() -> ! {
let dp = arduino_hal::Peripherals::take().unwrap();
let pins = arduino_hal::pins!(dp);
let mut left_forward = pins.d4.into_output();
let mut left_backward = pins.d2.into_output();
let mut right_backward = pins.d12.into_output();
let mut right_forward = pins.d13.into_output();
let tc2 = dp.TC2;
tc2.tccr2a.write(|w| w.wgm2().pwm_fast().com2a().match_clear().com2b().match_clear());
tc2.tccr2b.write(|w| w.cs2().prescale_1024());
// left speed
pins.d3.into_output();
// right speed
pins.d11.into_output();
left_forward.set_high();
left_backward.set_low();
right_forward.set_high();
right_backward.set_low();
let duty = 127u8;
tc2.ocr2b.write(|w| unsafe { w.bits(duty) });
tc2.ocr2a.write(|w| unsafe { w.bits(duty) });
loop {
}
} Surprisingly If I am not doing something terribly wrong than the issue can be closed. Thanks again for your help! |
Beta Was this translation helpful? Give feedback.
-
Glad you got it working! Your code looks good, nothing wrong there.
There is more to this: Servos are also controlled with a PWM-encoded signal, but differently - For your motor, you just vary the duty cycle between 0% and 100% and the frequency is largely not important. Most of the simple servo motors want a PWM signal of exactly 50 Hz and a pulse length varying between 1ms (left stop, =5%) and 2ms (right stop, =10%). Check out this diagram on Wikimedia.
Not that this code involves a timer, but not a timer interrupt. The timer runs next to the CPU, just doing its thing. The interrupt would come into play when the timer is instructed to "interrupt" the CPU whenever it overflows. This means the CPU would then stop what it is doing, execute an interrupt service routine (ISR), and then return to where it left off. This is also really useful, but not needed here :)
I would assume the pulses get so short that they are filtered out by your drive circuitry - but if it is working well with 1024 factor, there is no harm in using it.
Feel free to close the issue once your question is answered! |
Beta Was this translation helpful? Give feedback.
I assume by that you mean "working with Arduino C"? Can you share the working code if there is any?
This does not make any sense. A digital GPIO pin is just that - a single digital bit. It can be either 1 or 0, which means "on" or "off", also called HIGH or LOW.
You are looking to encode more than one bit of information on a single wire - this is possible by sending bit-patterns over time. The exact way (=encoding) depends on what is connected, though, as the other side will need to "interpret" this bit-pattern.
Assuming that you have some kind of transi…