上一章用IDE创建了包含2个应用的FreeRTOS工程,工程代码中关于应用的部分代码是空的,待开发者自己实现。
本章就通过实现一个LED灯闪烁来介绍一下FreeRTOS下的应用编程。
1.系统初始化介绍
int main ( void )
{
/* Initialize all modules */
SYS_Initialize ( NULL );
while ( true )
{
/* Maintain state machines of all polled MPLAB Harmony modules. */
SYS_Tasks ( );
}
/* Execution should not come here during normal operation */
return ( EXIT_FAILURE );
}
这里是IDE生成的main函数部分,首先是SYS_Initialize函数,主要是中断、时钟、cache、ram、各类外设的初始化。
void SYS_Initialize ( void* data )
{
/* MISRAC 2012 deviation block start */
/* MISRA C-2012 Rule 2.2 deviated in this file. Deviation record ID - H3_MISRAC_2012_R_2_2_DR_1 */
/* Start out with interrupts disabled before configuring any modules */
(void)__builtin_disable_interrupts();
CLK_Initialize();
/* Configure KSEG0 as cacheable memory. This is needed for Prefetch Buffer */
__builtin_mtc0(16, 0,(__builtin_mfc0(16, 0) | 0x3U));
/* Configure Flash Wait States and Prefetch */
CHECONbits.PFMWS = 1;
CHECONbits.PREFEN = 3;
/* Set the SRAM wait states to One */
BMXCONbits.BMXWSDRM = 1;
/* Configure Debug Data Port */
DDPCONbits.JTAGEN = 0;
GPIO_Initialize();
CORETIMER_Initialize();
/* MISRAC 2012 deviation block start */
/* Following MISRA-C rules deviated in this block */
/* MISRA C-2012 Rule 11.3 - Deviation record ID - H3_MISRAC_2012_R_11_3_DR_1 */
/* MISRA C-2012 Rule 11.8 - Deviation record ID - H3_MISRAC_2012_R_11_8_DR_1 */
/* MISRA C-2012 Rule 11.3, 11.8 deviated below. Deviation record ID -
H3_MISRAC_2012_R_11_3_DR_1 & H3_MISRAC_2012_R_11_8_DR_1*/
sysObj.sysTime = SYS_TIME_Initialize(SYS_TIME_INDEX_0, (SYS_MODULE_INIT *)&sysTimeInitData);
/* MISRAC 2012 deviation block end */
/* MISRAC 2012 deviation block end */
LED_Initialize();
EEPROM_Initialize();
EVIC_Initialize();
/* Enable global interrupts */
(void)__builtin_enable_interrupts();
/* MISRAC 2012 deviation block end */
}
2.GPIO模式配置
要实现一个LED灯的闪烁,我们需要初始化控制led电路的开关的GPIO为输出模式,然后再定时改变GPIO输出的高低电平即可实现LED的闪烁。在GPIO_Initialize函数中进行IO的模式设定。
实现IO的模式可通过2种方式,直接操作寄存器实现或者通过API实现。
以A0为例子,通过寄存器实现:
_TRISA0 = 0; //A0为输出模式
_RA0 = 0; //A0输出低电平
亦可直接调用API实现,打开plib_gpio.h文件,发现
static inline void GPIO_PinOutputEnable(GPIO_PIN pin)
{
GPIO_PortOutputEnable((GPIO_PORT)(pin>>4U), 0x1UL << (pin & 0xFU));
}
void GPIO_PortOutputEnable(GPIO_PORT port, uint32_t mask)
{
*(volatile uint32_t *)(&TRISACLR + (port * 0x10U)) = mask;
}
此API是通过先对PIC32MX795的112个GPIO进行编号,通过序号来找到对应IO寄存器地址实现模式的设置。A0是第一个IO,初始化代码如下:
GPIO_PinOutputEnable(GPIO_PIN_RA0);//头文件中已经对112个IO进行了宏定义
GPIO_PinClear(GPIO_PIN_RA0); // A0设为低电平
3. Led 任务初始化
typedef struct
{
/* The application's current state */
LED_STATES state;
/* TODO: Define any additional data used by the application. */
} LED_DATA;
void LED_Initialize ( void )
{
/* Place the App state machine in its initial state. */
ledData.state = LED_STATE_INIT;
/* TODO: Initialize your application's state machine and other
* parameters.
*/
}
这里只是初始化了ledData的状态为初始化状态。
3.系统任务函数
void SYS_Tasks ( void )
{
/* Maintain system services */
/* Maintain Device Drivers */
/* Maintain Middleware & Other Libraries */
/* Maintain the application's state machine. */
/* Create OS Thread for LED_Tasks. */
(void) xTaskCreate((TaskFunction_t) lLED_Tasks,
"LED_Tasks",
1024,
NULL,
1,
&xLED_Tasks);
/* Create OS Thread for EEPROM_Tasks. */
(void) xTaskCreate((TaskFunction_t) lEEPROM_Tasks,
"EEPROM_Tasks",
1024,
NULL,
1,
&xEEPROM_Tasks);
/* Start RTOS Scheduler. */
/**********************************************************************
* Create all Threads for APP Tasks before starting FreeRTOS Scheduler *
***********************************************************************/
vTaskStartScheduler(); /* This function never returns. */
}
此函数内使用了xTaskCreate函数创建了2个线程,并开启任务调度。
/* Handle for the LED_Tasks. */
TaskHandle_t xLED_Tasks;
static void lLED_Tasks( void *pvParameters )
{
while(true)
{
LED_Tasks();
vTaskDelay(100U / portTICK_PERIOD_MS);
}
}
lLED_Tasks内每100ms执行以下LED_Tasks函数。
void LED_Tasks ( void )
{
/* Check the application's current state. */
switch ( ledData.state )
{
/* Application's initial state. */
case LED_STATE_INIT:
{
bool appInitialized = true;
if (appInitialized)
{
ledData.state = LED_STATE_SERVICE_TASKS;
}
break;
}
case LED_STATE_SERVICE_TASKS:
{
break;
}
/* TODO: implement your application state machine.*/
/* The default state should never be executed. */
default:
{
/* TODO: Handle error in application's state machine. */
break;
}
}
}
4.实现LED闪烁
LED_Tasks分为2种模式,init模式和service模式。可以在init模式进行IO的设置。由于我们之前在GPIO_Initialize函数中已经对IO进行了初始化设置。这里就暂不做处理了。
在service模式下编写功能即可。在之前的LED_DATA结构体内增加一个io状态变量。
typedef struct
{
/* The application's current state */
LED_STATES state;
uint8_t io_status;
/* TODO: Define any additional data used by the application. */
} LED_DATA;
实现IO状态翻转
void LED_Tasks ( void )
{
/* Check the application's current state. */
switch ( ledData.state )
{
/* Application's initial state. */
case LED_STATE_INIT:
{
bool appInitialized = true;
if (appInitialized)
{
ledData.state = LED_STATE_SERVICE_TASKS;
}
break;
}
case LED_STATE_SERVICE_TASKS:
{
if(ledData.io_status == 0)
GPIO_PinClear(GPIO_PIN_RA0);
else
GPIO_PinSet(GPIO_PIN_RA0);
ledData.io_status = !ledData.io_status;
break;
}
/* TODO: implement your application state machine.*/
/* The default state should never be executed. */
default:
{
/* TODO: Handle error in application's state machine. */
break;
}
}
}
此处需要在led.c中添加“ #include “config/default/peripheral/gpio/plib_gpio.h” ”,解决编译错误。
编译完成,程序烧录完成后led即开始闪烁。