i.mx6ull 官方SDK学习与移植(2)中已经搭建起了裸板学习的框架,剩下的就是按部就班,参照正点原子的裸板学习教程,试验每一个例子。
看完正点原子《IMX6U嵌入式Linux驱动开发指南》教程,对照NXP官方例程,就会发现裸板试验代码其实也并不复杂。在理解对时钟、IO控制、外设原理等的基础上,使用很少的代码就可以完成裸板试验。
对按键试验、GPIO中断、高精度时延、定时中断、I2C读写就不做多写了,套路基本一致。
#include "fsl_gpio.h"
#include "fsl_debug_console.h"
#include "CommonHelper.h"
#include "board.h"
#include "key.h"
extern uint8_t led_cnt;
extern bool g_InputSignal;
/*按键初始化函数*/
void key_init(void)
{
/*添加中断服务函数到 "中断向量表"*/
SystemInstallIrqHandler(BOARD_KEY_IRQ, (system_irq_handler_t)GPIO5_Combined_0_15_IRQHandler, NULL);
gpio_pin_config_t gpio_config;
gpio_config.direction = kGPIO_DigitalInput;
gpio_config.outputLogic = 0;
gpio_config.interruptMode = kGPIO_IntRisingEdge;
/* Init input switch GPIO. */
EnableIRQ(BOARD_KEY_IRQ);
GPIO_PinInit(BOARD_KEY_GPIO, BOARD_KEY_GPIO_PIN, &gpio_config);
/* Enable GPIO pin interrupt */
GPIO_EnableInterrupts(BOARD_KEY_GPIO, 1U << BOARD_KEY_GPIO_PIN);
PRINTF("\r\nKey Interrupt installed.\r\n");
}
/*按键中断处理函数*/
void Key_GPIO_IRQHandler(void)
{
GPIO_ClearPinsInterruptFlags(BOARD_KEY_GPIO,1U << BOARD_KEY_GPIO_PIN);
/*按键引脚中断服务函数*/
g_InputSignal = true;
led_cnt++;
PRINTF("\r\n %s Key Pressed %d.\r\n",BOARD_KEY_NAME,led_cnt);
}
I2C.h
#ifndef _I2C_H_
#define _I2C_H_
#include "fsl_common.h"
#include "fsl_clock.h"
#include "fsl_i2c.h"
#define I2C1_MASTER_BASEADDR I2C1
#define I2C1_MASTER_CLK_FREQ (CLOCK_GetFreq(kCLOCK_IpgClk) / \
(CLOCK_GetDiv(kCLOCK_PerclkDiv) + 1U))
#define I2C1_MASTER_SLAVE_ADDR_7BIT 0x7EU
#define I2C1_BAUDRATE 100000U
#define I2C1_DATA_LENGTH 32U
i2c_master_handle_t g_m_handle;
volatile bool g_MasterCompletionFlag = false;
void I2C1_Init();
#endif
I2C.c
#include "i2c.h"
#include "fsl_common.h"
#include "fsl_debug_console.h"
#include "fsl_i2c.h"
static void I2C1_Master_Callback(I2C_Type *base, i2c_master_handle_t *handle, status_t status, void *userData)
{
/* Signal transfer success when received success status. */
if (status == kStatus_Success)
{
g_MasterCompletionFlag = true;
}
}
void I2C1_Init()
{
i2c_master_config_t masterConfig;
uint32_t sourceClock;
/*
* masterConfig->baudRate_Bps = 100000U;
* masterConfig->enableHighDrive = false;
* masterConfig->enableStopHold = false;
* masterConfig->glitchFilterWidth = 0U;
* masterConfig->enableMaster = true;
*/
I2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Bps = I2C1_BAUDRATE;
sourceClock = I2C1_MASTER_CLK_FREQ;
I2C_MasterInit(I2C1_MASTER_BASEADDR, &masterConfig, sourceClock);
memset(&g_m_handle, 0, sizeof(g_m_handle));
I2C_MasterTransferCreateHandle(I2C1_MASTER_BASEADDR, &g_m_handle, I2C1_Master_Callback, NULL);
}
I2C回调函数暂时没有写其它的,完成一个状态重置。
I2C_AP3216C.h
#ifndef _I2C_AP3216C_H_
#define _I2C_AP3216C_H_
#include "fsl_common.h"
#define AP3216C_SLAVE_ADDR 0X1E /* AP3216C器件地址 */
/*
* AP3216C操作码定义
*/
typedef enum enAP3216C_OPCODE
{
AP3216C_SYSTEMCONFIG = 0, /* 配置寄存器 */
AP3216C_INTSTATUS = 1, /* 中断状态寄存器 */
AP3216C_INTCLEAR = 2, /* 中断清除寄存器 */
AP3216C_START_ALL = 3, /* 开启全模式ALS/PS/IR */
AP3216C_INIT = 4, /* 复位/初始化 */
AP3216C_IRDATALOW = 10, /* IR数据低字节 */
AP3216C_IRDATAHIGH = 11, /* IR数据高字节 */
AP3216C_ALSDATALOW = 12, /* ALS数据低字节 */
AP3216C_ALSDATAHIGH = 13, /* ALS数据高字节 */
AP3216C_PSDATALOW = 14, /* PS数据低字节 */
AP3216C_PSDATAHIGH = 15, /* PS数据高字节 */
AP3216C_DONOTHING_BULL
} AP3216C_OPCODE;
void AP3216C_Init_IO();
status_t AP3216C_Init(void);
status_t AP3216C_Translate(uint8_t regAddr,i2c_direction_t dr,uint8_t* pData);
status_t AP3216C_Read_IR(uint16_t *ir);
status_t AP3216C_Read_ALS(uint16_t *als);
status_t AP3216C_Read_PS(uint16_t *ps);
status_t AP3216C_Read_Data(uint16_t *ir, uint16_t *ps, uint16_t *als);
#endif
I2C_AP3216C.c
#include "CommonHelper.h"
#include "i2c.h"
#include "i2c_ap3216c.h"
#include "fsl_common.h"
#include "fsl_debug_console.h"
#include "fsl_iomuxc.h"
#include "fsl_gpio.h"
#include "fsl_i2c.h"
#include "board.h"
void AP3216C_Init_IO()
{
/*I2C1 复用UART4 SCL-TXD SDA-RXD 开启 AP3216C的中断,外接SNVS_TAMPER2*/
/*设置 按键引脚的复用功能以及PAD属性*/
IOMUXC_SetPinMux(AP3216C_INT_IOMUXC,0);
IOMUXC_SetPinConfig(AP3216C_INT_IOMUXC, button_PAD_CONFIG_DATA);
gpio_pin_config_t gpio_config;
gpio_config.direction = kGPIO_DigitalInput;
gpio_config.outputLogic = 0;
gpio_config.interruptMode = kGPIO_NoIntmode;
//gpio_config.interruptMode = kGPIO_IntLowLevel;
/* Init input switch GPIO. */
//EnableIRQ(AP3216C_INT_IRQ);
GPIO_PinInit(AP3216C_INT_GPIO, AP3216C_INT_GPIO_PIN, &gpio_config);
/* Enable GPIO pin interrupt */
//GPIO_EnableInterrupts(AP3216C_INT_GPIO, 1U << AP3216C_INT_GPIO_PIN);
//PRINTF("\r\nAP3216C Interrupt installed.\r\n");
}
status_t AP3216C_Translate(uint8_t regAddr,i2c_direction_t dr,uint8_t* pData)
{
i2c_master_transfer_t masterXfer;
masterXfer.slaveAddress = AP3216C_SLAVE_ADDR;
masterXfer.direction = dr;
masterXfer.subaddress = regAddr;
masterXfer.subaddressSize = 1;
masterXfer.data = pData;
masterXfer.dataSize = 1;
masterXfer.flags = kI2C_TransferDefaultFlag;
return I2C_MasterTransferBlocking(I2C1_MASTER_BASEADDR, &masterXfer);
}
status_t AP3216C_Init(void)
{
AP3216C_Init_IO();
status_t status = kStatus_Success;
/*初始化/复位 AP3216C 软复位04*/
uint8_t opcode = AP3216C_INIT;
status = AP3216C_Translate(AP3216C_SYSTEMCONFIG,kI2C_Write,&opcode);
if(status == kStatus_Fail)
{
PRINTF("AP3216C Init Error!\r\n");
return kStatus_Fail;
}
/*手册上写至少等待10ms*/
delay(MSEC_TO_COUNT(15U, EPIT_CLK_FREQ) - 1);
//开启ALS and PS+IR
opcode = AP3216C_START_ALL;
status = AP3216C_Translate(AP3216C_SYSTEMCONFIG,kI2C_Write,&opcode);
if(status == kStatus_Fail)
{
PRINTF("AP3216C Start All Error!\r\n");
return kStatus_Fail;
}
return status;
}
status_t AP3216C_Read_IR(uint16_t *irData)
{
uint8_t ucIrLow;
uint8_t ucIrHigh;
status_t status = kStatus_Success;
status = AP3216C_Translate(AP3216C_IRDATALOW,kI2C_Read,&ucIrLow);
if(status == kStatus_Fail)
{
PRINTF("AP3216C IR Data Low Error!\r\n");
return kStatus_Fail;
}
status = AP3216C_Translate(AP3216C_IRDATAHIGH,kI2C_Read,&ucIrHigh);
if(status == kStatus_Fail)
{
PRINTF("AP3216C IR Data High Error!\r\n");
return kStatus_Fail;
}
if(ucIrLow & 0x80) //bit7 1:无效
{
*irData = 0;
}
else
{
*irData = ((uint16_t)ucIrHigh << 2) | (ucIrLow & 0X03);
}
return status;
}
status_t AP3216C_Read_ALS(uint16_t *alsData)
{
uint8_t ucAlsLow;
uint8_t ucAlsHigh;
status_t status = kStatus_Success;
status =AP3216C_Translate(AP3216C_ALSDATALOW,kI2C_Read,&ucAlsLow);
if(status == kStatus_Fail)
{
PRINTF("AP3216C ALS Data Low Error!\r\n");
return kStatus_Fail;
}
status =AP3216C_Translate(AP3216C_ALSDATAHIGH,kI2C_Read,&ucAlsHigh);
if(status == kStatus_Fail)
{
PRINTF("AP3216C ALS Data High Error!\r\n");
return kStatus_Fail;
}
*alsData = ((uint16_t)ucAlsHigh << 8) | ucAlsLow;
return status;
}
status_t AP3216C_Read_PS(uint16_t *psData)
{
uint8_t ucPsLow;
uint8_t ucPsHigh;
status_t status = kStatus_Success;
status = AP3216C_Translate(AP3216C_PSDATALOW,kI2C_Read,&ucPsLow);
if(status == kStatus_Fail)
{
PRINTF("AP3216C PS Data Low Error!\r\n");
return kStatus_Fail;
}
status = AP3216C_Translate(AP3216C_PSDATAHIGH,kI2C_Read,&ucPsHigh);
if(status == kStatus_Fail)
{
PRINTF("AP3216C PS Data High Error!\r\n");
return kStatus_Fail;
}
if(ucPsLow & 0x40)
{
*psData = 0;
}
else
{
*psData = ((uint16_t)(ucPsHigh & 0X3F) << 4) | (ucPsLow & 0x0F);
}
return status;
}
status_t AP3216C_Read_Data(uint16_t *ir, uint16_t *ps, uint16_t *als)
{
return AP3216C_Read_IR(ir) & AP3216C_Read_ALS(als) & AP3216C_Read_PS(ps);
}
在这里为了简便,没有使用AP3216C的中断功能。可以通过设置阈值,对超出阈值的事件引发中断再进行处理。后续可以继续尝试一下。
野火的开发板配套是5寸液晶屏,通过查阅野火的屏幕资料,可知其使用的屏幕通过LCD-FPC接口(开发板上J16接口)接入核心板。
LCD的驱动主要包括数据线、控制线(包含时钟)、背光等几部分。
LCD_DATA是数据总线。GPIO_8是背光控制总线。LCD_PCLK是时钟,LCD_HSYNC、LCD_VSYNC是行同步信号、帧同步信号,LCD_DE是数据使能。
//LCD引脚
IOMUXC_SetPinMux(IOMUXC_LCD_CLK_LCDIF_CLK, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_CLK_LCDIF_CLK, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA00_LCDIF_DATA00, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA00_LCDIF_DATA00, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA01_LCDIF_DATA01, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA01_LCDIF_DATA01, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA02_LCDIF_DATA02, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA02_LCDIF_DATA02, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA03_LCDIF_DATA03, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA03_LCDIF_DATA03, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA04_LCDIF_DATA04, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA04_LCDIF_DATA04, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA05_LCDIF_DATA05, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA05_LCDIF_DATA05, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA06_LCDIF_DATA06, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA06_LCDIF_DATA06, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA07_LCDIF_DATA07, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA07_LCDIF_DATA07, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA08_LCDIF_DATA08, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA08_LCDIF_DATA08, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA09_LCDIF_DATA09, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA09_LCDIF_DATA09, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA10_LCDIF_DATA10, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA10_LCDIF_DATA10, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA11_LCDIF_DATA11, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA11_LCDIF_DATA11, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA12_LCDIF_DATA12, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA12_LCDIF_DATA12, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA13_LCDIF_DATA13, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA13_LCDIF_DATA13, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA14_LCDIF_DATA14, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA14_LCDIF_DATA14, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA15_LCDIF_DATA15, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA15_LCDIF_DATA15, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA16_LCDIF_DATA16, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA16_LCDIF_DATA16, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA17_LCDIF_DATA17, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA17_LCDIF_DATA17, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA18_LCDIF_DATA18, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA18_LCDIF_DATA18, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA19_LCDIF_DATA19, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA19_LCDIF_DATA19, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA20_LCDIF_DATA20, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA20_LCDIF_DATA20, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA21_LCDIF_DATA21, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA21_LCDIF_DATA21, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA22_LCDIF_DATA22, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA22_LCDIF_DATA22, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_DATA23_LCDIF_DATA23, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_DATA23_LCDIF_DATA23, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_ENABLE_LCDIF_ENABLE, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_HSYNC_LCDIF_HSYNC, LCD_PAD_CONFIG_DATA);
IOMUXC_SetPinMux(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, 0U);
IOMUXC_SetPinConfig(IOMUXC_LCD_VSYNC_LCDIF_VSYNC, LCD_PAD_CONFIG_DATA);
引脚设置完成后,就可以按照野火、正点原子、官方例程来测试了。
/*
* Initialize the Video PLL.
* Video PLL output clock is OSC24M * (loopDivider + (denominator / numerator)) / postDivider = 93MHz.
*/
clock_video_pll_config_t config = {
.loopDivider = 31,
.postDivider = 4,
.numerator = 0,
.denominator = 0,
};
CLOCK_InitVideoPll(&config);
/*
* 000 derive clock from PLL2
* 001 derive clock from PLL3 PFD3
* 010 derive clock from PLL5
* 011 derive clock from PLL2 PFD0
* 100 derive clock from PLL2 PFD1
* 101 derive clock from PLL3 PFD1
*/
CLOCK_SetMux(kCLOCK_Lcdif1PreMux, 2);
CLOCK_SetDiv(kCLOCK_Lcdif1PreDiv, 1);
CLOCK_SetDiv(kCLOCK_Lcdif1Div, 1);
/*
* 000 derive clock from divided pre-muxed lcdif1 clock
* 001 derive clock from ipp_di0_clk
* 010 derive clock from ipp_di1_clk
* 011 derive clock from ldb_di0_clk
* 100 derive clock from ldb_di1_clk
*/
CLOCK_SetMux(kCLOCK_Lcdif1Mux, 0);
参照野火的5寸屏幕参数,
/* Macros for panel. */
#define LCD_HSW 41
#define LCD_HFP 4
#define LCD_HBP 8
#define LCD_VSW 10
#define LCD_VFP 4
#define LCD_VBP 2
#define LCD_WIDTH 800
#define LCD_HEIGHT 480
/* Initialize the display. */
const elcdif_rgb_mode_config_t config = {
.panelWidth = LCD_WIDTH,
.panelHeight = LCD_HEIGHT,
.hsw = LCD_HSW,
.hfp = LCD_HFP,
.hbp = LCD_HBP,
.vsw = LCD_VSW,
.vfp = LCD_VFP,
.vbp = LCD_VBP,
.polarityFlags = LCD_POL_FLAGS,
/* lvgl starts render in frame buffer 0, so show frame buffer 1 first. */
.bufferAddr = (uint32_t)s_frameBuffer[1],
.pixelFormat = kELCDIF_PixelFormatRGB888,
.dataBus = LCD_LCDIF_DATA_BUS,
};
ELCDIF_RgbModeInit(LCDIF, &config);
SystemInstallIrqHandler(LCDIF_IRQn, (system_irq_handler_t)(uint32_t)LCDIF_IRQHandler, NULL);
ELCDIF_EnableInterrupts(LCDIF, kELCDIF_CurFrameDoneInterruptEnable);
EnableIRQ(LCDIF_IRQn);
ELCDIF_RgbModeStart(LCDIF);
这样,LCD屏幕就初始化成功了。
可以通过官方的例程进行测试了。
void APP_FillFrameBuffer(uint32_t frameBuffer[APP_IMG_HEIGHT][APP_IMG_WIDTH])
{
/* Background color. */
static const uint32_t bgColor = 0U;
/* Foreground color. */
static uint8_t fgColorIndex = 0U;
static const uint32_t fgColorTable[] = {0x000000FFU, 0x0000FF00U, 0x0000FFFFU, 0x00FF0000U,
0x00FF00FFU, 0x00FFFF00U, 0x00FFFFFFU};
uint32_t fgColor = fgColorTable[fgColorIndex];
/* Position of the foreground rectangle. */
static uint16_t upperLeftX = 0U;
static uint16_t upperLeftY = 0U;
static uint16_t lowerRightX = (APP_IMG_WIDTH - 1U) / 2U;
static uint16_t lowerRightY = (APP_IMG_HEIGHT - 1U) / 2U;
static int8_t incX = 1;
static int8_t incY = 1;
/* Change color in next forame or not. */
static bool changeColor = false;
uint32_t i, j;
/* Background color. */
for (i = 0; i < APP_IMG_HEIGHT; i++)
{
for (j = 0; j < APP_IMG_WIDTH; j++)
{
frameBuffer[i][j] = bgColor;
}
}
/* Foreground color. */
for (i = upperLeftY; i < lowerRightY; i++)
{
for (j = upperLeftX; j < lowerRightX; j++)
{
frameBuffer[i][j] = fgColor;
}
}
/* Update the format: color and rectangle position. */
upperLeftX += incX;
upperLeftY += incY;
lowerRightX += incX;
lowerRightY += incY;
changeColor = false;
if (0U == upperLeftX)
{
incX = 1;
changeColor = true;
}
else if (APP_IMG_WIDTH - 1 == lowerRightX)
{
incX = -1;
changeColor = true;
}
if (0U == upperLeftY)
{
incY = 1;
changeColor = true;
}
else if (APP_IMG_HEIGHT - 1 == lowerRightY)
{
incY = -1;
changeColor = true;
}
if (changeColor)
{
fgColorIndex++;
if (ARRAY_SIZE(fgColorTable) == fgColorIndex)
{
fgColorIndex = 0U;
}
}
}
在主程序中调用,交替使用双缓冲进行显示输出。
while (1)
{
frameBufferIndex ^= 1U;
APP_FillFrameBuffer(s_frameBuffer[frameBufferIndex]);
ELCDIF_SetNextBufferAddr(APP_ELCDIF, (uint32_t)s_frameBuffer[frameBufferIndex]);
s_frameDone = false;
/* Wait for previous frame complete. */
while (!s_frameDone)
{
}
}
当看到一个蓝色的方块由左上角向右下角慢慢移动时,就说明屏幕驱动正常了。