Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
11cxx_async_can.cxx
Go to the documentation of this file.
1
35#ifdef TARGET_LPC11Cxx
36
37#include "11CXX_rom_driver_CAN.h"
38#include "nmranet_config.h"
39
41#include "utils/Hub.hxx"
42
43typedef struct _ROM
44{
45 const unsigned p_usbd;
46 const unsigned p_clib;
47 const CAND *pCAND;
48} ROM;
49
51const ROM *const *const rom = (ROM **)0x1fff1ff8;
52
53namespace lpc11cxx
54{
55
56#define FIRST_TX_OBJ 1
57#define FIRST_RX_OBJ 16
58
59class CanPipeMember;
60class CanRxFlow;
61
62/* Static instances of the CAN drivers */
63CanPipeMember *g_tx_instance = nullptr;
64CanRxFlow *g_rx_instance = nullptr;
65CanHubFlow *g_pipe = nullptr;
66
67class CanPipeMember : public CanHubPort
68{
69public:
70 CanPipeMember(Service *s)
71 : CanHubPort(s)
72 , freeTxBuffers_(0xFFFFFFFF)
73 , frameToWrite_(nullptr)
74 , bufferFull_(0)
75 {
76 }
77
78 Action entry() OVERRIDE
79 {
80 AtomicHolder h(this);
81 HASSERT(!frameToWrite_);
82 frameToWrite_ = message()->data()->mutable_frame();
83 return call_immediately(STATE(try_send));
84 }
85
86 Action try_send()
87 {
88 HASSERT(frameToWrite_);
89 const struct can_frame *frame = nullptr;
90 uint8_t buf_num = FIRST_TX_OBJ;
91 {
92 AtomicHolder h(this);
93
94 // Looks for a free tx buffer.
95 while (buf_num < FIRST_RX_OBJ &&
96 (!(freeTxBuffers_ & (1 << buf_num))))
97 {
98 buf_num++;
99 }
100 if (buf_num >= FIRST_RX_OBJ)
101 {
102 // Wait for ISR to wake us up.
103 bufferFull_ = 1;
104 return wait();
105 }
106
107 freeTxBuffers_ &= ~(1 << buf_num);
108
109 // We decided to send the frame.
110 frame = frameToWrite_;
111 frameToWrite_ = nullptr; // no callbacks from ISR.
112 }
113 CAN_MSG_OBJ msg_obj;
114 msg_obj.msgobj = buf_num;
115 msg_obj.mode_id = frame->can_id |
116 (frame->can_rtr ? CAN_MSGOBJ_RTR : 0) |
117 (frame->can_eff ? CAN_MSGOBJ_EXT : 0);
118 msg_obj.mask = 0x0;
119 msg_obj.dlc = frame->can_dlc;
120 memcpy(msg_obj.data, frame->data, frame->can_dlc);
121 (*rom)->pCAND->can_transmit(&msg_obj);
122
123 // The incoming frame is no longer needed.
124 return release_and_exit();
125 }
126
127 void TxFinishedFromIsr(uint8_t buf_num)
128 {
129 HASSERT(!(freeTxBuffers_ & (1 << buf_num)));
130 freeTxBuffers_ |= (1 << buf_num);
131 if (frameToWrite_ && bufferFull_)
132 {
133 bufferFull_ = 0;
134 service()->executor()->add_from_isr(this, priority());
135 }
136 }
137
138private:
139 uint32_t freeTxBuffers_;
140 const struct can_frame *frameToWrite_;
141 // 1 if we are waiting for a free tx buffer.
142 unsigned bufferFull_ : 1;
143};
144
145class CanRxFlow : public StateFlowBase, private Atomic
146{
147public:
148 CanRxFlow(Service *s)
149 : StateFlowBase(s)
150 , bufFull_(0)
151 , rxPending_(1)
152 , frameLost_(0)
153 {
154 /* Configures msgobj NN to receive all extended frames. */
155 CAN_MSG_OBJ msg_obj;
156 msg_obj.msgobj = FIRST_RX_OBJ;
157 msg_obj.mode_id = 0x000 | CAN_MSGOBJ_EXT;
158 msg_obj.mask = 0x000;
159 msg_obj.dlc = 0x000;
160 (*rom)->pCAND->config_rxmsgobj(&msg_obj);
161 start_flow(STATE(wait_or_copy));
162 }
163
164 // Callback inside the ISR context. @param buf_num is the number of the
165 // message object in the hardware.
166 void isr(uint8_t buf_num)
167 {
168 if (bufFull_)
169 {
170 // Another interrupt came in before we have cleared the buffer.
171 frameLost_ = 1;
172 return;
173 }
174 else
175 {
176 bufFull_ = 1;
177 }
178 if (!rxPending_)
179 {
180 rxPending_ = 1;
181 // highest priority
182 service()->executor()->add_from_isr(this, 0);
183 }
184 }
185
186 Action wait_or_copy()
187 {
188 {
189 AtomicHolder h(this);
190 if (!bufFull_)
191 {
192 rxPending_ = 0;
193 return wait();
194 }
195 }
196 return allocate_and_call(g_pipe, STATE(allocation_complete));
197 }
198
199 // Scheduled by the ISR when the frame has arrived.
200 Action allocation_complete()
201 {
202 HASSERT(bufFull_);
203 auto *b = get_allocation_result(g_pipe);
204 {
205 AtomicHolder h(this);
206 copy_hardware_frame(FIRST_RX_OBJ, b->data()->mutable_frame());
207 }
208 b->data()->skipMember_ = g_tx_instance;
209 g_pipe->send(b);
210 return call_immediately(STATE(wait_or_copy));
211 }
212
213private:
214 // Has to run either in ISR or in a critical section. Retrieves a hardware
215 // buffer and copies it to the class-local can_frame.
216 void copy_hardware_frame(uint8_t buf_num, struct can_frame* frame)
217 {
218 HASSERT(bufFull_);
219 CAN_MSG_OBJ msg_obj;
220 /* Determine which CAN message has been received */
221 msg_obj.msgobj = buf_num;
222
223 /* Now load up the msg_obj structure with the CAN message */
224 (*rom)->pCAND->can_receive(&msg_obj);
225
226 // Here we need to be in a critical section; otherwise another CAN
227 // interrupt might come in between these two calls.
228 bufFull_ = 0;
229 rxPending_ = 0;
230
231 frame->can_id = msg_obj.mode_id & ((1 << 29) - 1);
232 // JMRI crashes the node here.
233 // HASSERT(frame->can_id & 0xfff);
234 frame->can_rtr = (msg_obj.mode_id & CAN_MSGOBJ_RTR) ? 1 : 0;
235 frame->can_eff = (msg_obj.mode_id & CAN_MSGOBJ_EXT) ? 1 : 0;
236 frame->can_err = 0;
237 frame->can_dlc = msg_obj.dlc;
238 memcpy(frame->data, msg_obj.data, msg_obj.dlc);
239 }
240
241 // 1 if the hardware object for RX has a frame.
242 unsigned bufFull_ : 1;
243 // 1 if the state flow is in operation, 0 if the state flow can be
244 // scheduled.
245 unsigned rxPending_ : 1;
246 // 1 if we have lost a frame.
247 unsigned frameLost_ : 1;
248};
249
253void CAN_rx(uint8_t msg_obj_num)
254{
255 HASSERT(g_rx_instance);
256 g_rx_instance->isr(msg_obj_num);
257 // We always assume there is someone woken up. This is not nice.
258 portYIELD();
259}
260
264void CAN_tx(uint8_t msg_obj_num)
265{
266 HASSERT(g_tx_instance);
267 g_tx_instance->TxFinishedFromIsr(msg_obj_num);
268 // We always assume there is someone woken up. This is not nice.
269 portYIELD();
270}
271
275void CAN_error(uint32_t error_info)
276{
277 return;
278}
279
281static const CAN_CALLBACKS callbacks = {CAN_rx, CAN_tx, CAN_error, NULL,
282 NULL, NULL, NULL, NULL, };
283
285static const uint32_t ClkInitTable125[2] = {0x00000000UL, // CANCLKDIV
286 0x00001C57UL // CAN_BTR
287};
288
290static const uint32_t ClkInitTable250[2] = {0x00000000UL, // CANCLKDIV
291 0x00001C4BUL // CAN_BTR
292};
293
294void CreateCanDriver(CanHubFlow *parent)
295{
296 if (config_nmranet_can_bitrate() == 250000) {
297 /* Initialize the CAN controller */
298 (*rom)->pCAND->init_can((uint32_t *)&ClkInitTable250[0], 1);
299 } else if ((config_nmranet_can_bitrate() == 125000)) {
300 /* Initialize the CAN controller */
301 (*rom)->pCAND->init_can((uint32_t *)&ClkInitTable125[0], 1);
302 } else {
303 DIE("Unknown can bitrate.");
304 }
305 /* Configure the CAN callback functions */
306 (*rom)->pCAND->config_calb((CAN_CALLBACKS *)&callbacks);
307
308 g_pipe = parent;
309 g_rx_instance = new CanRxFlow(parent->service());
310 g_tx_instance = new CanPipeMember(parent->service());
311 parent->register_port(g_tx_instance);
312
313 /* Enable the CAN Interrupt */
314 NVIC_SetPriority(CAN_IRQn, 1);
315 NVIC_EnableIRQ(CAN_IRQn);
316}
317
318} // namespace lpc11cxx
319
320extern "C" {
323void CAN_IRQHandler(void)
324{
325 (*rom)->pCAND->isr();
326}
327}
328
329#else
330#error You need to define TARGET_LPC11Cxx if you want to compiple its rom driver.
331#endif
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
void register_port(port_type *port)
Adds a new port.
Definition Hub.hxx:167
Collection of related state machines that pend on incoming messages.
Base class for state machines.
Service * service()
Return a pointer to the service I am bound to.
State flow with a given typed input queue.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
All API functions of the ROM can driver.
Specifies the callbacks from the CAN stack to the application.
CAN message object.