| 214 | } |
| 215 | |
| 216 | void BitBangChannelDriver::transmitSpi( |
| 217 | fl::span<const ChannelDataPtr> channels) FL_NOEXCEPT { |
| 218 | if (channels.empty()) return; |
| 219 | |
| 220 | // Group channels by clock pin |
| 221 | fl::vector<bool> processed(channels.size(), false); |
| 222 | |
| 223 | for (fl::size i = 0; i < channels.size(); ++i) { |
| 224 | if (processed[i] || !channels[i] || !channels[i]->isSpi()) continue; |
| 225 | |
| 226 | const auto* spiCfg = |
| 227 | channels[i]->getChipset().ptr<SpiChipsetConfig>(); |
| 228 | if (!spiCfg) continue; |
| 229 | |
| 230 | int clockPin = spiCfg->clockPin; |
| 231 | u32 clockHz = spiCfg->timing.clock_hz; |
| 232 | u32 halfPeriodNs = (clockHz > 0) ? (500000000u / clockHz) : 500u; |
| 233 | |
| 234 | // Collect all channels sharing this clock pin |
| 235 | fl::vector<fl::size> groupIndices; |
| 236 | groupIndices.push_back(i); |
| 237 | processed[i] = true; |
| 238 | |
| 239 | for (fl::size j = i + 1; j < channels.size(); ++j) { |
| 240 | if (processed[j] || !channels[j] || !channels[j]->isSpi()) continue; |
| 241 | const auto* otherSpi = |
| 242 | channels[j]->getChipset().ptr<SpiChipsetConfig>(); |
| 243 | if (otherSpi && otherSpi->clockPin == clockPin) { |
| 244 | groupIndices.push_back(j); |
| 245 | processed[j] = true; |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | // Find max data size in group |
| 250 | size_t maxBytes = 0; |
| 251 | for (fl::size gi = 0; gi < groupIndices.size(); ++gi) { |
| 252 | size_t sz = channels[groupIndices[gi]]->getSize(); |
| 253 | if (sz > maxBytes) maxBytes = sz; |
| 254 | } |
| 255 | |
| 256 | // Bit-bang SPI: MSB-first, data before rising edge (mode 0) |
| 257 | for (size_t byteIdx = 0; byteIdx < maxBytes; ++byteIdx) { |
| 258 | for (int bit = 7; bit >= 0; --bit) { |
| 259 | // Build data mask for all lanes |
| 260 | u8 dataMask = 0; |
| 261 | for (fl::size gi = 0; gi < groupIndices.size(); ++gi) { |
| 262 | const auto& ch = channels[groupIndices[gi]]; |
| 263 | const auto* chSpi = |
| 264 | ch->getChipset().ptr<SpiChipsetConfig>(); |
| 265 | if (!chSpi) continue; |
| 266 | |
| 267 | int pin = chSpi->dataPin; |
| 268 | if (pin < 0 || pin > 255) continue; |
| 269 | int slot = mSlotForPin[pin]; |
| 270 | if (slot < 0) continue; |
| 271 | |
| 272 | const auto& data = ch->getData(); |
| 273 | u8 byte = (byteIdx < data.size()) ? data[byteIdx] : 0; |
nothing calls this directly
no test coverage detected