You should not try to read the LEUART in the IRQ handler!
The TD SDK programming model is using a non-preemptive multi-tasking model, i.e. you won't be interrupted (preempted) randomly in a task to give back the CPU to another task by a dedicated timer running in a privileged mode.
OTOH, this means that your task has to be nice and collaborate with other ones, and give back the CPU on a voluntary manner for the system to continue working. This is generally not a problem with embedded systems, where tasks are pre-defined and cooperate to do the job.
This also means that you should not be blocking in your task, and in particular that you should not go to sleep within your task, or this will then add preemption points to the single existing one in the main loop, where you possibly would have to handle all possible events upon wake up in several places in the code, leading you to complex reentrancy problems.
This mode of operation was chosen because it is the only one that works for ultra-low power operation, where you just can't afford to have a cyclic timer waking you up to do nothing because of the increased power consumption. Another good reason is that each preempted task would require a large amount of RAM to store the task status (all CPU registers, stack, heap if there are real tasks and not simpler threads, file / stream descriptors...), where we only have 16 KB RAM total.
Besides this collaborative multi-tasking model, there is another concept used by the TD SDK: it uses 2 separate processing domains for handling asynchronous events:
- The hard real-time interrupt domain: this is where the IRQ handlers are located, The processing here must be as fast as possible and non blocking in order to guarantee the lowest possible IRQ latency, This is to keep the time required for an IRQ to be taken into account to the minimum possible, i.e. to keep good responsiveness
- The soft real-time event domain: this is where the main loop (TD_USER_Loop()) is called: even if you need to be decently fast here to do the required job, you don't need to be as fast as when in the interrupt domain. This is where the main processing is taking place.
In your case, you need to separate your processing between interrupt and event domains: you should not try to read characters directly into the IRQ handler: instead, only set a volatile flag there. Upon IRQ handler exit, the TD_USER_Loop() function will eventually be called back in the relaxed event domain, where you can check for this flag and read characters on the UART, then reset the flag for further processing, or give hand to other tasks if you don't have anything to do.
However, you should not need to go so low in the API to perform the task of reading characters on the UART: by default, the CPU is in sleep mode and will be awaken when a character is received on the UART, the LEUART IRQ handler will be called in the interrupt domain, put the read characters in a FIFO, then the TD_USER_Loop() function will be called while in the event domain and will check for available characters and perform the required processing.
If you really want to check for incoming characters at strict intervals, you would better use the scheduler to register a callback function that will be called at regular intervals in the relaxed event domain to check for incoming characters on the UART.