In the github issue, existed a discussion about call mutex for access BaseType_t type in some places of the FreeRTOS-Kernel.
The BaseType_t on FreeRTOS-Kernel is a data type used for storage integer values and is used a lot in all the FreeRTOS. This is set to fit the size of the CPU architecture that is running FreeRTOS.
Some files, before access the BaseType_t type, call the mutex like this:
BaseType_t ret; taskENTER_CRITICAL(); { ret = get_any_value(); } taskEXIT_CRITICAL();The programmers did this for controll access for get any value and storage this in a BaseType_t variable.
Instead of doing a change in a way which impacts all the CPU architecture, we can introduce this gragually on a port-by-port basis.
How the FreeRTOS-Kernel have your hardware-dependent code splited from hardware-independent code, then we can take advantage of this decoupling.
The portable.h file, is a interface to portmacro.h file which is a hardware-dependent. The portable.h is hardware-independent. Each portmacro.h in your architecture implements a portable.h how you can see in portable/{COMPILER}/{CPU}/portmacro.h. One example is: portable/GCC/ATMega323/portmacro.h
So, if the portable.h is a hardware-independent file, then I can declare is this header file this macros:
#ifndef portBASE_TYPE_ENTER_CRITICAL #define portBASE_TYPE_ENTER_CRITICAL() taskENTER_CRITICAL() #endif #ifndef portBASE_TYPE_EXIT_CRITICAL #define portBASE_TYPE_EXIT_CRITICAL() taskEXIT_CRITICAL() #endifYou can see that the portBASE_TYPE_ENTER_CRITICAL call the taskENTER_CRITICAL() if portBASE_TYPE_ENTER_CRITICAL is not define yet.
#define portBASE_TYPE_ENTER_CRITICAL() #define portBASE_TYPE_EXIT_CRITICAL()In this form, the taskENTER_CRITICAL is not call, because the portBASE_TYPE_ENTER_CRITICAL is already defined.
For test in ATMega328p:
First I create this FreeRTOSConfig.h
#ifndef FREERTOS_CONFIG_H #define FREERTOS_CONFIG_H #define configUSE_PREEMPTION 1 #define configUSE_IDLE_HOOK 0 #define configUSE_TICK_HOOK 0 #define configCPU_CLOCK_HZ ( 16000000UL ) #define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) #define configMAX_PRIORITIES ( 4 ) #define configMINIMAL_STACK_SIZE ( 85 ) #define configTOTAL_HEAP_SIZE ( ( size_t ) ( 1024 ) ) #define configMAX_TASK_NAME_LEN ( 8 ) #define configUSE_TRACE_FACILITY 0 #define configUSE_16_BIT_TICKS 1 #define configIDLE_SHOULD_YIELD 1 #define configUSE_MUTEXES 1 #define configQUEUE_REGISTRY_SIZE 0 #define configCHECK_FOR_STACK_OVERFLOW 0 #define configUSE_RECURSIVE_MUTEXES 1 #define configUSE_MALLOC_FAILED_HOOK 0 #define configUSE_APPLICATION_TASK_TAG 0 #define configUSE_COUNTING_SEMAPHORES 1 #define configSUPPORT_DYNAMIC_ALLOCATION 1 #define INCLUDE_uxTaskPriorityGet 1 #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY 1 #define configTIMER_QUEUE_LENGTH 1 #define configUSE_TIMERS 1 #define configTIMER_TASK_STACK_DEPTH 1 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 #define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); } #endif /* FREERTOS_CONFIG_H */Then, I create this simple Makefile:
MCU = atmega328p F_CPU = 16000000UL CC = avr-gcc OBJCOPY = avr-objcopy CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall -Os LDFLAGS = -mmcu=$(MCU) -lm SRC = main.c help.c FreeRTOS-Kernel/timers.c FreeRTOS-Kernel/portable/MemMang/heap_1.c FreeRTOS-Kernel/portable/GCC/ATMega323/port.c FreeRTOS-Kernel/list.c FreeRTOS-Kernel/queue.c FreeRTOS-Kernel/tasks.c TARGET = main all: $(TARGET).hex $(TARGET).elf: $(SRC) $(CC) $(CFLAGS) -o $@ $(SRC) %.hex: %.elf $(OBJCOPY) -O ihex -R .eeprom $< $@ clean: rm -f *.o *.elf *.hexFor help me with some things like print in serial port I create this help.c
#include <avr/interrupt.h> #include "help.h" #includeand this help.h#include void serial_init(unsigned int ubrr) { UBRR0H = (unsigned char)(ubrr >> 8); UBRR0L = (unsigned char)ubrr; UCSR0B = (1 << RXEN0) | (1 << TXEN0); UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); } static void USART_Transmit(unsigned char data) { while (!(UCSR0A & (1 << UDRE0))); UDR0 = data; } void serial_print(const char *str) { while (*str) { USART_Transmit(*str++); } } void to_str(unsigned int from, char *to) { sprintf(to, "%u", (unsigned int) from); }
#ifndef __HELP_H__ #define __HELP_H__ void serial_init(unsigned int ubrr); void serial_print(const char *str); void to_str(unsigned int from, char *to); #endif
#define __AVR_ATmega328P__ #define F_CPU 16000000UL #include "FreeRTOS-Kernel/Arduino_FreeRTOS.h" #include "FreeRTOS-Kernel/include/task.h" #include "help.h" #define MYUBRR F_CPU/16/9599 void vTask1(void *pvParameters); int main(void) { serial_init(MYUBRR); /// test a simple task create xTaskCreate(vTask1, "task1", 128, NULL, 1, NULL); vTaskStartScheduler(); while(1); } void vTask1(void *pvParameters) { serial_print("simple test about create a simple test..."); }
avrdude -c arduino -P /dev/ttyUSB0 -b 115200 -p m328p -U flash:w:main.hexI use the picocom for monitoring of serial port:
picocom -b 9600 /dev/ttyUSB0
#define __AVR_ATmega328P__ #define F_CPU 16000000UL #include <avr/io.h> #include <util/delay.h> #include <stdlib.h> #include "FreeRTOS-Kernel/Arduino_FreeRTOS.h" #include "FreeRTOS-Kernel/include/queue.h" #include "help.h" #define MYUBRR F_CPU/16/9599 QueueHandle_t xQueue; int main(void) { serial_init(MYUBRR); char *str = (char*) malloc(sizeof(char)); xQueue = xQueueCreate(10, sizeof(int)); if (xQueue == NULL) { serial_print("fail\n"); return 1; } serial_print("queue created\n"); serial_print("messages in the queue (expected 0): "); to_str(uxQueueMessagesWaiting(xQueue), str); serial_print(str); // should print 0 serial_print("\n"); int valor = 42; for (int i = 0; i < 3; i++) { xQueueSend(xQueue, &valor, 0); } serial_print("messeges in the queue after added 3 elements (expected 3): "); to_str(uxQueueMessagesWaiting(xQueue), str); serial_print(str); // sould print 3 serial_print("\n"); int recebido; xQueueReceive(xQueue, &recebido, 0); serial_print("messages in the queue after remove 1 element (expected 2): "); to_str(uxQueueMessagesWaiting(xQueue), str); serial_print(str); // should print 2 serial_print("\n"); serial_print("uxQueueSpacesAvailable (expected 8): "); to_str(uxQueueSpacesAvailable(xQueue), str); serial_print(str); serial_print("\n"); while(1); }
#define __AVR_ATmega328P__ #define F_CPU 16000000UL #include <stdlib.h> #include "FreeRTOS-Kernel/Arduino_FreeRTOS.h" #include "FreeRTOS-Kernel/include/task.h" #include "help.h" #define MYUBRR F_CPU/16/9599 void vTask1(void *pvParameters); int main(void) { serial_init(MYUBRR); TaskHandle_t xHandle = NULL; /// test a simple task create xTaskCreate(vTask1, "task1", 128, NULL, 1, &xHandle); serial_print("task priority (expected 1): "); UBaseType_t res = uxTaskPriorityGet(xHandle); char *str = (char*) malloc(sizeof(char)); to_str(res, str); serial_print(str); serial_print("\n"); serial_print("task base priority (expected 1): "); UBaseType_t res_base = uxTaskBasePriorityGet(xHandle); to_str(res_base, str); serial_print(str); serial_print("\n"); vTaskStartScheduler(); } void vTask1(void *pvParameters) { serial_print("simple test about create a simple test..."); }
#define __AVR_ATmega328P__ #define F_CPU 16000000UL #include <stdlib.h> #include "FreeRTOS-Kernel/Arduino_FreeRTOS.h" #include "FreeRTOS-Kernel/include/timers.h" #include "help.h" #define MYUBRR F_CPU/16/9599 TimerHandle_t periodicTimer; TimerHandle_t oneShotTimer; void vPeriodicTimerCallback(TimerHandle_t xTimer) { serial_print("temp added\n"); } void vOneShotTimerCallback(TimerHandle_t xTimer) { serial_print("temp no periodic added\n"); } int main(void) { serial_init(MYUBRR); periodicTimer = xTimerCreate("PeriodicTimer", pdMS_TO_TICKS(1000), pdTRUE, 0, vPeriodicTimerCallback); if (periodicTimer == NULL) { serial_print("fail\n"); return 1; } oneShotTimer = xTimerCreate("OneShotTimer", pdMS_TO_TICKS(1000), pdFALSE, 0, vOneShotTimerCallback); if (oneShotTimer == NULL) { serial_print("fail\n"); return 1; } xTimerStart(periodicTimer, 0); xTimerStart(oneShotTimer, 0); serial_print("Periodic timer recharge mode (expect - periodical): "); BaseType_t periodic = xTimerGetReloadMode(periodicTimer); char *str = periodic == pdTRUE ? "periodical" : "not periodical"; serial_print(str); serial_print("\n"); serial_print("Non-periodic timer recharge mode (expect - not periodical): "); BaseType_t nperiodic = xTimerGetReloadMode(oneShotTimer); char *nstr = periodic == pdFALSE ? "periodical" : "not periodical"; serial_print(nstr); serial_print("\n"); char* astr = (char*) malloc(sizeof(char)); serial_print("The timer is active? (expected 0): "); BaseType_t act = xTimerIsTimerActive(oneShotTimer); to_str(act, astr); serial_print(astr); serial_print("\n"); }
diff --git a/portable/GCC/ATMega323/portmacro.h b/portable/GCC/ATMega323/portmacro.h index 6ed5e4295..131819004 100644 --- a/portable/GCC/ATMega323/portmacro.h +++ b/portable/GCC/ATMega323/portmacro.h @@ -61,6 +61,8 @@ #define portSTACK_TYPE uint8_t #define portBASE_TYPE char +#include "help.h" /// this is the help.h that I mentionated in the PR description + #define portPOINTER_SIZE_TYPE uint16_t typedef portSTACK_TYPE StackType_t; @@ -78,13 +80,18 @@ typedef unsigned char UBaseType_t; #endif /*-----------------------------------------------------------*/ +#define portBASE_TYPE_ENTER_CRITICAL() +#define portBASE_TYPE_EXIT_CRITICAL() + /* Critical section management. */ #define portENTER_CRITICAL() \ + serial_print("\nenter critical\n"); \ asm volatile ( "in __tmp_reg__, __SREG__" ::); \ asm volatile ( "cli" ::); \ asm volatile ( "push __tmp_reg__" ::) #define portEXIT_CRITICAL() \ + serial_print("\nexit critical\n"); \ asm volatile ( "pop __tmp_reg__" ::); \ asm volatile ( "out __SREG__, __tmp_reg__" ::)
/// #define portBASE_TYPE_ENTER_CRITICAL() /// #define portBASE_TYPE_EXIT_CRITICAL()Then, the portENTER_CRITIAL() should execute .. Then in your console, you can see this:
messages in the queue (expected 0): enter critical exit critical 0
I didn't come up with this solution alone, I had help from the Amazon team allocated to FreeRTOS.
I would like to thanks:
- n9wxu
- svenbieg
- richard-damon
- kar-rahul-aws
- aggarg