- 本文作为K210开发板的裸机开发基础,环境采用cmake+vs code2019,权威请参考嘉楠官方的开发手册。文章中问题在所难免,欢迎讨论~
文章目录
- 基础例程
- 点亮LED灯
- 1. SDK中对应的API
- 2. 步骤
- 双核并行
- 1. SDK中对应的API
- 外部中断
- 1. SDK中对应的API
- 定时器实验
- 1.SDK中对应的API功能
- 2. 步骤
- PWM实验
- 1.SDK中对应的API功能
- 2. 步骤
- 摄像头显示
- 0.元件介绍
- 1.SDK中对应的API功能
- 2. 步骤
基础例程
点亮LED灯
1. SDK中对应的API
K210的硬件引脚和软件功能使用的是FPIOA映射关系
2. 步骤
- 在硬件初始化函数中,设定fpioa的关系
fpioa_set_function(硬件外设的连接引脚(那48个), FUNC_LED0:FUNC_GPIO0+软件gpio口);
fpioa_function_t在SDK里 FUNC_GPIO0之类的管脚的功能定义
LED 灯是低电平点亮的
//绑定GPIO 引脚
void hardware_init(void)
{fpioa_set_function(PIN_LED_0, FUNC_LED0);fpioa_set_function(PIN_LED_1, FUNC_LED1);
}//头文件
/*****************************HEAR-FILE************************************/
#include "fpioa.h"/*****************************HARDWARE-PIN*********************************/
// 硬件IO口,与原理图对应
#define PIN_LED_0 (0)
#define PIN_LED_1 (17)/*****************************SOFTWARE-GPIO********************************/
// 软件GPIO口,与程序对应
#define LED0_GPIONUM (0)
#define LED1_GPIONUM (1)/*****************************FUNC-GPIO************************************/
// GPIO口的功能,绑定到硬件IO口
#define FUNC_LED0 (FUNC_GPIO0 + LED0_GPIONUM)
#define FUNC_LED1 (FUNC_GPIO0 + LED1_GPIONUM)
8个通用GPIO 使用一个中断源可设置边沿触发和电平触发每个IO口可以分配到FPIOA上的48个管脚之一FPIOA (现场可编程IO阵列)允许用户将255 个内部功能映射到芯片外围的48个自由IO上
- 主函数
- 初始化硬件引脚
- 使能GPIO时钟 gpio_init
- 设置硬件引脚的GPIO输入输出模式
用户接口:• gpio_init:GPIO口初始化
• gpio_set_drive_mode:设置GPIO口输入或输出模式
• gpio_set_pin:设置GPIO引脚电平高/低
• gpio_get_pin:读取GPIO引脚电平- 定义GPIO的值
typedef enum_gpio_pin_value
{GPIO_PV_HIGH,GPIO_PV_LOW
}gpio_pin_value_pin ;
- 烧录
cmake … -DPROJ=gpio_led -G “MinGW Makefiles”
这里会出现问题的原因:等于前后不能有空格,文件名不要有前后空格
make
双核并行
双核对等,每个核心具备独立的FPU(浮点运算单元)
系统默认使用核心 0 ,如果需要使用核心 1 需要手动开启核心 1 的服务。
1. SDK中对应的API
板级对应的头文件 bsp.h
bsp.h头文件是与平台相关的通用函数,核之间锁的相关操作。
提供获取当前运行程序的CPU核编号的接口以及启动第二个核的入口。
为用户提供以下接口:
• register_core1:向核心1注册函数,并启动核心1
• current_coreid:获取当前CPU的核心编号(0/1)
• read_cycle:获取CPU开机至今的时钟数。可以用使用这个函数精准的确定程序运行时钟。可以配合sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)计算运行的时间。
• spinlock_lock:自旋锁,不可嵌套,不建议在中断使用,中断中可以使用spinlock_trylock。
• spinlock_unlock:自旋锁解锁。
• spinlock_trylock:获取自旋锁,成功获取锁会返回0,失败返回-1。
• corelock_lock:获取核间锁,核之间互斥的锁,同核内该锁会嵌套,只有异核之间会阻塞。不建议在中断使用该函数,中断中可以使用corelock_trylock。
• corelock_trylock:获取核间锁,同核时锁会嵌套,异核时非阻塞。成功获取锁会返回0,失败返回-1。
• corelock_unlock:核间锁解锁。
• sys_register_putchar:注册系统输出回调函数,printf时会调用该函数。系统默认使用UART3,如果需要修改UART则调用uart_debug_init函数。
• sys_register_getchar:注册系统输入回调函数,scanf时会调用该函数。系统默认使用UART3,如果需要修改UART则调用uart_debug_init函数。
• sys_stdin_flush:清理stdin缓存。
• get_free_heap_size:获取空闲内存大小。
• printk:打印核心调试信息,用户不必理会。
系统的 printf 默认使用高速串口 UARTHS(UART0)
外部中断
BOOT键按下为低电平
1. SDK中对应的API
对应的头文件 plic.h
PLIC可以将任一外部中断源单独分配到每个CPU的外部中断上。这提供了强大的灵活性,能适应不同的应用需求。
PLIC模块可以设置中断回调函数,当触发中断时,会自动运行中断回调函数,并且可以配置中断优先级。
为用户提供以下接口:
• plic_init:PLIC初始化外部中断。
• plic_irq_enable:使能外部中断。
• plic_irq_disable:禁用外部中断。
• plic_set_priority:设置中断优先级。
• plic_get_priority:获取中断优先级。
• plic_irq_register:注册外部中断函数。
• plic_irq_deregister:注销外部中断函数。
定时器实验
芯片定时器总共有3个,每个定时器有4路通道.
每个定时器可以设置触发间隔和定时器中断处理函数。
定时器可以设置纳秒级别的超时时间,并且可以设置中断回调。
定时器可以通过控制 使能与禁止的方式来暂停和重新启动,而不需要重新配置。
1.SDK中对应的API功能
对应的头文件 timer.h 为用户提供以下接口:
• timer_init:初始化定时器。
• timer_set_interval:设置定时间隔。
//timer_set_irq (0.6.0 后不再支持,请使用timer_irq_register)
• timer_set_enable:使能/禁止定时器。 0禁用 1使能
• timer_irq_register:注册定时器中断回调函数。
int timer_irq_register(timer_device_number_t device, //定时器号timer_channel_number_t channel, //定时器通道号int is_single_shot, //是否单次通道uint32_t priority, //中断优先级timer_callback_t callback, //中断回调函数void *ctx//中断回调函数参数);
//返回0成功 1失败• timer_irq_deregister:注销定时器中断。
2. 步骤
- 硬件初始化,定义FPIOA 映射
- 初始化外部中断服务,使能全局中断
/* 初始化系统中断并使能 */plic_init();sysctl_enable_irq();
- 在使用RGB灯前需要初始化,把GPIO设置为输出模式。
- 按键初始化,设置BOOT为上拉输入模式,设置按键的GPIO电平触发模式为上升/下降沿,设置中断回调函数
- BOOT触发的外部中断函数key_irq_cb
- 初始化定时器
void init_timer(void) {/* 定时器初始化 */timer_init(TIMER_DEVICE_0);/* 设置定时器超时时间 */==单位为ns==timer_set_interval(TIMER_DEVICE_0, TIMER_CHANNEL_0, 500 * 1e6);//0.5s/* 设置定时器中断回调 */timer_irq_register(TIMER_DEVICE_0, TIMER_CHANNEL_0, 0, 1, timer_timeout_cb, &g_count);/* 使能定时器 */timer_set_enable(TIMER_DEVICE_0, TIMER_CHANNEL_0, 1);
}
- 定时器内部中断处理timer_timeout_cb
PWM实验
1 PWM的内部实现是基于定时器的定时功能 。
2 控制PWM的两个重要因素是频率和占空比。
1.SDK中对应的API功能
对应的头文件 pwm.h 脉冲宽度调制器PWM用于控制脉冲输出的占空比,其本质是一个定时器,
所以注意设置PWM号与通道时,不要与TIMER定时器冲突。
• pwm_init
• pwm_set_frequency
• pwm_set_enable 1开启 0禁用
2. 步骤
- 硬件初始化,定义FPIOA 映射
- 初始化外部中断服务,使能全局中断
- 初始化定时器
- 初始化PWM 定时器1通道0 频率200k 占空比0.5
void init_pwm(void)
{/* 初始化PWM */pwm_init(PWM_DEVICE_1);/* 设置PWM频率为200KHZ,占空比为0.5的方波 */pwm_set_frequency(PWM_DEVICE_1, PWM_CHANNEL_0, 200000, 0.5);/* 使能 PWM 输出 */pwm_set_enable(PWM_DEVICE_1, PWM_CHANNEL_0, 1);
}
- 在定时器内部中断处理timer_timeout_cb函数中,设置PWM的占空比
摄像头显示
ov2640摄像头、LCD显示屏
- K210开发板板载DVP接口可以与兼容DVP接口的ov2640摄像头连接使用。
- K210开发板显示摄像头画面是通过一帧一帧刷新LCD界面来达到动态效果的。
0.元件介绍
OV2640 摄像头与K210开发板通过数字摄像头接口(DVP)连接DVP是常用的摄像头接口模块
具有以下特性:
- 支持 支持 SCCB 协议配置摄像头寄存器
- 支持 640×480 3 0 万像素) 及以下分辨率,每帧大小可配置
- 支持 YUV422 和 RGB565 格式的图像输入
- 支持图像同时输出到 KPU 和显示屏:
- 输出到 KPU 的格式可选 RGB888 ,或 YUV422 输入时的 Y 分量
- 输出到显示屏的格式为 RGB565
- 检测到一帧开始或一帧图像传输完成时可向 CPU 发送中断
1.SDK中对应的API功能
对应的头文件 dvp.h
• dvp_init :DVP初始化。
• dvp_set_output_enable:设置输出模式(内存或AI)使能或禁止。
• dvp_set_image_format:设置图像接收模式,RGB或YUV。
• dvp_set_image_size:设置DVP图像采集尺寸。
• dvp_set_ai_addr:设置AI存放图像的地址,供AI模块进行算法处理。
• dvp_set_display_addr:设置采集图像在内存中的存放地址,可以用来显示。
• dvp_config_interrupt:设置图像开始和结束中断状态,使能或禁用。
• dvp_get_interrupt:判断是否是输入的中断类型,返回值:0为否,非0为是。
• dvp_clear_interrupt:清除中断。
• dvp_start_convert:开始采集图像,在确定图像采集开始中断后调用。
• dvp_enable_burst:使能突发传输模式。
• dvp_disable_burst:禁用突发传输模式。
• dvp_enable_auto:使能自动接收图像模式。
• dvp_disable_auto:禁用自动接收图像模式。
• dvp_sccb_send_data:通过sccb协议发送数据。
• dvp_sccb_receive_data:通过SCCB接收数据。
• dvp_sccb_set_clk_rate:设置sccb的速率。
• dvp_set_xclk_rate:设置输入时钟的速率。
2. 步骤
- 硬件初始化,定义FPIOA 映射
加一个使能SPI0和DVP
- 初始化电源域电压,摄像头和显示器都需要1.8V电平信号,根据所电源域设置 bank6 bank7的电压为1.8V
void io_set_power
- 设置系统时钟 DVP时钟
- 使能全局中断
- 初始化LCD
- 初始化DVP
void dvp_cam_init(void)
{/* DVP初始化,设置sccb的寄存器长度为8bit */dvp_init(8);/* 设置输入时钟为24000000*/dvp_set_xclk_rate(24000000);/* 使能突发传输模式 */dvp_enable_burst();/* 关闭AI输出模式,使能显示模式 */dvp_set_output_enable(DVP_OUTPUT_AI, 0);dvp_set_output_enable(DVP_OUTPUT_DISPLAY, 1);/* 设置输出格式为RGB */dvp_set_image_format(DVP_CFG_RGB_FORMAT);/* 设置输出像素大小为320*240 */dvp_set_image_size(CAM_WIDTH_PIXEL, CAM_HIGHT_PIXEL);/* 设置DVP的显示地址参数和中断 */display_buf = (uint32_t*)iomem_malloc(CAM_WIDTH_PIXEL * CAM_HIGHT_PIXEL * 2);display_buf_addr = display_buf;dvp_set_display_addr((uint32_t)display_buf_addr);dvp_config_interrupt(DVP_CFG_START_INT_ENABLE | DVP_CFG_FINISH_INT_ENABLE, 0);dvp_disable_auto();
}
- 设置DVP中断服务
配置中断,清楚中断标志位
- DVP中断回调,把摄像头内容保存到display_buf_addr这个地址变量中
- 初始化ov2640 直接发送对应寄存器及数据即可
for (index = 0; ov2640_config[index][0]; index++)dvp_sccb_send_data(OV2640_ADDR, ov2640_config[index][0], ov2640_config[index][1]);
- 等待DVP传输完成后,LCD显示地址变量的数据
while (1){/* 等待摄像头采集结束,然后清除结束标志 */while (g_dvp_finish_flag == 0);g_dvp_finish_flag = 0;/* 显示画面 */lcd_draw_picture(0, 0, 320, 240, display_buf_addr);}
cmake … -DPROJ=camera -G "MinGW Makefiles