По опыту скажу: крайне опасно обращаться к аппаратному ресурсу - в данном случае к конкретному регистру железа - из разных мест (задач RTOS или обработчиков прерываний).
В кооперативном планировщике это допустимо, но, все равно, не желательно (нарушается логичность кода), а в вытесняющем, как у FreeRTOS - вообще нельзя!
Но, если уж очень хочеться так делать (бывает, что просто нет другого выбора), то нужно такие обращения делать атомарными, т.е. непрерывными - например, использовать критическую секцию, или мьютекс (предпочтительнее).
В данном случае это удобно оформить в виде сервиса RTOS - флага (типа команда, вкл/выкл светодиод) или сообщения (так сделано в Windows).
Если светодиодов много, то лучше - флаги: одно битовое поле - один светодиод.
Тогда управлять светодиодами можно безопасно из любой задачи, а одна задача просто постоянно ждет изменения любого из флагов (имеется ввиду использование сервиса ожидания события RTOS), а остальные задачи в любой момент посылают соотв. сообщение.
В этом случае доступ к железу исключительно атомарен, безопасность достигнута и, просто, красиво построен сам код - легче отлаживать и исправлять ошибки.
Если посмотреть код асемблера, то такая строчка
Код
GPIOB->ODR &= 0x03FF; // LED ON
выйдет в ряд команд ассемблера.
Если бы эта же строчка занимала одну команду процессора, то проблем бы не было.