diff options
| author | HybridChild <[email protected]> | 2025-08-22 13:18:52 +0200 |
|---|---|---|
| committer | HybridChild <[email protected]> | 2025-08-23 08:53:48 +0200 |
| commit | 8111bbc54594706d26d02a22ad8f1853317d3495 (patch) | |
| tree | d0c29bf6307cf7b04bb25da889dfba3bf9e9b663 | |
| parent | a52497bd39701cd71a877a3d2bd264ae2dea716c (diff) | |
stm32/i2c_v1: Clean up Async MultiMaster implementation
| -rw-r--r-- | embassy-stm32/src/i2c/v1.rs | 732 |
1 files changed, 340 insertions, 392 deletions
diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index c37b48728..0f2953ef6 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs | |||
| @@ -758,6 +758,17 @@ enum ReceiveResult { | |||
| 758 | Restarted, | 758 | Restarted, |
| 759 | } | 759 | } |
| 760 | 760 | ||
| 761 | /// Enumeration of slave transaction termination conditions | ||
| 762 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
| 763 | enum SlaveTermination { | ||
| 764 | /// STOP condition received - normal end of transaction | ||
| 765 | Stop, | ||
| 766 | /// RESTART condition received - master starting new transaction | ||
| 767 | Restart, | ||
| 768 | /// NACK received - normal end of read transaction | ||
| 769 | Nack, | ||
| 770 | } | ||
| 771 | |||
| 761 | impl<'d, M: Mode> I2c<'d, M, Master> { | 772 | impl<'d, M: Mode> I2c<'d, M, Master> { |
| 762 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) | 773 | /// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster) |
| 763 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { | 774 | pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> { |
| @@ -778,6 +789,98 @@ impl<'d, M: Mode> I2c<'d, M, Master> { | |||
| 778 | } | 789 | } |
| 779 | } | 790 | } |
| 780 | 791 | ||
| 792 | // Address configuration methods | ||
| 793 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | ||
| 794 | /// Initialize slave mode with address configuration | ||
| 795 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | ||
| 796 | trace!("I2C slave: initializing with config={:?}", config); | ||
| 797 | |||
| 798 | // Disable peripheral for configuration | ||
| 799 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 800 | |||
| 801 | // Configure slave addresses | ||
| 802 | self.apply_address_configuration(config); | ||
| 803 | |||
| 804 | // Enable peripheral with slave settings | ||
| 805 | self.info.regs.cr1().modify(|reg| { | ||
| 806 | reg.set_pe(true); | ||
| 807 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 808 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 809 | }); | ||
| 810 | |||
| 811 | trace!("I2C slave: initialization complete"); | ||
| 812 | } | ||
| 813 | |||
| 814 | /// Apply the complete address configuration for slave mode | ||
| 815 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 816 | match config.addr { | ||
| 817 | OwnAddresses::OA1(addr) => { | ||
| 818 | self.configure_primary_address(addr); | ||
| 819 | self.disable_secondary_address(); | ||
| 820 | }, | ||
| 821 | OwnAddresses::OA2(oa2) => { | ||
| 822 | self.configure_default_primary_address(); | ||
| 823 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 824 | }, | ||
| 825 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 826 | self.configure_primary_address(oa1); | ||
| 827 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 828 | } | ||
| 829 | } | ||
| 830 | |||
| 831 | // Configure general call detection | ||
| 832 | if config.general_call { | ||
| 833 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 834 | } | ||
| 835 | } | ||
| 836 | |||
| 837 | /// Configure the primary address (OA1) register | ||
| 838 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 839 | match addr { | ||
| 840 | Address::SevenBit(addr) => { | ||
| 841 | self.info.regs.oar1().write(|reg| { | ||
| 842 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 843 | reg.set_add(hw_addr); | ||
| 844 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 845 | }); | ||
| 846 | }, | ||
| 847 | Address::TenBit(addr) => { | ||
| 848 | self.info.regs.oar1().write(|reg| { | ||
| 849 | reg.set_add(addr); | ||
| 850 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 851 | }); | ||
| 852 | } | ||
| 853 | } | ||
| 854 | |||
| 855 | // Set required bit 14 as per reference manual | ||
| 856 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 857 | } | ||
| 858 | |||
| 859 | /// Configure the secondary address (OA2) register | ||
| 860 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 861 | self.info.regs.oar2().write(|reg| { | ||
| 862 | reg.set_add2(addr); | ||
| 863 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 864 | }); | ||
| 865 | } | ||
| 866 | |||
| 867 | /// Set a default primary address when using OA2-only mode | ||
| 868 | fn configure_default_primary_address(&mut self) { | ||
| 869 | self.info.regs.oar1().write(|reg| { | ||
| 870 | reg.set_add(0); // Reserved address, safe to use | ||
| 871 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 872 | }); | ||
| 873 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 874 | } | ||
| 875 | |||
| 876 | /// Disable secondary address when not needed | ||
| 877 | fn disable_secondary_address(&mut self) { | ||
| 878 | self.info.regs.oar2().write(|reg| { | ||
| 879 | reg.set_endual(i2c::vals::Endual::SINGLE); | ||
| 880 | }); | ||
| 881 | } | ||
| 882 | } | ||
| 883 | |||
| 781 | impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | 884 | impl<'d, M: Mode> I2c<'d, M, MultiMaster> { |
| 782 | /// Listen for incoming I2C address match and return the command type | 885 | /// Listen for incoming I2C address match and return the command type |
| 783 | /// | 886 | /// |
| @@ -1170,165 +1273,85 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { | |||
| 1170 | reg.set_af(false); | 1273 | reg.set_af(false); |
| 1171 | }); | 1274 | }); |
| 1172 | } | 1275 | } |
| 1173 | } | ||
| 1174 | 1276 | ||
| 1175 | // Address configuration methods | 1277 | /// Configure DMA settings for slave operations (shared between read/write) |
| 1176 | impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { | 1278 | fn setup_slave_dma_base(&mut self) { |
| 1177 | /// Initialize slave mode with address configuration | 1279 | self.info.regs.cr2().modify(|w| { |
| 1178 | pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) { | 1280 | w.set_itbufen(false); // Always disable buffer interrupts when using DMA |
| 1179 | trace!("I2C slave: initializing with config={:?}", config); | 1281 | w.set_dmaen(true); // Enable DMA requests |
| 1180 | 1282 | w.set_last(false); // LAST bit not used in slave mode for v1 hardware | |
| 1181 | // Disable peripheral for configuration | ||
| 1182 | self.info.regs.cr1().modify(|reg| reg.set_pe(false)); | ||
| 1183 | |||
| 1184 | // Configure slave addresses | ||
| 1185 | self.apply_address_configuration(config); | ||
| 1186 | |||
| 1187 | // Enable peripheral with slave settings | ||
| 1188 | self.info.regs.cr1().modify(|reg| { | ||
| 1189 | reg.set_pe(true); | ||
| 1190 | reg.set_ack(true); // Enable acknowledgment for slave mode | ||
| 1191 | reg.set_nostretch(false); // Allow clock stretching for processing time | ||
| 1192 | }); | ||
| 1193 | |||
| 1194 | trace!("I2C slave: initialization complete"); | ||
| 1195 | } | ||
| 1196 | |||
| 1197 | /// Apply the complete address configuration for slave mode | ||
| 1198 | fn apply_address_configuration(&mut self, config: SlaveAddrConfig) { | ||
| 1199 | match config.addr { | ||
| 1200 | OwnAddresses::OA1(addr) => { | ||
| 1201 | self.configure_primary_address(addr); | ||
| 1202 | self.disable_secondary_address(); | ||
| 1203 | }, | ||
| 1204 | OwnAddresses::OA2(oa2) => { | ||
| 1205 | self.configure_default_primary_address(); | ||
| 1206 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 1207 | }, | ||
| 1208 | OwnAddresses::Both { oa1, oa2 } => { | ||
| 1209 | self.configure_primary_address(oa1); | ||
| 1210 | self.configure_secondary_address(oa2.addr); // v1 ignores mask | ||
| 1211 | } | ||
| 1212 | } | ||
| 1213 | |||
| 1214 | // Configure general call detection | ||
| 1215 | if config.general_call { | ||
| 1216 | self.info.regs.cr1().modify(|w| w.set_engc(true)); | ||
| 1217 | } | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | /// Configure the primary address (OA1) register | ||
| 1221 | fn configure_primary_address(&mut self, addr: Address) { | ||
| 1222 | match addr { | ||
| 1223 | Address::SevenBit(addr) => { | ||
| 1224 | self.info.regs.oar1().write(|reg| { | ||
| 1225 | let hw_addr = (addr as u16) << 1; // Address in bits [7:1] | ||
| 1226 | reg.set_add(hw_addr); | ||
| 1227 | reg.set_addmode(i2c::vals::Addmode::BIT7); | ||
| 1228 | }); | ||
| 1229 | }, | ||
| 1230 | Address::TenBit(addr) => { | ||
| 1231 | self.info.regs.oar1().write(|reg| { | ||
| 1232 | reg.set_add(addr); | ||
| 1233 | reg.set_addmode(i2c::vals::Addmode::BIT10); | ||
| 1234 | }); | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | // Set required bit 14 as per reference manual | ||
| 1239 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | /// Configure the secondary address (OA2) register | ||
| 1243 | fn configure_secondary_address(&mut self, addr: u8) { | ||
| 1244 | self.info.regs.oar2().write(|reg| { | ||
| 1245 | reg.set_add2(addr); | ||
| 1246 | reg.set_endual(i2c::vals::Endual::DUAL); | ||
| 1247 | }); | 1283 | }); |
| 1248 | } | 1284 | } |
| 1249 | 1285 | ||
| 1250 | /// Set a default primary address when using OA2-only mode | 1286 | /// Disable DMA and interrupts in a critical section |
| 1251 | fn configure_default_primary_address(&mut self) { | 1287 | fn disable_dma_and_interrupts(info: &'static Info) { |
| 1252 | self.info.regs.oar1().write(|reg| { | 1288 | critical_section::with(|_| { |
| 1253 | reg.set_add(0); // Reserved address, safe to use | 1289 | info.regs.cr2().modify(|w| { |
| 1254 | reg.set_addmode(i2c::vals::Addmode::BIT7); | 1290 | w.set_dmaen(false); |
| 1291 | w.set_iterren(false); | ||
| 1292 | w.set_itevten(false); | ||
| 1293 | }); | ||
| 1255 | }); | 1294 | }); |
| 1256 | self.info.regs.oar1().modify(|reg| reg.0 |= 1 << 14); | ||
| 1257 | } | 1295 | } |
| 1258 | 1296 | ||
| 1259 | /// Disable secondary address when not needed | 1297 | /// Check for early termination conditions during slave operations |
| 1260 | fn disable_secondary_address(&mut self) { | 1298 | /// Returns Some(result) if termination detected, None to continue |
| 1261 | self.info.regs.oar2().write(|reg| { | 1299 | fn check_slave_termination_conditions(sr1: i2c::regs::Sr1) -> Option<SlaveTermination> { |
| 1262 | reg.set_endual(i2c::vals::Endual::SINGLE); | 1300 | if sr1.stopf() { |
| 1263 | }); | 1301 | Some(SlaveTermination::Stop) |
| 1302 | } else if sr1.addr() { | ||
| 1303 | Some(SlaveTermination::Restart) | ||
| 1304 | } else if sr1.af() { | ||
| 1305 | Some(SlaveTermination::Nack) | ||
| 1306 | } else { | ||
| 1307 | None | ||
| 1308 | } | ||
| 1264 | } | 1309 | } |
| 1265 | } | 1310 | } |
| 1266 | 1311 | ||
| 1267 | impl<'d> I2c<'d, Async, MultiMaster> { | 1312 | impl<'d> I2c<'d, Async, MultiMaster> { |
| 1268 | /// Async listen for incoming I2C messages using interrupts | 1313 | /// Async listen for incoming I2C messages using interrupts |
| 1314 | /// | ||
| 1315 | /// Waits for a master to address this slave and returns the command type | ||
| 1316 | /// (Read/Write) and the matched address. This method will suspend until | ||
| 1317 | /// an address match occurs. | ||
| 1269 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { | 1318 | pub async fn listen(&mut self) -> Result<SlaveCommand, Error> { |
| 1270 | trace!("I2C slave: starting listen for address match"); | ||
| 1271 | |||
| 1272 | // Extract references needed by the closure | ||
| 1273 | let state = self.state; | 1319 | let state = self.state; |
| 1274 | let info = self.info; | 1320 | let info = self.info; |
| 1275 | 1321 | ||
| 1276 | Self::enable_interrupts(info); | 1322 | Self::enable_interrupts(info); |
| 1277 | trace!("I2C slave: enabled event and error interrupts"); | ||
| 1278 | 1323 | ||
| 1279 | // Sentinel to clean up interrupts on drop | 1324 | // Ensure interrupts are cleaned up on early exit |
| 1280 | let on_drop = OnDrop::new(|| { | 1325 | let on_drop = OnDrop::new(|| { |
| 1281 | critical_section::with(|_| { | 1326 | Self::disable_dma_and_interrupts(info); |
| 1282 | info.regs.cr2().modify(|w| { | ||
| 1283 | w.set_iterren(false); | ||
| 1284 | w.set_itevten(false); | ||
| 1285 | }); | ||
| 1286 | }); | ||
| 1287 | trace!("I2C slave: disabled interrupts on drop"); | ||
| 1288 | }); | 1327 | }); |
| 1289 | 1328 | ||
| 1290 | let result = poll_fn(|cx| { | 1329 | let result = poll_fn(|cx| { |
| 1291 | state.waker.register(cx.waker()); | 1330 | state.waker.register(cx.waker()); |
| 1292 | 1331 | ||
| 1293 | match Self::check_and_clear_error_flags(info) { | 1332 | match Self::check_and_clear_error_flags(info) { |
| 1294 | Err(e) => { | 1333 | Err(e) => Poll::Ready(Err(e)), |
| 1295 | error!("I2C slave: error during listen: {:?}", e); | ||
| 1296 | Poll::Ready(Err(e)) | ||
| 1297 | }, | ||
| 1298 | Ok(sr1) => { | 1334 | Ok(sr1) => { |
| 1299 | if sr1.addr() { | 1335 | if sr1.addr() { |
| 1300 | trace!("I2C slave: ADDR flag detected - address matched"); | 1336 | // Address matched - determine direction and decode address |
| 1301 | |||
| 1302 | // Address matched - determine direction | ||
| 1303 | let sr2 = info.regs.sr2().read(); | 1337 | let sr2 = info.regs.sr2().read(); |
| 1304 | let direction = if sr2.tra() { | 1338 | let direction = if sr2.tra() { |
| 1305 | trace!("I2C slave: direction = READ (master wants to read from us)"); | ||
| 1306 | SlaveCommandKind::Read | 1339 | SlaveCommandKind::Read |
| 1307 | } else { | 1340 | } else { |
| 1308 | trace!("I2C slave: direction = WRITE (master wants to write to us)"); | ||
| 1309 | SlaveCommandKind::Write | 1341 | SlaveCommandKind::Write |
| 1310 | }; | 1342 | }; |
| 1311 | 1343 | ||
| 1312 | let matched_address = match Self::decode_matched_address(sr2, info) { | 1344 | let matched_address = match Self::decode_matched_address(sr2, info) { |
| 1313 | Ok(addr) => { | 1345 | Ok(addr) => addr, |
| 1314 | trace!("I2C slave: matched address decoded: {:?}", addr); | 1346 | Err(e) => return Poll::Ready(Err(e)), |
| 1315 | addr | ||
| 1316 | }, | ||
| 1317 | Err(e) => { | ||
| 1318 | error!("I2C slave: failed to decode matched address: {:?}", e); | ||
| 1319 | return Poll::Ready(Err(e)); | ||
| 1320 | } | ||
| 1321 | }; | 1347 | }; |
| 1322 | 1348 | ||
| 1323 | trace!("I2C slave: listen complete - returning command"); | 1349 | // Don't clear ADDR here - leave it for DMA setup in respond methods |
| 1324 | // Don't clear ADDR here - leave it for DMA setup | ||
| 1325 | Poll::Ready(Ok(SlaveCommand { | 1350 | Poll::Ready(Ok(SlaveCommand { |
| 1326 | kind: direction, | 1351 | kind: direction, |
| 1327 | address: matched_address, | 1352 | address: matched_address, |
| 1328 | })) | 1353 | })) |
| 1329 | } else { | 1354 | } else { |
| 1330 | // Re-enable interrupts and continue waiting | ||
| 1331 | // Use safe method since handler disables them globally | ||
| 1332 | Self::enable_interrupts(info); | 1355 | Self::enable_interrupts(info); |
| 1333 | Poll::Pending | 1356 | Poll::Pending |
| 1334 | } | 1357 | } |
| @@ -1337,341 +1360,266 @@ impl<'d> I2c<'d, Async, MultiMaster> { | |||
| 1337 | }).await; | 1360 | }).await; |
| 1338 | 1361 | ||
| 1339 | drop(on_drop); | 1362 | drop(on_drop); |
| 1340 | trace!("I2C slave: listen result: {:?}", result); | ||
| 1341 | result | 1363 | result |
| 1342 | } | 1364 | } |
| 1343 | 1365 | ||
| 1344 | /// Async respond to write command using RX DMA | 1366 | /// Async respond to write command using RX DMA |
| 1367 | /// | ||
| 1368 | /// Receives data from the master into the provided buffer using DMA. | ||
| 1369 | /// If the master sends more bytes than the buffer can hold, excess bytes | ||
| 1370 | /// are acknowledged but discarded to prevent interrupt flooding. | ||
| 1371 | /// | ||
| 1372 | /// Returns the number of bytes stored in the buffer (not total received). | ||
| 1345 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 1373 | pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { |
| 1346 | trace!("I2C slave: starting respond_to_write, buffer_len={}", buffer.len()); | ||
| 1347 | |||
| 1348 | if buffer.is_empty() { | 1374 | if buffer.is_empty() { |
| 1349 | warn!("I2C slave: respond_to_write called with empty buffer"); | ||
| 1350 | return Err(Error::Overrun); | 1375 | return Err(Error::Overrun); |
| 1351 | } | 1376 | } |
| 1352 | 1377 | ||
| 1353 | // Extract references needed by closures | ||
| 1354 | let state = self.state; | 1378 | let state = self.state; |
| 1355 | let info = self.info; | 1379 | let info = self.info; |
| 1356 | 1380 | ||
| 1357 | trace!("I2C slave: configuring registers for RX DMA"); | 1381 | // Use shared DMA setup |
| 1358 | info.regs.cr2().modify(|w| { | 1382 | self.setup_slave_dma_base(); |
| 1359 | w.set_itbufen(false); // Disable buffer interrupts for DMA | ||
| 1360 | w.set_dmaen(true); // Enable DMA | ||
| 1361 | w.set_last(false); // Not needed for slave writes | ||
| 1362 | }); | ||
| 1363 | 1383 | ||
| 1364 | // Sentinel to clean up DMA on drop | 1384 | // Ensure proper cleanup on exit |
| 1365 | let on_drop = OnDrop::new(|| { | 1385 | let on_drop = OnDrop::new(|| { |
| 1366 | critical_section::with(|_| { | 1386 | Self::disable_dma_and_interrupts(info); |
| 1367 | info.regs.cr2().modify(|w| { | ||
| 1368 | w.set_dmaen(false); | ||
| 1369 | w.set_iterren(false); | ||
| 1370 | w.set_itevten(false); | ||
| 1371 | }); | ||
| 1372 | }); | ||
| 1373 | trace!("I2C slave: DMA and interrupts disabled on drop (respond_to_write)"); | ||
| 1374 | }); | 1387 | }); |
| 1375 | 1388 | ||
| 1376 | // Clear ADDR flag to release clock stretching - DMA is now ready | 1389 | // Clear ADDR flag to release clock stretching |
| 1377 | trace!("I2C slave: clearing ADDR flag to release clock stretching"); | 1390 | info.regs.sr2().read(); |
| 1378 | info.regs.sr2().read(); // Clear ADDR by reading SR2 | ||
| 1379 | |||
| 1380 | // Set up RX DMA transfer (I2C DR -> buffer) | ||
| 1381 | trace!("I2C slave: setting up RX DMA transfer"); | ||
| 1382 | let dma_transfer = unsafe { | ||
| 1383 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1384 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1385 | }; | ||
| 1386 | |||
| 1387 | // Poll for I2C errors while DMA runs | ||
| 1388 | let poll_error = poll_fn(|cx| { | ||
| 1389 | state.waker.register(cx.waker()); | ||
| 1390 | 1391 | ||
| 1391 | match Self::check_and_clear_error_flags(info) { | 1392 | // Start DMA transfer and monitor completion |
| 1392 | Err(e) => { | 1393 | let result = self.execute_slave_receive_transfer(buffer, state, info).await; |
| 1393 | error!("I2C slave: error during write operation: {:?}", e); | ||
| 1394 | Poll::Ready(Err::<(), Error>(e)) | ||
| 1395 | }, | ||
| 1396 | Ok(sr1) => { | ||
| 1397 | // Check for STOP condition (normal end of write) | ||
| 1398 | if sr1.stopf() { | ||
| 1399 | trace!("I2C slave: STOP condition detected - write transaction complete"); | ||
| 1400 | Self::clear_stop_flag(info); | ||
| 1401 | Poll::Ready(Ok(())) | ||
| 1402 | } else if sr1.addr() { | ||
| 1403 | trace!("I2C slave: RESTART condition detected - transitioning to read phase"); | ||
| 1404 | Poll::Ready(Ok(())) | ||
| 1405 | } else { | ||
| 1406 | // Re-enable interrupts and continue monitoring | ||
| 1407 | // Use safe method since handler disables them globally | ||
| 1408 | Self::enable_interrupts(info); | ||
| 1409 | Poll::Pending | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | } | ||
| 1413 | }); | ||
| 1414 | 1394 | ||
| 1415 | trace!("I2C slave: starting select between DMA completion and I2C events"); | 1395 | drop(on_drop); |
| 1416 | // Wait for either DMA completion or I2C termination condition (using master pattern) | 1396 | result |
| 1417 | let dma_result = match select(dma_transfer, poll_error).await { | ||
| 1418 | Either::Second(Err(e)) => { | ||
| 1419 | error!("I2C slave: I2C error occurred during write: {:?}", e); | ||
| 1420 | critical_section::with(|_| { | ||
| 1421 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | ||
| 1422 | }); | ||
| 1423 | drop(on_drop); | ||
| 1424 | return Err(e); | ||
| 1425 | } | ||
| 1426 | Either::First(_) => { | ||
| 1427 | trace!("I2C slave: DMA transfer completed first - buffer full"); | ||
| 1428 | critical_section::with(|_| { | ||
| 1429 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | ||
| 1430 | }); | ||
| 1431 | |||
| 1432 | // DMA completed but master might still be sending more bytes | ||
| 1433 | // We need to discard any excess bytes until STOP/RESTART | ||
| 1434 | trace!("I2C slave: DMA complete, checking for excess bytes to discard"); | ||
| 1435 | |||
| 1436 | let excess_discard = poll_fn(|cx| { | ||
| 1437 | state.waker.register(cx.waker()); | ||
| 1438 | |||
| 1439 | match Self::check_and_clear_error_flags(info) { | ||
| 1440 | Err(e) => { | ||
| 1441 | error!("I2C slave: error while discarding excess bytes: {:?}", e); | ||
| 1442 | Poll::Ready(Err::<(), Error>(e)) | ||
| 1443 | }, | ||
| 1444 | Ok(sr1) => { | ||
| 1445 | // Check for transaction termination first | ||
| 1446 | if sr1.stopf() { | ||
| 1447 | trace!("I2C slave: STOP condition detected while discarding excess"); | ||
| 1448 | Self::clear_stop_flag(info); | ||
| 1449 | return Poll::Ready(Ok(())); | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | if sr1.addr() { | ||
| 1453 | trace!("I2C slave: RESTART condition detected while discarding excess"); | ||
| 1454 | return Poll::Ready(Ok(())); | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | // If there's data to read, read and discard it | ||
| 1458 | if sr1.rxne() { | ||
| 1459 | let discarded_byte = info.regs.dr().read().dr(); | ||
| 1460 | trace!("I2C slave: discarded excess byte: 0x{:02X}", discarded_byte); | ||
| 1461 | // Continue polling for more bytes or termination | ||
| 1462 | Self::enable_interrupts(info); | ||
| 1463 | return Poll::Pending; | ||
| 1464 | } | ||
| 1465 | |||
| 1466 | // No immediate termination or data, keep waiting | ||
| 1467 | Self::enable_interrupts(info); | ||
| 1468 | Poll::Pending | ||
| 1469 | } | ||
| 1470 | } | ||
| 1471 | }); | ||
| 1472 | |||
| 1473 | // Wait for transaction to actually end | ||
| 1474 | excess_discard.await?; | ||
| 1475 | |||
| 1476 | let bytes_received = buffer.len(); | ||
| 1477 | trace!("I2C slave: write complete after discarding excess, returning {} bytes", bytes_received); | ||
| 1478 | drop(on_drop); | ||
| 1479 | Ok(bytes_received) | ||
| 1480 | } | ||
| 1481 | Either::Second(Ok(())) => { | ||
| 1482 | trace!("I2C slave: I2C event (STOP/RESTART) occurred before DMA completion"); | ||
| 1483 | critical_section::with(|_| { | ||
| 1484 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | ||
| 1485 | }); | ||
| 1486 | |||
| 1487 | // Transaction ended normally before buffer was full | ||
| 1488 | let bytes_received = buffer.len(); | ||
| 1489 | trace!("I2C slave: write complete via early I2C event, returning {} bytes", bytes_received); | ||
| 1490 | drop(on_drop); | ||
| 1491 | Ok(bytes_received) | ||
| 1492 | } | ||
| 1493 | }; | ||
| 1494 | |||
| 1495 | dma_result | ||
| 1496 | } | 1397 | } |
| 1497 | 1398 | ||
| 1498 | /// Async respond to read command using TX DMA | 1399 | /// Async respond to read command using TX DMA |
| 1400 | /// | ||
| 1401 | /// Transmits data to the master using DMA. If the master requests more bytes | ||
| 1402 | /// than available in the data buffer, padding bytes (0x00) are sent until | ||
| 1403 | /// the master terminates the transaction with NACK, STOP, or RESTART. | ||
| 1404 | /// | ||
| 1405 | /// Returns the total number of bytes transmitted (data + padding). | ||
| 1499 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { | 1406 | pub async fn respond_to_read(&mut self, data: &[u8]) -> Result<usize, Error> { |
| 1500 | trace!("I2C slave: starting respond_to_read, data_len={}", data.len()); | ||
| 1501 | |||
| 1502 | if data.is_empty() { | 1407 | if data.is_empty() { |
| 1503 | warn!("I2C slave: respond_to_read called with empty data"); | ||
| 1504 | return Err(Error::Overrun); | 1408 | return Err(Error::Overrun); |
| 1505 | } | 1409 | } |
| 1506 | 1410 | ||
| 1507 | // Extract references needed by closures | ||
| 1508 | let state = self.state; | 1411 | let state = self.state; |
| 1509 | let info = self.info; | 1412 | let info = self.info; |
| 1510 | 1413 | ||
| 1511 | trace!("I2C slave: configuring registers for TX DMA"); | 1414 | // Use shared DMA setup |
| 1512 | info.regs.cr2().modify(|w| { | 1415 | self.setup_slave_dma_base(); |
| 1513 | w.set_itbufen(false); // Disable buffer interrupts for DMA | ||
| 1514 | w.set_dmaen(true); // Enable DMA | ||
| 1515 | w.set_last(false); // Not applicable for slave transmit | ||
| 1516 | }); | ||
| 1517 | 1416 | ||
| 1518 | // Sentinel to clean up DMA on drop | 1417 | // Ensure proper cleanup on exit |
| 1519 | let on_drop = OnDrop::new(|| { | 1418 | let on_drop = OnDrop::new(|| { |
| 1520 | critical_section::with(|_| { | 1419 | Self::disable_dma_and_interrupts(info); |
| 1521 | info.regs.cr2().modify(|w| { | ||
| 1522 | w.set_dmaen(false); | ||
| 1523 | w.set_iterren(false); | ||
| 1524 | w.set_itevten(false); | ||
| 1525 | }); | ||
| 1526 | }); | ||
| 1527 | trace!("I2C slave: DMA and interrupts disabled on drop (respond_to_read)"); | ||
| 1528 | }); | 1420 | }); |
| 1529 | 1421 | ||
| 1530 | // Clear ADDR flag to release clock stretching | 1422 | // Clear ADDR flag to release clock stretching |
| 1531 | trace!("I2C slave: clearing ADDR flag to release clock stretching"); | ||
| 1532 | info.regs.sr2().read(); | 1423 | info.regs.sr2().read(); |
| 1533 | 1424 | ||
| 1534 | // Set up TX DMA transfer (data -> I2C DR) | 1425 | // Start DMA transfer and monitor completion |
| 1535 | trace!("I2C slave: setting up TX DMA transfer"); | 1426 | let result = self.execute_slave_transmit_transfer(data, state, info).await; |
| 1427 | |||
| 1428 | drop(on_drop); | ||
| 1429 | result | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | // === Private Transfer Execution Methods === | ||
| 1433 | |||
| 1434 | /// Execute complete slave receive transfer with excess byte handling | ||
| 1435 | async fn execute_slave_receive_transfer( | ||
| 1436 | &mut self, | ||
| 1437 | buffer: &mut [u8], | ||
| 1438 | state: &'static State, | ||
| 1439 | info: &'static Info | ||
| 1440 | ) -> Result<usize, Error> { | ||
| 1441 | // Start DMA transfer | ||
| 1442 | let dma_transfer = unsafe { | ||
| 1443 | let src = info.regs.dr().as_ptr() as *mut u8; | ||
| 1444 | self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) | ||
| 1445 | }; | ||
| 1446 | |||
| 1447 | // Monitor for I2C events during transfer | ||
| 1448 | let i2c_monitor = Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart]); | ||
| 1449 | |||
| 1450 | match select(dma_transfer, i2c_monitor).await { | ||
| 1451 | Either::Second(Err(e)) => { | ||
| 1452 | Self::disable_dma_and_interrupts(info); | ||
| 1453 | Err(e) | ||
| 1454 | } | ||
| 1455 | Either::First(_) => { | ||
| 1456 | // DMA completed first - handle potential excess bytes | ||
| 1457 | Self::disable_dma_and_interrupts(info); | ||
| 1458 | self.handle_excess_bytes(state, info).await?; | ||
| 1459 | Ok(buffer.len()) | ||
| 1460 | } | ||
| 1461 | Either::Second(Ok(_)) => { | ||
| 1462 | // I2C event occurred first - normal transaction end | ||
| 1463 | Self::disable_dma_and_interrupts(info); | ||
| 1464 | Ok(buffer.len()) | ||
| 1465 | } | ||
| 1466 | } | ||
| 1467 | } | ||
| 1468 | |||
| 1469 | /// Execute complete slave transmit transfer with padding byte handling | ||
| 1470 | async fn execute_slave_transmit_transfer( | ||
| 1471 | &mut self, | ||
| 1472 | data: &[u8], | ||
| 1473 | state: &'static State, | ||
| 1474 | info: &'static Info | ||
| 1475 | ) -> Result<usize, Error> { | ||
| 1476 | // Start DMA transfer | ||
| 1536 | let dma_transfer = unsafe { | 1477 | let dma_transfer = unsafe { |
| 1537 | let dst = info.regs.dr().as_ptr() as *mut u8; | 1478 | let dst = info.regs.dr().as_ptr() as *mut u8; |
| 1538 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) | 1479 | self.tx_dma.as_mut().unwrap().write(data, dst, Default::default()) |
| 1539 | }; | 1480 | }; |
| 1540 | 1481 | ||
| 1541 | // Monitor for I2C events while DMA runs | 1482 | // Monitor for I2C events during transfer (NACK is normal for slave transmit) |
| 1542 | let poll_completion = poll_fn(|cx| { | 1483 | let i2c_monitor = Self::create_termination_monitor(state, info, &[SlaveTermination::Stop, SlaveTermination::Restart, SlaveTermination::Nack]); |
| 1543 | state.waker.register(cx.waker()); | ||
| 1544 | 1484 | ||
| 1485 | match select(dma_transfer, i2c_monitor).await { | ||
| 1486 | Either::Second(Err(e)) => { | ||
| 1487 | Self::disable_dma_and_interrupts(info); | ||
| 1488 | Err(e) | ||
| 1489 | } | ||
| 1490 | Either::First(_) => { | ||
| 1491 | // DMA completed first - handle potential padding bytes | ||
| 1492 | Self::disable_dma_and_interrupts(info); | ||
| 1493 | let padding_count = self.handle_padding_bytes(state, info).await?; | ||
| 1494 | Ok(data.len() + padding_count) | ||
| 1495 | } | ||
| 1496 | Either::Second(Ok(_)) => { | ||
| 1497 | // I2C event occurred first - normal transaction end | ||
| 1498 | Self::disable_dma_and_interrupts(info); | ||
| 1499 | Ok(data.len()) | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | /// Create a future that monitors for specific slave termination conditions | ||
| 1505 | fn create_termination_monitor( | ||
| 1506 | state: &'static State, | ||
| 1507 | info: &'static Info, | ||
| 1508 | allowed_terminations: &'static [SlaveTermination], | ||
| 1509 | ) -> impl Future<Output = Result<SlaveTermination, Error>> { | ||
| 1510 | poll_fn(move |cx| { | ||
| 1511 | state.waker.register(cx.waker()); | ||
| 1512 | |||
| 1545 | match Self::check_and_clear_error_flags(info) { | 1513 | match Self::check_and_clear_error_flags(info) { |
| 1546 | Err(Error::Nack) => { | 1514 | Err(Error::Nack) if allowed_terminations.contains(&SlaveTermination::Nack) => { |
| 1547 | trace!("I2C slave: NACK received - master stopped reading (normal)"); | 1515 | Poll::Ready(Ok(SlaveTermination::Nack)) |
| 1548 | Poll::Ready(Ok(())) | ||
| 1549 | } | 1516 | } |
| 1550 | Err(e) => { | 1517 | Err(e) => Poll::Ready(Err(e)), |
| 1551 | error!("I2C slave: error during read operation: {:?}", e); | ||
| 1552 | Poll::Ready(Err(e)) | ||
| 1553 | }, | ||
| 1554 | Ok(sr1) => { | 1518 | Ok(sr1) => { |
| 1555 | if sr1.stopf() { | 1519 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { |
| 1556 | trace!("I2C slave: STOP condition detected - read transaction complete"); | 1520 | if allowed_terminations.contains(&termination) { |
| 1557 | Self::clear_stop_flag(info); | 1521 | // Handle the specific termination condition |
| 1558 | Poll::Ready(Ok(())) | 1522 | match termination { |
| 1559 | } else if sr1.addr() { | 1523 | SlaveTermination::Stop => Self::clear_stop_flag(info), |
| 1560 | trace!("I2C slave: RESTART condition detected during read"); | 1524 | SlaveTermination::Nack => { |
| 1561 | Poll::Ready(Ok(())) | 1525 | info.regs.sr1().write(|reg| { |
| 1562 | } else if sr1.af() { | 1526 | reg.0 = !0; |
| 1563 | trace!("I2C slave: acknowledge failure (NACK) - normal end of read"); | 1527 | reg.set_af(false); |
| 1564 | // Acknowledge failure (NACK) - normal end of read | 1528 | }); |
| 1565 | info.regs.sr1().write(|reg| { | 1529 | } |
| 1566 | reg.0 = !0; | 1530 | SlaveTermination::Restart => { |
| 1567 | reg.set_af(false); | 1531 | // ADDR flag will be handled by next listen() call |
| 1568 | }); | 1532 | } |
| 1569 | Poll::Ready(Ok(())) | 1533 | } |
| 1534 | Poll::Ready(Ok(termination)) | ||
| 1535 | } else { | ||
| 1536 | // Unexpected termination condition | ||
| 1537 | Poll::Ready(Err(Error::Bus)) | ||
| 1538 | } | ||
| 1570 | } else { | 1539 | } else { |
| 1571 | Self::enable_interrupts(info); | 1540 | Self::enable_interrupts(info); |
| 1572 | Poll::Pending | 1541 | Poll::Pending |
| 1573 | } | 1542 | } |
| 1574 | } | 1543 | } |
| 1575 | } | 1544 | } |
| 1576 | }); | 1545 | }) |
| 1546 | } | ||
| 1577 | 1547 | ||
| 1578 | trace!("I2C slave: starting select between DMA completion and I2C events"); | 1548 | /// Handle excess bytes after DMA buffer is full |
| 1579 | // Wait for either DMA completion or I2C termination (using master pattern) | 1549 | /// |
| 1580 | let dma_result = match select(dma_transfer, poll_completion).await { | 1550 | /// Reads and discards bytes until transaction termination to prevent interrupt flooding |
| 1581 | Either::Second(Err(e)) => { | 1551 | async fn handle_excess_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<(), Error> { |
| 1582 | error!("I2C slave: I2C error occurred during read: {:?}", e); | 1552 | poll_fn(|cx| { |
| 1583 | critical_section::with(|_| { | 1553 | state.waker.register(cx.waker()); |
| 1584 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | 1554 | |
| 1585 | }); | 1555 | match Self::check_and_clear_error_flags(info) { |
| 1586 | drop(on_drop); | 1556 | Err(e) => Poll::Ready(Err(e)), |
| 1587 | return Err(e); | 1557 | Ok(sr1) => { |
| 1588 | } | 1558 | // Check for transaction termination first |
| 1589 | Either::First(_) => { | 1559 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { |
| 1590 | trace!("I2C slave: DMA transfer completed first - all data sent"); | 1560 | match termination { |
| 1591 | critical_section::with(|_| { | 1561 | SlaveTermination::Stop => Self::clear_stop_flag(info), |
| 1592 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | 1562 | SlaveTermination::Restart => {}, // Leave ADDR for next operation |
| 1593 | }); | 1563 | SlaveTermination::Nack => unreachable!("NACK not expected during receive"), |
| 1594 | 1564 | } | |
| 1595 | // DMA completed but master might still be requesting more bytes | 1565 | return Poll::Ready(Ok(())); |
| 1596 | // We need to send padding bytes (0x00) until NACK/STOP/RESTART | 1566 | } |
| 1597 | trace!("I2C slave: DMA complete, entering padding phase"); | ||
| 1598 | let mut padding_bytes_sent = 0; | ||
| 1599 | |||
| 1600 | let padding_phase = poll_fn(|cx| { | ||
| 1601 | state.waker.register(cx.waker()); | ||
| 1602 | 1567 | ||
| 1603 | match Self::check_and_clear_error_flags(info) { | 1568 | // If there's data to read, discard it |
| 1604 | Err(Error::Nack) => { | 1569 | if sr1.rxne() { |
| 1605 | trace!("I2C slave: NACK received during padding - normal end"); | 1570 | let _discarded_byte = info.regs.dr().read().dr(); |
| 1606 | return Poll::Ready(Ok(())); | 1571 | Self::enable_interrupts(info); |
| 1607 | }, | 1572 | return Poll::Pending; |
| 1608 | Err(e) => { | 1573 | } |
| 1609 | error!("I2C slave: error while sending padding bytes: {:?}", e); | 1574 | |
| 1610 | return Poll::Ready(Err::<(), Error>(e)); | 1575 | Self::enable_interrupts(info); |
| 1611 | }, | 1576 | Poll::Pending |
| 1612 | Ok(sr1) => { | 1577 | } |
| 1613 | // Check for transaction termination first | 1578 | } |
| 1614 | if sr1.af() { | 1579 | }).await |
| 1615 | trace!("I2C slave: acknowledge failure during padding - normal end"); | 1580 | } |
| 1581 | |||
| 1582 | /// Handle padding bytes after DMA data is exhausted | ||
| 1583 | /// | ||
| 1584 | /// Sends 0x00 bytes until transaction termination to prevent interrupt flooding | ||
| 1585 | async fn handle_padding_bytes(&mut self, state: &'static State, info: &'static Info) -> Result<usize, Error> { | ||
| 1586 | let mut padding_count = 0; | ||
| 1587 | |||
| 1588 | poll_fn(|cx| { | ||
| 1589 | state.waker.register(cx.waker()); | ||
| 1590 | |||
| 1591 | match Self::check_and_clear_error_flags(info) { | ||
| 1592 | Err(Error::Nack) => Poll::Ready(Ok(padding_count)), // Normal termination | ||
| 1593 | Err(e) => Poll::Ready(Err(e)), | ||
| 1594 | Ok(sr1) => { | ||
| 1595 | // Check for transaction termination first | ||
| 1596 | if let Some(termination) = Self::check_slave_termination_conditions(sr1) { | ||
| 1597 | match termination { | ||
| 1598 | SlaveTermination::Stop => Self::clear_stop_flag(info), | ||
| 1599 | SlaveTermination::Restart => {}, // Leave ADDR for next operation | ||
| 1600 | SlaveTermination::Nack => { | ||
| 1616 | info.regs.sr1().write(|reg| { | 1601 | info.regs.sr1().write(|reg| { |
| 1617 | reg.0 = !0; | 1602 | reg.0 = !0; |
| 1618 | reg.set_af(false); | 1603 | reg.set_af(false); |
| 1619 | }); | 1604 | }); |
| 1620 | return Poll::Ready(Ok(())); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | if sr1.stopf() { | ||
| 1624 | trace!("I2C slave: STOP condition detected during padding"); | ||
| 1625 | Self::clear_stop_flag(info); | ||
| 1626 | return Poll::Ready(Ok(())); | ||
| 1627 | } | ||
| 1628 | |||
| 1629 | if sr1.addr() { | ||
| 1630 | trace!("I2C slave: RESTART condition detected during padding"); | ||
| 1631 | return Poll::Ready(Ok(())); | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | // If transmit buffer is empty, send a padding byte | ||
| 1635 | if sr1.txe() { | ||
| 1636 | info.regs.dr().write(|w| w.set_dr(0x00)); | ||
| 1637 | padding_bytes_sent += 1; | ||
| 1638 | trace!("I2C slave: sent padding byte #{}", padding_bytes_sent); | ||
| 1639 | // Continue padding until transaction ends | ||
| 1640 | Self::enable_interrupts(info); | ||
| 1641 | return Poll::Pending; | ||
| 1642 | } | 1605 | } |
| 1643 | |||
| 1644 | // No immediate termination or transmit request, keep waiting | ||
| 1645 | Self::enable_interrupts(info); | ||
| 1646 | Poll::Pending | ||
| 1647 | } | 1606 | } |
| 1607 | return Poll::Ready(Ok(padding_count)); | ||
| 1648 | } | 1608 | } |
| 1649 | }); | 1609 | |
| 1650 | 1610 | // If transmit buffer is empty, send padding byte | |
| 1651 | // Wait for transaction to actually end | 1611 | if sr1.txe() { |
| 1652 | padding_phase.await?; | 1612 | info.regs.dr().write(|w| w.set_dr(0x00)); |
| 1653 | 1613 | padding_count += 1; | |
| 1654 | let total_bytes_sent = data.len() + padding_bytes_sent; | 1614 | Self::enable_interrupts(info); |
| 1655 | trace!("I2C slave: read complete after sending {} padding bytes, total {} bytes sent", | 1615 | return Poll::Pending; |
| 1656 | padding_bytes_sent, total_bytes_sent); | 1616 | } |
| 1657 | drop(on_drop); | 1617 | |
| 1658 | Ok(total_bytes_sent) | 1618 | Self::enable_interrupts(info); |
| 1659 | } | 1619 | Poll::Pending |
| 1660 | Either::Second(Ok(())) => { | 1620 | } |
| 1661 | trace!("I2C slave: I2C event (STOP/NACK/RESTART) occurred before DMA completion"); | ||
| 1662 | critical_section::with(|_| { | ||
| 1663 | info.regs.cr2().modify(|w| w.set_dmaen(false)); | ||
| 1664 | }); | ||
| 1665 | |||
| 1666 | // Transaction ended normally before all data was sent | ||
| 1667 | let bytes_sent = data.len(); | ||
| 1668 | trace!("I2C slave: read complete via early I2C event, sent {} bytes", bytes_sent); | ||
| 1669 | drop(on_drop); | ||
| 1670 | Ok(bytes_sent) | ||
| 1671 | } | 1621 | } |
| 1672 | }; | 1622 | }).await |
| 1673 | |||
| 1674 | dma_result | ||
| 1675 | } | 1623 | } |
| 1676 | } | 1624 | } |
| 1677 | 1625 | ||
