I think this is overlooked quite often. Forgetting that the software can only interface with the data coming from and going to the hardware.
The random characters on the display to me sounds exactly like this is the problem: multiple tasks are accessing a hardware resource “at the same time” (or rather in their respective time slices) and as a consequence the buffers are getting filled with invalid data.
One possible solution could be to have a task monitoring a queue which contains information about what to display etc., anything wanting to write to the display should then place an item onto the queue.
This would ensure that your hardware resource is only accessed by one task.