UdonLibrary 1.0.0
機械システム研究部 C++ ライブラリ
読み取り中…
検索中…
一致する文字列を見つけられません
CanBusSpiPico.hpp
[詳解]
1//
2// CAN通信 Raspberry Pi Pico用バスクラス 実装部
3//
4// Copyright (c) 2022-2024 udonrobo
5//
6
7#pragma once
8
9namespace Udon
10{
13 inline CanBusSpi::CanBusSpi(const Config& config = {})
14 : config(config)
15 , bus(/* spi_inst_t* CHANNEL */ config.channel,
16 /* uint8_t CS_PIN */ config.cs,
17 /* uint32_t _SPI_CLOCK */ config.spiClock)
18 {
19 }
20
23 inline void CanBusSpi::begin()
24 {
25 (void)spi_init(/* spi_inst_t* spi */ config.channel,
26 /* uint baudrate */ config.spiClock);
27
28 spi_set_format(/* spi_inst_t* spi */ config.channel,
29 /* uint data_bits */ 8,
30 /* spi_cpol_t cpol */ SPI_CPOL_0,
31 /* spi_cpha_t cpha */ SPI_CPHA_0,
32 /* spi_order_t order */ SPI_MSB_FIRST);
33
34 spi_set_slave(/* spi_inst_t* spi */ config.channel,
35 /* bool slave */ false);
36
37 gpio_set_function(config.mosi, GPIO_FUNC_SPI);
38 gpio_set_function(config.miso, GPIO_FUNC_SPI);
39 gpio_set_function(config.sck, GPIO_FUNC_SPI);
40
41 gpio_init(config.cs);
42 gpio_set_dir(config.cs, true);
43 gpio_put(config.cs, true);
44
45
46 bus.reset();
47
48 // 受信開始
49 if (const auto rxSize = rxNodes.size())
50 {
51 // 割り込み設定
52 pinMode(config.interrupt, INPUT);
53
54 attachInterruptParam(
55 digitalPinToInterrupt(config.interrupt),
56 [](void* p)
57 {
58 auto self = static_cast<CanBusSpi*>(p);
59 can_frame msg;
60 if (self->bus.readMessage(&msg) == MCP2515::ERROR_OK)
61 {
62 self->rxBuffer.push_back(msg); // 受信データ追加
63 }
64 },
65 LOW, // 送信処理中、割り込み禁止になるため FALLING による監視はしない (変化のタイミングを逃すため)
66 this);
67
68 // 受信フィルタ設定 (ノード数が6以下の場合のみ)
69 constexpr size_t Mcp2515MaxFilterCount = 6;
70 if (rxSize <= Mcp2515MaxFilterCount)
71 {
72 bus.setFilterMask(MCP2515::MASK0, false, 0x7FF);
73 bus.setFilterMask(MCP2515::MASK1, false, 0x7FF);
74
75 constexpr MCP2515::RXF filters[] = { MCP2515::RXF0, MCP2515::RXF1, MCP2515::RXF2, MCP2515::RXF3, MCP2515::RXF4, MCP2515::RXF5 };
76
77 for (size_t i = 0; i < Mcp2515MaxFilterCount; ++i)
78 {
79 if (i < rxSize)
80 bus.setFilter(filters[i], false, rxNodes[i].id);
81 else
82 bus.setFilter(filters[i], false, 0x7FF); // 未使用のフィルタは全て0x7FFに設定
83 }
84 }
85 }
86
87 bus.setBitrate(config.canBaudrate, config.mcpClock);
88
89 bus.setNormalMode();
90 }
91
93 inline void CanBusSpi::end()
94 {
95 bus.reset();
96 }
97
99 inline CanBusSpi::operator bool() const
100 {
101 return not(txTimeout() or rxTimeout());
102 }
103
104 inline bool CanBusSpi::txTimeout() const
105 {
106 if (txNodes.size())
107 return millis() - transmitMs >= config.transmitTimeout;
108 else
109 return false;
110 }
111
112 inline bool CanBusSpi::rxTimeout() const
113 {
114 if (rxNodes.size())
115 return millis() - receiveMs >= config.receiveTimeout;
116 else
117 return false;
118 }
119
121 inline void CanBusSpi::update()
122 {
123 onReceive();
124 if (txNodes.size() and millis() - transmitMs >= config.transmitInterval)
125 {
126 onTransmit();
127 transmitMs = millis();
128 }
129 }
130
132 inline void CanBusSpi::show() const
133 {
134 Serial.print("CanBusSpiPico\n");
135
136 for (auto&& node : txNodes)
137 {
138 Serial.print("\tTX ");
139
140 Serial.printf("0x%03x ", static_cast<int>(node.id));
141
142 Serial.printf("%2zu byte ", node.data.size());
143
144 Serial.print(node.data.size() > SingleFrameSize ? "(multi frame) " : "(single frame) ");
145
146 Serial.print("[");
147
148 for (const auto& data : node.data)
149 {
150 Serial.printf("%4d", data);
151 }
152
153 Serial.print(" ]");
154
155 Serial.println();
156 }
157
158 for (auto&& rxNode : rxNodes)
159 {
160 Serial.print("\tRX ");
161
162 Serial.printf("0x%03x ", static_cast<int>(rxNode.id));
163
164 Serial.printf("%2zu byte ", rxNode.data.size());
165
166 Serial.print(rxNode.data.size() > SingleFrameSize ? "(multi frame) " : "(single frame) ");
167
168 Serial.print("[");
169
170 for (const auto& data : rxNode.data)
171 {
172 Serial.printf("%4d", data);
173 }
174 Serial.print(" ]");
175
176 Serial.println();
177 }
178 }
179
184 inline CanTxNode* CanBusSpi::createTx(uint32_t id, size_t length)
185 {
186 auto it = std::find_if(txNodes.begin(), txNodes.end(), [id](const CanTxNode& tx)
187 { return tx.id == id; });
188 if (it == txNodes.end())
189 {
190 txNodes.push_back({ id, std::vector<uint8_t>(length), 0 });
191 return &txNodes.back();
192 }
193 return &(*it);
194 }
195
200 inline CanRxNode* CanBusSpi::createRx(uint32_t id, size_t length)
201 {
202 rxNodes.push_back({
203 id,
204 std::vector<uint8_t>(length),
205 nullptr,
206 nullptr,
207 0,
208 });
209 return &rxNodes.back();
210 }
211
212 inline void CanBusSpi::onReceive()
213 {
214 for (auto&& msg : rxBuffer)
215 {
216 // IDに対応する受信ノードを探す
217 auto it = std::find_if(rxNodes.begin(), rxNodes.end(), [msg](const CanRxNode& rx)
218 { return rx.id == msg.can_id; });
219 if (it == rxNodes.end())
220 {
221 continue;
222 }
223
224 // 分割されたフレームを結合(マルチフレームの場合)
225 Udon::Impl::Unpacketize({ msg.data }, { it->data }, SingleFrameSize);
226
227 // 登録されている受信クラスのコールバック関数を呼ぶ
228 // 最終フレームの到達時にコールバックを呼ぶため、受信中(完全に受信しきっていないとき)にデシリアライズすることを防いでいる。
229 if (it->data.size() > SingleFrameSize)
230 {
231 // マルチフレーム
232 const auto index = std::ceil(static_cast<double>(it->data.size()) / (SingleFrameSize - 1 /*index*/)) - 1;
233
234 if (msg.data[0] == index)
235 {
236 it->callback();
237 }
238 }
239 else
240 {
241 // シングルフレーム
242 it->callback();
243 }
244
245 receiveMs = it->transmitMs = millis();
246 }
247 rxBuffer.clear();
248 }
249
250 inline void CanBusSpi::onTransmit()
251 {
252 for (auto&& node : txNodes)
253 {
254 can_frame msg{};
255 msg.can_id = node.id;
256 msg.can_dlc = SingleFrameSize;
257
258 // 一度に8バイトしか送れないため、分割し送信
259 Udon::Impl::Packetize({ node.data }, { msg.data }, SingleFrameSize,
260 [this, &msg](size_t)
261 {
262 bus.sendMessage(&msg);
263 delayMicroseconds(200);
264 });
265 // ここには来てる
266 node.transmitMs = millis();
267 }
268 }
269} // namespace Udon
void begin()
通信開始
Definition CanBusSpiPico.hpp:23
CanBusSpi(const Config &config)
コンストラクタ
Definition CanBusSpiPico.hpp:13
void Packetize(Udon::ArrayView< const uint8_t > &&input, Udon::ArrayView< uint8_t > &&output, size_t singlePacketSize, Function func)
バイト列を複数のパケット、単一のパケットにパケット化する
Definition CanUtility.hpp:24
void Unpacketize(Udon::ArrayView< const uint8_t > &&input, Udon::ArrayView< uint8_t > &&output, size_t singlePacketSize)
複数のパケット、単一のパケットからバイト列にアンパケット化する
Definition CanUtility.hpp:83
Definition Bit.hpp:12
設定
Definition CanBusSpiPico.hpp:28
uint32_t spiClock
Definition CanBusSpiPico.hpp:36
uint8_t miso
Definition CanBusSpiPico.hpp:32
uint8_t sck
Definition CanBusSpiPico.hpp:33
spi_inst_t * channel
Definition CanBusSpiPico.hpp:29
uint8_t mosi
Definition CanBusSpiPico.hpp:31
uint8_t cs
Definition CanBusSpiPico.hpp:30
uint8_t interrupt
Definition CanBusSpiPico.hpp:34
CAN受信ノード
Definition ICanBus.hpp:28
CAN送信ノード
Definition ICanBus.hpp:17