32F417: How to disable interrupts - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: June 16, 2022, 08:33:39 am
content: I've been having a bit of fun with thishttps://www.eevblog.com/forum/microcontrollers/lyontek-ly68l6400-8-megabyte-spi-sram-am-i-reading-the-data-sheet-right/I was getting errors when an interrupt occurred between these two points, and lengthened the CS=0 durationSo I went digging around to see how to do this. Some existing code (written by someone else) usesCode:portENTER_CRITICAL();portEXIT_CRITICAL();and that didn't work in this case. It definitely didn't disable interrupts. Looking at the code for the above, they seem to just raise some interrupt priority, which may work to disable interrupts of the same priority, onlyCode:portFORCE_INLINE static void vPortRaiseBASEPRI( void ){uint32_t ulNewBASEPRI;__asm volatile("mov %0, %1\n"\"msr basepri, %0\n" \"isb\n" \"dsb\n" \:"=r" (ulNewBASEPRI) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory");}What works isCode:__disable_irq()__enable_irq()Code:/**\brief Disable IRQ Interrupts\details Disables IRQ interrupts by setting the I-bit in the CPSR.Can only be executed in Privileged modes.*/__STATIC_FORCEINLINE void __disable_irq(void){__ASM volatile ("cpsid i" : : : "memory");}For the same reason I also need to stop the RTOS switching tasks, and triedCode:vTaskSuspendAll()xTaskResumeAllwhich might work; not tested it yet. One could achieve that bit with a mutex, which is necessary anyway if using the same SPI controller for multiple SPI slaves.There is a lot of stuff online about this and most of it doesn't work

author: nctnico
author_rank: Super Contributor
timestamp: June 16, 2022, 08:42:19 am
content: If you disable the interrupts, then the RTOS won't get a timer interrupt to do a task switch so disabling the interrupts is killing two birds with one stone. The problem I foresee though is that interrupts may be blocked too long if those DMA transfers take long to execute.

author: peter-h
author_rank: Super Contributor
timestamp: June 16, 2022, 08:48:07 am
content: QuoteYes I was just about to say thatQuite a good solution.The DMA transfers, I can control. Typically that period is 10us or so (16 byte transfers; more or less within the CS=0 spec of that PSRAM). It could go to 200us if doing 512 bytes (well outside the chip spec but apparently fine; see the other link).

author: Jeroen3
author_rank: Super Contributor
timestamp: June 16, 2022, 08:48:59 am
content: The __disable_irq() intrinsics callingCPS instructionrequire you to be inprivileged modeand can't be used inside threads who run inuser mode.The only way from user mode to get reduced interrupts (for data integrity) is to change the priorities.If you require specific timing of a small section you may want to run that in privileged mode, as a driver, inside an interrupt.Not sure how freertos does things, but you can make software interrupt requests.

author: peter-h
author_rank: Super Contributor
timestamp: June 16, 2022, 09:11:40 am
content: I suspect the whole project runs in privileged mode, because this clearly works.

author: agehall
author_rank: Frequent Contributor
timestamp: June 16, 2022, 10:47:56 am
content: Isn’t the SPI transaction supposed to be done with one call, i.e. transmitting both the command and the data in a single call to SPI3_DMA_TransmitReceive()?

author: peter-h
author_rank: Super Contributor
timestamp: June 16, 2022, 11:25:49 am
content: Not necessary with SPI. See the scope pics herehttps://www.eevblog.com/forum/microcontrollers/lyontek-ly68l6400-8-megabyte-spi-sram-am-i-reading-the-data-sheet-right/msg4228621/#msg4228621All that happens is that the SPI clock stops for a bit. That is normally OK. The way it works is that you "connect" DMA TX+RX to SPI TX+RX and then when you enable both, it runs until the transfer count is 0. Then to do more bytes you reload the transfer count, and re-enable both peripherals. The gap doesn't matter.Doing it in one DMA transfer is an obvious speed optimisation. It involves concatenating the command+address stringCode:static uint8_t readcmd = {0x03,0,0,0};// basic read command and 3 byte addressstatic uint8_t writecmd = {0x02,0,0,0};// basic write command and 3 byte addressstatic uint8_t readid = {0x9f,0,0,0};// Read ID. Send 9F and any 3 byte addressstatic uint8_t id = {0,0,0,0,0,0,0,0,0};// ID gets read into herewith the data buffer. This may or may not be convenient, because you have to write your code so that every possible piece of data has the appropriate above string in front of it.The next optimisation is putting the DMA code inline and putting the CE=0 and CE=1 GPIO pin setting in the optimal places. I might still do it but, as you can see on the scope pics, on 512 byte packets it is not worth the effort.CE waggling to a real hassle. There is the NSS feature; most of the many posts on google suggest it doesn't work usefully.

author: mikerj
author_rank: Super Contributor
timestamp: June 16, 2022, 02:11:43 pm
content: portENTER_CRITICAL() and portEXIT_CRITICAL() are used to create a critical section where the code cannot be pre-empted by the RTOS (e.g. task switch). Interrupts with priority below configMAX_SYSCALL_INTERRUPT_PRIORITY (i.e. interrupts that are permitted to call RTOS API functions) will be disabled.One thing that seems odd to me is an SPI device having a maximum time between asserting CS and the first clock, are you sure this is a real thing? I had a quick look at the datasheet and could only see Tcsp (CE# setup time to CLK rising edge) which has a minimum time of 2.5ns, and no maximum.

author: peter-h
author_rank: Super Contributor
timestamp: June 16, 2022, 02:55:51 pm
content: QuoteI struggle to understand this, because I thought thatnonormal RTOS API can be called from an ISR. Certainly I am not doing that anywhere (although there are huge libs like the ST ETH one).I can see lots of references to it, with this being the main one, in freertosconfig.hCode:#define configMAX_SYSCALL_INTERRUPT_PRIORITY( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )QuoteI have probably described it badly. The SPI PSRAM (LY68L6400) has a max time ofCE=0of8us. So you are supposed to do the whole SPI transaction in 8us. As I show in the other thread there is a huge latitude on this limit but it is heavily temperature dependent, and if you go way over 8us (say 200us, corresponding to a 512 byte packet at 21MHz SPI clock) then you start to approach the area where things might be a bit tight, if your product internals get anywhere near +70C. But 512 bytes still seems "unbreakable", assisted no doubt by the total lack of self heating at just 21MHz. But if you then get something jumping in while CS=0 for say 15ms that does break it.Obviously it can be interrupts. You will say 15ms for an ISR is ridiculous, and I agree, not elegant or purist, but sometimes the workaround is incredibly complex and just leaving it doesn't cause problems - this is what I am doing, and the 15ms ISR occurs in very rare scenarios ofwritingto the FatFS file system:https://www.eevblog.com/forum/microcontrollers/cleanest-way-to-block-usb-interrupts/msg3925778/#msg3925778Readingthe file system produces a 200us ISR which is much better, and I can't break it without a hot air gun, but it still stretches the "8us max" CE=0 by 200us, which "works at all reasonable temperatures"Or it can be RTOS task switching. I have thisconfigUSE_TIME_SLICING defaults to 1 (it is defined in FreeRTOS.h if not already defined - NOT defined currently) and we do not override it in FreeRTOSConfig.h. This means that task switching will occur pre-emptively between *equal* priority tasks on the FreeRTOS time slice timerBottom line is that I know that any packet of 64 bytes or less will be completely unbreakable at any temperature at which the box starts to deformbut not if the 200us or especially the 15ms ISR can jump in, or the RTOS switches tasks.

author: mikerj
author_rank: Super Contributor
timestamp: June 16, 2022, 03:35:36 pm
content: Quote from: peter-h on June 16, 2022, 02:55:51 pmFreeRTOS API callscanbe made from interrupts provided both conditions are met:1) The interrupt priority is at or below configMAX_SYSCALL_INTERRUPT_PRIORITY. The FreeRTOS scheduler will temporarily disable all these interrupts when required for data safety (e.g. during context switches) so these interrupt can have higher latency than you might expect. Interrupts with priorities higher than this must not call RTOS API functions.2) The ISR specific version of the API function is used e.g. xSemaphoreTakeFromISR(), xQueueSendFromISR(), xTaskNotifyFromISR().Quote from: peter-h on June 16, 2022, 02:55:51 pmOk I can see tCEM= 8us max, a little inconvenient for sure. After a power on reset the Cortex-M thread mode is configured for privileged access so you can use the __disable_irq(), __enable_irq() intrinsics mentioned previously.

author: newbrain
author_rank: Super Contributor
timestamp: June 16, 2022, 07:53:44 pm
content: QuoteFor FreeRTOS, there's a separate set of API that can be called from a ISR with a lower (numerically higher) priority than configMAX_SYSCALL_INTERRUPT_PRIORITY, as many have said above.Other RTOSes may not even have the need for a specific priority or a separate set of API, e.g. in AzureRTOS (ThreadX) one can more or less call any messaging API, but not API to create new objects (queue, mutex, etc.).