Skip to content
Snippets Groups Projects

Queued uart can example

Merged A Sundbom requested to merge Queued-UART-CAN-example into master
1 file
+ 388
0
Compare changes
  • Side-by-side
  • Inline
+ 388
0
//! # CAN Bridge Example
//! Data recieved at CAN0 id 0x4B0 is sent to UART3 (19200 baud)
//! Data recieved from UART1 is sent as single bytes on CAN Id 0x530
//!
//! Tested with UART loopback (TX connected to Rx)
//! # Usage
//! On the host machine, send messages to the bridge using
//! ```
//! cansend slcan0 4b0#6162636465 ->
//!
//! candump:
//! slcan0 4B0 [5] 61 62 63 64 65
//! slcan0 530 [1] 61
//! slcan0 530 [1] 62
//! slcan0 530 [1] 63
//! slcan0 530 [1] 64
//! slcan0 530 [1] 65
//! ```
//!
//! PuTTy listen on Uart
//! abcde
//!
#![no_main]
#![no_std]
#[allow(unused_extern_crates)]
extern crate panic_halt;
use cortex_m::asm;
use rtfm::app;
use heapless::{
self,
consts::U32,
spsc::{Consumer, Producer, Queue},
};
use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
use lpc43xx_hal::{
io::pin::{
split_ports,
Gpio,
IntoGpioPin,
Input,
Output,
OpenDrain,
OpenDrainPin,
Pin,
PinSplit,
U1, U4, U5, U6, U3, U2, U10, Uninit},
can::{CanBus, MsgIface, MsgObj},
serial::{Read, Serial, Write},
timer::{self, EMC0_A, MR0I_A, MR0R_A, MR0S_A, NoMatchPin, NoCapturePin},
pac::{
self,
c_can1::{
stat::LEC_A,
cntl::{DAR_A, EIE_A, IE_A, SIE_A},
},
usart0::{
lcr::{BC_A, DLAB_A, PE_A, PS_A, SBS_A, WLS_A},
iir::{INTID_A, INTSTATUS_A},
},
SCU,
},
};
type Test0Timer = timer::Timer<pac::TIMER0, NoMatchPin, NoMatchPin, NoMatchPin, NoMatchPin, NoCapturePin, NoCapturePin, NoCapturePin, NoCapturePin>;
type CanBridge = CanBus<pac::C_CAN0, heapless::consts::U32>;
type UartBridge = Serial<pac::USART0>;
static BLINK_PERIOD: u32 = 30384;
static STRING: [u8; 13] = [
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0x20,
];
/// Transmit data over UART
fn send_over_can(can: &mut CanBridge, tx_obj: &mut MsgObj, val: u8) {
// Setup CAN TX data
tx_obj.data_a1(val, 0);
// Set TX status
tx_obj.msgval(true)
.newdat(true)
.mctrl_txrqst(true);
// Transfer object to message RAM
can.transfer_obj(MsgIface::Iface1, 10, &tx_obj);
}
#[app(device = lpc43xx_pac)]
const APP: () = {
static mut UART: UartBridge = ();
static mut CAN_0: CanBridge = ();
static mut TIMER: Test0Timer = ();
static mut XRAY_TX_OBJ: MsgObj = ();
static mut TO_XRAY_QUEUE: Queue<u8, heapless::consts::U64> = ();
static mut FROM_XRAY_QUEUE: Queue<u8, heapless::consts::U64> = ();
static mut IDX: u8 = 0;
#[init]
fn init() -> init::LateResources {
// ============= TX/RX Queues ==============
let to_xray_queue: Queue<u8, heapless::consts::U64> = Queue::new();
let from_xray_queue: Queue<u8, heapless::consts::U64> = Queue::new();
// =========================================
// ================= Clock =================
// TODO: move me!
setup_clk(&device.CGU);
// Set PLL1 on UART0
unsafe {
device.CGU.base_uart0_clk.write(|w| w.clk_sel().bits(0x09));
}
// =========================================
// ================= Pins ==================
device.SCU.sfsp3_2.write(|w| w.mode().function_2()); //P1_17 CAN0_TD
device.SCU.sfsp3_1.write(|w| w //P1_18 CAN0_RD
.mode().function_2()
.ezi().enable_input_buffer()
.zif().disable_input_glitch_filter() );
// device.SCU.sfsp1_17.write(|w| w.mode().function_5()); //P1_17 CAN0_TD
// device.SCU.sfsp1_18.write(|w| w //P1_18 CAN0_RD
// .mode().function_5()
// .ezi().enable_input_buffer()
// .zif().disable_input_glitch_filter() );
// Configure Serial
let ports = split_ports(device.SCU, device.GPIO_PORT);
let port_7 = ports
.port_7
.split_pins();
let mut can_stby = port_7.p7_4.into_gpio().into_output();
can_stby.set_low();
let port_9 = ports
.port_9
.split_pins();
let uart0_tx = port_9.p9_5;
let uart0_rx = port_9.p9_6;
// =========================================
// ================= Timer =================
let mut t = Test0Timer::new_no_pins(device.TIMER0);
t.start_timer(false);
t.set_match(0, BLINK_PERIOD);
t.set_prescaler(2048, Some(0));
unsafe {
t.mr0(MR0I_A::INTERRUPT, MR0R_A::RESET, MR0S_A::NO_STOP, EMC0_A::NOP);
}
t.start_timer(true);
// =========================================
// ================== CAN ==================
let mut can_0 = CanBridge::new(device.C_CAN0);
can_0.initialize(
12_000_000,
500000,
IE_A::ENABLE_CAN_INTERRUPTS,
SIE_A::DISABLE_STATUS_CHANGE_INTERRUPTS,
EIE_A::ENABLE_ERROR_INTERRUPTS,
DAR_A::ENABLED,
false,
);
// == Setup message objects
let power_control_rx_obj = MsgObj::new_rx_obj(true, 0x6b0, 0x2, 0xfffffff);
can_0.transfer_obj(MsgIface::Iface1, 13, &power_control_rx_obj);
let to_x_ray_rx_obj = MsgObj::new_rx_obj(true, 0x4b0, 0x8, 0xfffffff);
can_0.transfer_obj(MsgIface::Iface1, 9, &to_x_ray_rx_obj);
let mut from_x_ray_tx_obj = MsgObj::new_tx_obj(true, 0x530, 0x1, 0, 0);
from_x_ray_tx_obj.data_a(true);
from_x_ray_tx_obj.data_b(true);
// =========================================
// ================ USART ==================
let mut serial =
UartBridge::new(device.USART0, uart0_tx, uart0_rx, 0x98, 0x02, 0x0); //.. DLL=0x41, DLM, Trigger)
//DLL 0x91, DLH 0x01 PCLK 126MHz -> 19200 baud
//DLL 0x98, DLH 0x02, PCLK 204MHz -> 19200 baud
serial.lcr(
WLS_A::_8_BIT_CHARACTER_LENGTH,
SBS_A::_1_STOP_BIT,
PE_A::DISABLE_PARITY_GENERERATION,
PS_A::ODD_PARITY,
BC_A::DISABLED,
DLAB_A::DISABLED,
);
serial.ier(true, true, false, false, false);
serial.reset_rx_fifo();
serial.reset_tx_fifo();
// =========================================
init::LateResources {
TO_XRAY_QUEUE: to_xray_queue,
FROM_XRAY_QUEUE: from_xray_queue,
UART: serial,
TIMER: t,
CAN_0: can_0,
XRAY_TX_OBJ: from_x_ray_tx_obj,
}
}
#[interrupt(resources = [CAN_0, XRAY_TX_OBJ, TO_XRAY_QUEUE, IDX, TIMER, UART])]
fn TIMER0() {
*resources.IDX = (*resources.IDX + 1) % 13;
//Clear interrupt
resources
.TIMER
.clear_interrupt(timer::InterruptType::MatchChannel, 0);
}
#[interrupt(resources = [CAN_0, XRAY_TX_OBJ, FROM_XRAY_QUEUE, TO_XRAY_QUEUE, UART])]
fn USART0() {
// Get interrupt info
let (int_status, int_id) = resources.UART.read_iir();
match int_id {
INTID_A::RDA => {
// Data available
let queue_was_empty = resources.FROM_XRAY_QUEUE.len() == 0;
// Read a single byte from UART and place on CAN TX queue
let val = resources.UART.read().unwrap();
match resources.FROM_XRAY_QUEUE.enqueue(val) {
Ok(_) => (),
Err(_) => asm::bkpt(),
}
if queue_was_empty {
match resources.FROM_XRAY_QUEUE.dequeue() {
None => (),
Some(val) => send_over_can(&mut *resources.CAN_0, &mut *resources.XRAY_TX_OBJ, val),
}
}
},
INTID_A::THRE => {
// USART TX queue ready for data
if resources.TO_XRAY_QUEUE.len() > 0 {
match resources.TO_XRAY_QUEUE.dequeue() {
None => (),
Some(i) => {
resources
.UART
.write(i)
.unwrap();
}
}
}
},
_ => (),
}
}
#[interrupt(resources = [CAN_0, XRAY_TX_OBJ, UART, TO_XRAY_QUEUE, FROM_XRAY_QUEUE])]
fn C_CAN0() {
// Get interrupt ID
let intid: u16 = resources.CAN_0.read_intid();
match resources.CAN_0.receive_obj(MsgIface::Iface1) {
Ok(res) => match intid{
9 => {
// Receive from UART
let data_length = res.len();
let queue_was_empty = resources.TO_XRAY_QUEUE.len() == 0;
for data_num in 0..data_length {
//0..5 -> 0,1,2,3,4
let the_byte = res[data_num];
// Send byte from CAN to UART
match resources.TO_XRAY_QUEUE.enqueue(the_byte) {
Ok(_) => (),
Err(_) => asm::bkpt(),
}
}
// Dequeue and write to UART
if queue_was_empty {
match resources.TO_XRAY_QUEUE.dequeue() {
None => (),
Some(i) => {
resources
.UART
.write(i)
.unwrap();
}
}
}
},
10 => {
// Send to UART
if resources.FROM_XRAY_QUEUE.len() > 0 {
match resources.FROM_XRAY_QUEUE.dequeue() {
None => (),
Some(val) => send_over_can(&mut *resources.CAN_0, &mut *resources.XRAY_TX_OBJ, val),
}
}
},
_ => (),
}
Err(u) => match u {
Ok(status) => {
let lec = resources.CAN_0.can.stat.read().lec().variant();
match lec{
LEC_A::ACKERROR_THE_MESSAG =>{
asm::bkpt()
},
LEC_A::NO_ERROR =>{},
_ =>{
asm::bkpt()
}
}
},
Err(err) => (),
},
}
}
};
pub fn setup_clk(cgu: &lpc43xx_pac::CGU){
unsafe {
// 204 MHz setup, direct mode
// Select the IRC as BASE_M4_CLK source
cgu.base_m4_clk.write(|w| w.clk_sel().bits(0x1));
// Enable the crystal oscillator
cgu.xtal_osc_ctrl.modify(|_, w| w.hf().bit(false));
cgu.xtal_osc_ctrl.modify(|_, w| w.enable().bit(false));
// Wait 250us
for i in 0..3_000 {
core::ptr::read_volatile(&i);
}
cgu.pll1_ctrl.write(|w| {
w.pd()
.bit(false)
.clk_sel()
.bits(0x6) // Select the crystal oscillator as clock source
.msel()
.bits(16) //16 => M=17
.nsel()
.bits(0) // 0 => N=1
.psel()
.bits(0) // 0 => P=1
.fbsel()
.bit(true) //true => output feedback
.bypass()
.bit(false) // 0=> normal PLL
.direct()
.bit(true)// 0=> use post-divider
//=> no post divider => direct mode
// Clk = M * COSC/N
// CCO = CLK = 204 MHz
//156MHz < CCO < 320 MHz => OK
});
// Wait for the PLL to lock
while !cgu.pll1_stat.read().lock().bit() {}
// Select PLL1 as base_m4_clk source
cgu.base_m4_clk.modify(|_, w| w.clk_sel().bits(0x09));
// Wait for 50us
for i in 0..600 {
core::ptr::read_volatile(&i);
}
}
}
Loading