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()
#endif
You 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