chore: migrate project into clean repository

This commit is contained in:
yuuux
2026-08-13 16:50:52 +08:00
commit d1d25a09e7
27405 changed files with 9422808 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
add_subdirectory(lisa_device)
add_subdirectory(lisa_adc)
add_subdirectory(lisa_audio)
add_subdirectory(lisa_gpio)
add_subdirectory(lisa_i2c)
add_subdirectory(lisa_pwm)
add_subdirectory(lisa_spi)
add_subdirectory(lisa_uart)
add_subdirectory(lisa_display)
add_subdirectory(lisa_touch)
add_subdirectory(lisa_wdt)
add_subdirectory(lisa_camera)
add_subdirectory(lisa_rtc)
add_subdirectory(lisa_flash)
add_subdirectory(lisa_hwtimer)
add_subdirectory(lisa_sdmmc)
add_subdirectory(lisa_qspilcd)
add_subdirectory(lisa_dvp)
add_subdirectory(lisa_rgb)

View File

@@ -0,0 +1,682 @@
# ARCS 平台驱动设备文档编写规范
本规范基于 lisa_adc、lisa_gpio、lisa_uart 三个驱动文档的分析总结而成,旨在为 ARCS 平台的驱动开发提供统一的文档编写标准。
## 一、文档结构规范
### 1.1 标题与简介(必需)
**格式**:
```markdown
# <驱动名称> 驱动
基于 lisa_device 框架的 <设备类型> 设备驱动,为 ARCS 平台提供统一的<功能描述>接口。
```
**要求**:
- 标题使用一级标题,格式为 "<驱动名称> 驱动"
- 简介一句话说明驱动的定位和用途
- 必须提及基于 lisa_device 框架
- 必须说明为 ARCS 平台提供的核心功能
**示例**:
```markdown
# ADC 驱动
基于 lisa_device 框架的 ADC 设备驱动,为 ARCS 平台提供统一的模拟信号采样接口。
```
### 1.2 功能特性(必需)
**格式**:
```markdown
## 功能特性
- **特性名称**: 特性说明
- **特性名称**: 特性说明
...
```
**要求**:
- 使用二级标题 "功能特性"
- 使用无序列表,每项使用 `**粗体**` 突出特性名称
- 按重要性排序:设备支持 → 核心功能 → 高级功能
- 5-8 个要点,简洁明了
- 必须包含的特性:设备支持、核心功能、线程安全(如果支持)
**示例**:
```markdown
## 功能特性
- **设备支持**: UART0、UART1、UART2 三个串口设备
- **传输模式**: 支持中断模式和 DMA 模式
- **通信配置**: 灵活配置波特率、数据位、停止位、校验位、流控
- **数据传输**: 同步/异步读写、轮询收发
- **线程安全**: 支持全双工通信,可在多线程环境中使用
```
### 1.3 配置选项(必需)
**格式**:
```markdown
## 配置选项
`prj.conf` 中启用驱动:
```kconfig
CONFIG_LISA_<驱动名称>=y
CONFIG_LISA_<设备1>=y # 启用说明(可选)
CONFIG_LISA_<设备2>=y # 启用说明(可选)
```
根据需要选择启用的设备或功能。
```
**要求**:
- 使用二级标题 "配置选项"
- 明确说明配置文件位置prj.conf 或 Kconfig
- 使用 kconfig 代码块
- 提供必需和可选配置项
- 对每个配置项添加注释说明(如果有多个选项)
### 1.4 API 接口(必需)
**格式**:
```markdown
## API 接口
### <功能分类1>
```c
函数原型1;
函数原型2;
```
功能说明(可选)。
### <功能分类2>
```c
函数原型1;
函数原型2;
```
功能说明(可选)。
```
**要求**:
- 使用二级标题 "API 接口"
- 按功能分类组织,使用三级标题
- 常见分类:配置接口、数据传输接口、控制接口、中断管理等
- 仅提供函数原型,详细说明放在后续章节
- 如有重要提示,使用 **粗体**`代码` 强调
**示例**:
```markdown
## API 接口
### 配置接口
```c
int lisa_uart_configure(lisa_device_t *dev, const lisa_uart_config_t *config);
int lisa_uart_get_config(lisa_device_t *dev, lisa_uart_config_t *config);
```
**重要**: 必须先调用 `lisa_uart_configure()` 配置设备后,才能使用下面的功能 API。
### 数据传输接口
```c
int lisa_uart_write_sync(lisa_device_t *dev, const uint8_t *buf, uint32_t len, uint32_t timeout_ms);
int lisa_uart_read_sync(lisa_device_t *dev, uint8_t *buf, uint32_t len);
```
```
### 1.5 使用示例/使用方法(必需)
**格式**:
**方式一:基本步骤 + 示例**(适用于步骤明确的驱动,如 ADC
```markdown
## 使用方法
### 基本步骤
1. **步骤名称**: 步骤说明
2. **步骤名称**: 步骤说明
3. **步骤名称**: 步骤说明
### 基础使用示例
```c
// 完整代码示例
```
### 进阶示例1
```c
// 完整代码示例
```
```
**方式二:直接示例**(适用于使用模式多样的驱动,如 GPIO、UART
```markdown
## 使用示例
### 场景1名称
```c
// 完整代码示例
```
### 场景2名称
```c
// 完整代码示例
```
```
**要求**:
- 使用二级标题 "使用示例" 或 "使用方法"
- 每个示例使用三级标题,标题应清晰描述使用场景
- 代码必须完整可运行,包含必要的头文件、错误处理
- 代码注释清晰,使用 `//` 单行注释说明步骤
- 从简单到复杂排序
- 至少提供 2-3 个典型使用场景
- 示例应覆盖主要 API 的使用
**代码风格**:
- 使用行内注释 `// 说明` 而非块注释
- 注释使用中文
- 关键步骤前添加序号注释:`// 1. 获取设备`
- 错误处理要简洁但完整
### 1.6 硬件配置(必需)
**格式**:
```markdown
## 硬件配置
### 引脚复用配置
<驱动名称> 驱动在初始化时会自动调用板型目录中定义的 `lisa_<驱动>_pinmux()` 函数,用于配置引脚复用。
**配置位置**:
- **定义**: `boards/<板型名>/pinmux.c` 中实现函数
- **声明**: `boards/<板型名>/pinmux.h` 中声明函数
- **调用时机**: 设备初始化时自动调用
**示例** (参考 `boards/arcs_evb/pinmux.c`):
```c
// 代码示例
```
**注意**:
- 该函数由板型相关代码实现,不同板型的引脚配置可能不同
- 只需配置实际使用的引脚
- 其他注意事项...
### 其他硬件相关配置(可选)
如:支持的通道、参考电压选择、中断触发模式等
```
**要求**:
- 使用二级标题 "硬件配置"
- 必须包含 "引脚复用配置" 三级标题
- 说明 pinmux 函数的定义位置、声明位置、调用时机
- 提供完整的代码示例(参考 arcs_evb 板型)
- 列出注意事项
- 如有其他硬件相关配置(通道、模式等),使用独立的三级标题
### 1.7 详细参数说明(可选,按需添加)
**适用场景**:
- 配置参数复杂(如 ADC 参考电压、UART 配置宏)
- 事件类型多样(如 UART 事件类型)
- 标志位组合(如 GPIO 配置标志)
**格式**:
```markdown
## <参数类型>说明
### <参数分类1>
说明文字或表格
### <参数分类2>
说明文字或表格
```
**要求**:
- 使用二级标题,标题应明确说明参数类型
- 复杂参数优先使用表格展示
- 表格包含:参数名/枚举、说明、示例(如适用)
- 如有组合使用,提供示例代码
**示例**:
```markdown
## 配置标志位
驱动使用标志位方式进行配置,可以通过按位或(`|`)组合多个标志:
### 方向标志
- `LISA_GPIO_INPUT` - 输入模式(默认)
- `LISA_GPIO_OUTPUT` - 输出模式
### 示例组合
```c
// 输入模式 + 上拉
LISA_GPIO_INPUT | LISA_GPIO_PULL_UP
```
```
### 1.8 注意事项(必需)
**格式**:
```markdown
## 注意事项
1. **注意点标题**: 详细说明
2. **注意点标题**: 详细说明
...
```
**要求**:
- 使用二级标题 "注意事项"
- 使用有序列表,每项使用 `**粗体**` 突出要点
- 包含但不限于:
- 使用限制(范围、顺序、前置条件)
- 常见错误和解决方法
- 性能影响
- 线程安全说明
- 硬件限制
- 按重要性和逻辑关系排序
- 10 条左右为宜,不超过 15 条
**示例**:
```markdown
## 注意事项
1. **必须先配置**: 设备初始化后必须先调用 `lisa_uart_configure()` 配置设备,才能使用功能 API
2. **引脚范围**: 每个 GPIO 控制器支持 0-31 共 32 个引脚
3. **线程安全**: 驱动内部使用互斥锁保护,可在多线程环境中使用
```
### 1.9 文件说明(可选)
**格式**:
```markdown
## 文件说明
- `文件名` - 文件用途说明
- `文件名` - 文件用途说明
```
**要求**:
- 使用二级标题 "文件说明"
- 使用无序列表
- 列出驱动目录下的主要文件
- 简要说明每个文件的用途
## 二、内容编写规范
### 2.1 术语使用
**设备名称**:
- 使用大写ADC0、GPIOA、UART0
- 示例:"支持 UART0、UART1、UART2 三个串口设备"
**API 函数**:
- 使用反引号包裹:`lisa_adc_read()`
- 示例:"调用 `lisa_uart_configure()` 配置设备"
**配置项**:
- 使用反引号包裹:`CONFIG_LISA_ADC=y`
- 枚举使用反引号:`LISA_GPIO_INPUT`
**文件路径**:
- 使用反引号包裹:`boards/<板型名>/pinmux.c`
- 使用尖括号表示变量部分:`<板型名>`
### 2.2 代码示例规范
**完整性**:
```c
#include "lisa_xxx.h" // 必须包含头文件
// 获取设备
lisa_device_t *dev = lisa_device_get_by_name("xxx");
if (!dev) { // 必须有错误处理
return -1;
}
// 功能代码
...
// 返回值检查
int ret = lisa_xxx_function(dev, ...);
if (ret == LISA_DEVICE_OK) {
// 成功处理
}
```
**注释风格**:
- 使用 `//` 单行注释
- 关键步骤添加序号:`// 1. 获取设备`
- 代码后添加说明注释:`return -1; // 设备未找到`
- 使用中文注释
**错误处理**:
- 设备获取必须检查空指针
- API 调用必须检查返回值
- 简化示例可省略详细错误处理,但需保留结构
### 2.3 表格使用规范
**适用场景**:
- 参数列表(通道、引脚映射)
- 枚举说明(事件类型、配置标志)
- 返回值说明
- 配置选项对比
**格式要求**:
```markdown
| 列标题1 | 列标题2 | 列标题3 |
|--------|--------|--------|
| 内容1 | 内容2 | 内容3 |
```
**内容要求**:
- 表头简洁明确
- 单元格内容简短,避免长句
- 代码使用反引号包裹
- 对齐美观
### 2.4 强调和提示
**粗体强调**:
- 用于标题关键词:"**设备支持**: UART0、UART1"
- 用于注意事项:"**重要**: 必须先配置设备"
**代码强调**:
- 用于函数名:`lisa_uart_configure()`
- 用于配置项:`CONFIG_LISA_ADC=y`
- 用于枚举:`LISA_GPIO_INPUT`
**重要提示**:
```markdown
**重要**: 说明文字
**注意**: 说明文字
```
### 2.5 语言风格
**简洁明确**:
- 避免冗长描述
- 一句话说清楚一个概念
- 使用主动语态:"调用函数配置设备" 而非 "设备通过调用函数被配置"
**专业准确**:
- 使用准确的技术术语
- 避免模糊表述:"支持 0-7 通道" 而非 "支持多个通道"
- 数值明确:波特率范围、通道数量
**用户视角**:
- 从使用者角度组织内容
- 先说明"是什么",再说明"怎么用"
- 提供足够的上下文信息
## 三、章节顺序规范
### 3.1 必需章节(按顺序)
1. 标题与简介
2. 功能特性
3. 配置选项
4. API 接口
5. 使用示例/使用方法
6. 硬件配置
7. 注意事项
### 3.2 可选章节(插入位置)
- **详细 API 说明**:插入在"API 接口"和"使用示例"之间(如果 API 需要详细说明)
- **配置宏/标志位说明**:插入在"API 接口"和"使用示例"之间
- **事件类型说明**:插入在"使用示例"之后,"硬件配置"之前
- **返回值说明**:插入在"硬件配置"之后,"注意事项"之前
- **数据转换/计算**:根据逻辑插入合适位置(如 ADC 的电压转换放在"使用示例"之后)
- **文件说明**:放在文档末尾
### 3.3 章节组织原则
1. 从概念到实践:功能特性 → API → 示例 → 配置
2. 从常用到进阶:基础 API → 进阶功能 → 硬件细节
3. 相关内容靠近:配置参数紧跟配置 API
4. 重要内容前置:常用功能优先介绍
## 四、文档质量检查清单
### 4.1 结构完整性
- [ ] 包含所有必需章节
- [ ] 章节顺序符合规范
- [ ] 标题层级正确(# ## ###
- [ ] 代码块语言标注正确
### 4.2 内容准确性
- [ ] API 函数原型正确
- [ ] 配置选项有效
- [ ] 代码示例可运行
- [ ] 参数范围准确
- [ ] 返回值说明完整
### 4.3 可读性
- [ ] 术语使用一致
- [ ] 代码注释清晰
- [ ] 表格格式规范
- [ ] 强调使用恰当
- [ ] 无错别字
### 4.4 实用性
- [ ] 提供足够的使用示例
- [ ] 覆盖常见使用场景
- [ ] 包含错误处理示例
- [ ] 注意事项全面
- [ ] 硬件配置说明清晰
## 五、特殊场景处理
### 5.1 复杂 API 的详细说明
当 API 参数复杂或使用有特殊要求时,在"API 接口"章节后添加详细说明:
```markdown
## API 接口
### 配置 ADC 通道
```c
int lisa_adc_channel_setup(lisa_device_t *dev, uint32_t channel,
const lisa_adc_channel_config_t *config);
```
配置指定通道的参考电压和分辨率。应在读取通道之前调用。
**参数**:
- `dev`: ADC 设备指针
- `channel`: ADC 通道号0-5普通通道6VBAT7TEMP
- `config`: 通道配置参数,包含:
- `reference`: 参考电压类型(枚举)
- `resolution`: ADC 分辨率(枚举)
**返回值**:
- `LISA_DEVICE_OK (0)`: 成功
- `LISA_DEVICE_ERR_INVALID`: 参数无效
```
### 5.2 多设备驱动
当驱动支持多个设备实例时(如 GPIOA/GPIOB、UART0/1/2
1. 在"功能特性"中明确列出支持的设备
2. 在"配置选项"中分别列出每个设备的配置项
3. 在"硬件配置"中为每个设备提供独立的 pinmux 示例
4. 在"使用示例"中展示不同设备的使用
### 5.3 特殊功能说明
对于特殊的计算、转换或配置逻辑,使用独立章节说明:
```markdown
## 电压转换
使用 `LISA_ADC_RAW_TO_MV` 宏进行转换:
```c
uint32_t voltage_mv = LISA_ADC_RAW_TO_MV(raw_value, reference_mv, resolution_bits);
```
**参数**:
- `raw_value`: ADC 原始采样值
- `reference_mv`: 参考电压(毫伏)
- `resolution_bits`: 分辨率(位数)
```
## 六、版本和维护
### 6.1 文档更新
- 驱动 API 变更时,同步更新文档
- 新增功能时,添加对应示例
- 发现问题时,补充注意事项
### 6.2 示例代码维护
- 确保示例代码与最新 API 保持一致
- 定期验证示例代码的可运行性
- 根据用户反馈改进示例
### 6.3 术语统一
- 维护统一的术语表
- 新驱动文档参考已有文档的术语使用
- 定期审查术语一致性
---
## 附录:快速模板
### A. 基础驱动文档模板
```markdown
# LISA <驱动名称> 驱动
基于 lisa_device 框架的 <设备类型> 设备驱动,为 ARCS 平台提供统一的<功能描述>接口。
## 功能特性
- **设备支持**:
- **核心功能**:
- **配置选项**:
- **线程安全**:
## 配置选项
`prj.conf` 中启用驱动:
```kconfig
CONFIG_LISA_<驱动名称>=y
```
## API 接口
### 配置接口
```c
int lisa_xxx_configure();
```
### 控制接口
```c
int lisa_xxx_xxx();
```
## 使用示例
### 基础使用
```c
#include "lisa_xxx.h"
// 1. 获取设备
lisa_device_t *dev = lisa_device_get_by_name("xxx");
if (!dev) {
return -1;
}
// 2. 配置设备
// ...
// 3. 使用功能
// ...
```
## 硬件配置
### 引脚复用配置
<驱动名称> 驱动在初始化时会自动调用板型目录中定义的 `lisa_xxx_pinmux()` 函数。
**配置位置**:
- **定义**: `boards/<板型名>/pinmux.c`
- **声明**: `boards/<板型名>/pinmux.h`
- **调用时机**: 设备初始化时自动调用
**示例**:
```c
void lisa_xxx_pinmux()
{
// 配置引脚
}
```
## 注意事项
1. **使用限制**:
2. **配置要求**:
3. **线程安全**:
```
### B. 参数说明模板
```markdown
## <参数类型>说明
### <参数分类>
| 参数名 | 说明 | 备注 |
|-------|------|------|
| xxx | xxx | xxx |
**示例**:
```c
// 代码示例
```
```
---
**本规范基于以下文档分析**:
- drivers/lisa_adc/README.md
- drivers/lisa_gpio/README.md
- drivers/lisa_uart/README.md
**规范版本**: v1.0
**最后更新**: 2025-11-27

53
arcs-sdk/drivers/Kconfig Normal file
View File

@@ -0,0 +1,53 @@
# LISA Device Framework Configuration
#
# Copyright (c) 2025, LISTENAI
# SPDX-License-Identifier: Apache-2.0
menuconfig LISA_DEVICE
bool "Enable LISA Device Framework"
default n
select LISA_OS
select LOG
help
Enable the LISA device framework for unified device management.
This provides a base class for all devices with automatic registration,
state management, and observability features.
if LISA_DEVICE
config LISA_DEVICE_DEBUG
bool "Enable device framework debug features"
default n
help
Enable debug features like device registry printing, verification, etc.
This will increase code size and should be disabled in production builds.
config LISA_DEVICE_MANUAL_INIT
bool "Manual device initialization mode"
default n
help
Enable manual initialization mode for testing purposes.
When enabled, lisa_device_init() will not be called automatically.
This allows test code to control the exact timing of device initialization,
useful for testing initialization logic, error handling, and edge cases.
rsource "lisa_adc/Kconfig"
rsource "lisa_audio/Kconfig"
rsource "lisa_gpio/Kconfig"
rsource "lisa_i2c/Kconfig"
rsource "lisa_pwm/Kconfig"
rsource "lisa_spi/Kconfig"
rsource "lisa_uart/Kconfig"
rsource "lisa_display/Kconfig"
rsource "lisa_touch/Kconfig"
rsource "lisa_wdt/Kconfig"
rsource "lisa_camera/Kconfig"
rsource "lisa_rtc/Kconfig"
rsource "lisa_flash/Kconfig"
rsource "lisa_hwtimer/Kconfig"
rsource "lisa_sdmmc/Kconfig"
rsource "lisa_qspilcd/Kconfig"
rsource "lisa_dvp/Kconfig"
rsource "lisa_rgb/Kconfig"
endif # LISA_DEVICE

View File

@@ -0,0 +1,31 @@
.. _drivers:
设备驱动
========
ARCS SDK 设备驱动基于 LISA 轻量级设备框架,提供统一的设备抽象层,实现自动注册、状态管理和访问控制。
驱动层自动处理引脚复用配置,应用层通过统一的设备 API 访问 GPIO、UART、ADC、定时器等外设无需关心底层硬件细节。
.. toctree::
:maxdepth: 1
lisa_pinmux/README.md
lisa_gpio/README.md
lisa_adc/README.md
lisa_uart/README.md
lisa_hwtimer/README.md
lisa_flash/README.md
lisa_sdmmc/README.md
lisa_spi/README.md
lisa_qspilcd/README.md
lisa_display/README.md
lisa_pwm/README.md
lisa_rtc/README.md
lisa_wdt/README.md
lisa_audio/README.md
lisa_i2c/README.md
lisa_touch/README.md
lisa_dvp/README.md
lisa_camera/README.md
lisa_rgb/README.md

View File

@@ -0,0 +1,16 @@
# LISA ADC Driver Example
#
# Copyright (c) 2025, LISTENAI
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_LISA_ADC)
listenai_library_named(lisa_adc)
listenai_library_sources(
lisa_adc_arcs.c # 平台适配层实现
)
listenai_include_directories(.)
endif() # CONFIG_LISA_ADC

View File

@@ -0,0 +1,16 @@
# LISA ADC Driver Configuration
#
# Copyright (c) 2025, LISTENAI
# SPDX-License-Identifier: Apache-2.0
menuconfig LISA_ADC
bool "Enable LISA ADC Driver"
default n
depends on LISA_DEVICE
help
Enable the LISA ADC device driver.
if LISA_ADC
endif # LISA_ADC

View File

@@ -0,0 +1,311 @@
# ADC 驱动
基于 lisa_device 框架的 ADC 设备驱动,为 ARCS 平台提供统一的模拟信号采样接口。
## 功能特性
- **设备支持**: ADC0 控制器,支持多个模拟输入通道
- **通道支持**: 支持通道 0-5 的模拟信号采样,以及 VBAT 和温度传感器特殊通道
- **按通道配置**: 每个通道可独立配置参考电压和分辨率
- **多种参考电压**: 支持 1.2V、3.6V、VDD_IO/2、VDD_IO/3、VDD_IO*3/2、VDD_IO、外部参考电压
- **线程安全**: 内部使用互斥锁保护并发访问
- **灵活配置**: 通过 `lisa_adc_channel_setup()` 运行时配置通道参数
## 配置选项
在 Kconfig 中启用驱动:
```kconfig
CONFIG_LISA_ADC=y
```
## API 接口
### 配置 ADC 通道
```c
int lisa_adc_channel_setup(lisa_device_t *dev, uint32_t channel,
const lisa_adc_channel_config_t *config);
```
配置指定通道的参考电压和分辨率。应在读取通道之前调用。
**参数**:
- `dev`: ADC 设备指针
- `channel`: ADC 通道号0-5普通通道6VBAT7TEMP
- `config`: 通道配置参数,包含:
- `reference`: 参考电压类型(枚举)
- `resolution`: ADC 分辨率(枚举)
**参考电压类型**:
- `LISA_ADC_REF_VDD_1V2`: 固定 1.2V 参考电压
- `LISA_ADC_REF_VDD_3V6`: 固定 3.6V 参考电压
- `LISA_ADC_REF_VDD_IO_AUTO`: VDD_IO 自动分压硬件自动选择VDD_IO≤2.4V时分压系数1/2>2.4V时分压系数1/3
- `LISA_ADC_REF_VDD_IO_AUTO_MUL3`: VDD_IO 自动分压 + 3倍缓冲测量范围扩大3倍
- `LISA_ADC_REF_EXTERNAL`: 外部参考电压 Vref_ext
**分辨率类型**:
- `LISA_ADC_RESOLUTION_10BIT`: 10-bit0-1023
**返回值**:
- `LISA_DEVICE_OK (0)`: 成功
- `LISA_DEVICE_ERR_INVALID`: 参数无效
- `LISA_DEVICE_ERR_RANGE`: 通道号超出范围
- `LISA_DEVICE_ERR_NOT_SUPPORT`: 不支持的配置
### 读取 ADC 值
```c
int lisa_adc_read(lisa_device_t *dev, uint32_t channel, uint16_t *value);
```
读取指定通道的 ADC 原始采样值。如果通道未配置将使用默认配置1.2V 参考电压)。
**参数**:
- `dev`: ADC 设备指针
- `channel`: ADC 通道号0-5普通通道6VBAT7TEMP
- `value`: 输出采样值指针
**返回值**:
- `LISA_DEVICE_OK (0)`: 成功
- `LISA_DEVICE_ERR_INVALID`: 参数无效
- `LISA_DEVICE_ERR_RANGE`: 通道号超出范围
- `LISA_DEVICE_ERR_IO`: 硬件 IO 错误
## 使用方法
### 基本步骤
1. **获取设备**: 通过 `lisa_device_get()` 获取 ADC 设备
2. **配置通道**: 调用 `lisa_adc_channel_setup()` 配置通道参数
3. **读取数据**: 调用 `lisa_adc_read()` 读取指定通道的 ADC 值
4. **电压转换**: 使用 `LISA_ADC_RAW_TO_MV` 宏将原始值转换为电压值
### 基础使用示例
```c
#include "lisa_adc.h"
// 1. 获取 ADC 设备
lisa_device_t *adc = lisa_device_get("adc0");
if (!lisa_device_ready(adc)) {
return -1;
}
// 2. 配置通道2使用1.2V参考电压
lisa_adc_channel_config_t ch2_config = {
.reference = LISA_ADC_REF_VDD_1V2,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 2, &ch2_config);
// 3. 读取 ADC 通道2的值
uint16_t raw_value;
int ret = lisa_adc_read(adc, 2, &raw_value);
if (ret == LISA_DEVICE_OK) {
// 5. 计算电压值(毫伏)
uint32_t voltage_mv = LISA_ADC_RAW_TO_MV(raw_value, 1200, 10);
printf("Channel 2: %u mV (raw: %u)\n", voltage_mv, raw_value);
}
```
### 不同通道使用不同参考电压
```c
#include "lisa_adc.h"
lisa_device_t *adc = lisa_device_get("adc0");
// 通道0: 使用1.2V参考电压测量低电压信号
lisa_adc_channel_config_t ch0_config = {
.reference = LISA_ADC_REF_VDD_1V2,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 0, &ch0_config);
// 通道2: 使用3.6V参考电压测量高电压信号
lisa_adc_channel_config_t ch2_config = {
.reference = LISA_ADC_REF_VDD_3V6,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 2, &ch2_config);
// 读取并转换
uint16_t raw0, raw2;
lisa_adc_read(adc, 0, &raw0);
lisa_adc_read(adc, 2, &raw2);
uint32_t voltage0 = LISA_ADC_RAW_TO_MV(raw0, 1200, 10);
uint32_t voltage2 = LISA_ADC_RAW_TO_MV(raw2, 3600, 10);
printf("CH0: %u mV (1.2V ref)\n", voltage0);
printf("CH2: %u mV (3.6V ref)\n", voltage2);
```
### 使用 VDD_IO 自动分压参考电压
```c
#include "lisa_adc.h"
lisa_device_t *adc = lisa_device_get("adc0");
// 配置通道3使用 VDD_IO 自动分压
lisa_adc_channel_config_t ch3_config = {
.reference = LISA_ADC_REF_VDD_IO_AUTO,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 3, &ch3_config);
// 读取 ADC 值
uint16_t raw3;
lisa_adc_read(adc, 3, &raw3);
// 电压转换: 用户需要知道实际的 VDD_IO 电压
// 假设硬件 VDD_IO = 3.3V (>2.4V),硬件自动选择分压系数 1/3
// 则参考电压 = 3.3V / 3 = 1.1V
uint32_t voltage3 = LISA_ADC_RAW_TO_MV(raw3, 1100, 10);
printf("CH3: %u mV (VDD_IO/3 ref)\n", voltage3);
// 如果硬件 VDD_IO = 2.0V (≤2.4V),硬件自动选择分压系数 1/2
// 则参考电压 = 2.0V / 2 = 1.0V
// uint32_t voltage3 = LISA_ADC_RAW_TO_MV(raw3, 1000, 10);
```
### 读取特殊通道VBAT 和温度)
```c
#include "lisa_adc.h"
lisa_device_t *adc = lisa_device_get("adc0");
// 配置 VBAT 通道(通道 6
lisa_adc_channel_config_t vbat_config = {
.reference = LISA_ADC_REF_VDD_1V2,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 6, &vbat_config);
// 读取 VBAT 通道
uint16_t vbat_raw;
if (lisa_adc_read(adc, 6, &vbat_raw) == LISA_DEVICE_OK) {
// VBAT 通道测量的是实际电池电压的 1/3
uint32_t vbat_sense_mv = LISA_ADC_RAW_TO_MV(vbat_raw, 1200, 10);
uint32_t vbat_actual_mv = vbat_sense_mv * 3;
printf("Battery voltage: %u mV\n", vbat_actual_mv);
}
// 配置温度传感器通道(通道 7
lisa_adc_channel_config_t temp_config = {
.reference = LISA_ADC_REF_VDD_1V2,
.resolution = LISA_ADC_RESOLUTION_10BIT,
};
lisa_adc_channel_setup(adc, 7, &temp_config);
// 读取温度传感器通道
uint16_t temp_raw;
if (lisa_adc_read(adc, 7, &temp_raw) == LISA_DEVICE_OK) {
printf("Temperature sensor raw value: %u\n", temp_raw);
// 温度计算公式请参考芯片数据手册
}
```
## 硬件配置
### 引脚复用配置
ADC 驱动在初始化时会自动调用板型目录中定义的 `lisa_adc_pinmux()` 函数,用于配置 ADC 通道的引脚复用。
**配置位置**:
- **定义**: `boards/<板型名>/pinmux.c` 中实现 `lisa_adc_pinmux()` 函数
- **声明**: `boards/<板型名>/pinmux.h` 中声明 `void lisa_adc_pinmux()`
- **调用时机**: ADC0 设备初始化时自动调用
**示例** (参考 `boards/arcs_evb/pinmux.c`):
```c
void lisa_adc_pinmux()
{
// 配置 PB06 为 ADC 功能(功能码 3
AON_IOMuxManager_PinConfigure(CSK_IOMUX_PAD_B, 6, 3);
}
```
**注意**:
- 该函数由板型相关代码实现,不同板型的引脚配置可能不同
- 只需配置实际使用的 ADC 通道引脚
- 内部通道VBAT、TEMP无需配置引脚
### 支持的通道
| 通道号 | 对应引脚/功能 | 说明 |
|--------|--------------|------|
| 0 | PB02 | ADC 通道 0 |
| 1 | PB03 | ADC 通道 1 |
| 2 | PB04 | ADC 通道 2 |
| 3 | PB05 | ADC 通道 3 |
| 4 | PB06 | ADC 通道 4 |
| 5 | PB07 | ADC 通道 5 |
| 6 | 内部 VBAT | 电池电压监测通道VBAT/3 |
| 7 | 内部 TEMP | 芯片温度传感器通道 |
**注意**:
- 通道 0-5 使用前需先通过 GPIO 驱动将对应引脚配置为 `LISA_GPIO_MODE_ANALOG` 模式
- 通道 6 (VBAT) 和通道 7 (TEMP) 为内部通道,无需配置引脚
### 参考电压选择
驱动支持以下参考电压(通过 `lisa_adc_channel_setup()` 配置):
| 参考电压枚举 | 说明 | 电压计算 |
|-------------|------|--------|
| `LISA_ADC_REF_VDD_1V2` | 固定 1.2V |
| `LISA_ADC_REF_VDD_3V6` | 固定 3.6V (1.2V × 3) |
| `LISA_ADC_REF_VDD_IO_AUTO` | VDD_IO 自动分压 | VDD_IO/2 (VDD_IO≤2.4V) 或 VDD_IO/3 (VDD_IO>2.4V) |
| `LISA_ADC_REF_VDD_IO_AUTO_MUL3` | VDD_IO 自动分压 + 3倍缓冲 | (VDD_IO/2 或 VDD_IO/3) × 3 |
| `LISA_ADC_REF_EXTERNAL` | 外部参考电压 | 依赖外部 Vref_ext 电路 |
**重要说明**:
1. **固定电压类型** (1.2V, 3.6V): 电压值固定,用户直接使用
2. **VDD_IO 自动分压** (`LISA_ADC_REF_VDD_IO_AUTO`):
- 硬件会**自动检测**外部 VDD_IO 电压并选择内部分压系数
- VDD_IO ≤ 2.4V 时,分压系数 1/2参考电压 = VDD_IO / 2
- VDD_IO > 2.4V 时,分压系数 1/3参考电压 = VDD_IO / 3
- 用户需要根据实际 VDD_IO 电压计算参考电压值
> **硬件限制警告**: 实际可测量的外部电压不能超过 VDD_IO (默认 3.3V)。
> 超过此电压可能损坏芯片。请确保输入信号经过适当的分压或限幅处理。
## 电压转换
使用 `LISA_ADC_RAW_TO_MV` 宏进行转换:
```c
uint32_t voltage_mv = LISA_ADC_RAW_TO_MV(raw_value, reference_mv, resolution_bits);
```
**参数**:
- `raw_value`: ADC 原始采样值
- `reference_mv`: 参考电压(毫伏)
- `resolution_bits`: 分辨率(位数,如 10
**示例**:
```c
// 10-bit ADC, 1.2V 参考电压,原始值 512
uint32_t voltage = LISA_ADC_RAW_TO_MV(512, 1200, 10);
// 结果: (512 × 1200) / 1024 = 600 mV
```
## 注意事项
1. **通道配置**: 建议在读取通道之前先调用 `lisa_adc_channel_setup()` 配置通道参数
2. **引脚配置**: 使用通道 0-5 前必须先将对应引脚配置为 ADC 模拟输入模式
3. **特殊通道**: 通道 6 (VBAT) 和通道 7 (TEMP) 为内部通道,无需配置引脚
4. **通道范围**: 支持通道 0-70-5外部通道6VBAT7TEMP
5. **VBAT 分压**: VBAT 通道测量的是实际电池电压的 1/3计算实际电压时需乘以 3
6. **温度转换**: 温度传感器的原始值需根据芯片数据手册提供的公式进行温度转换
7. **参考电压计算**: 对于依赖硬件的参考电压VDD_IO 系列、外部参考),用户需要根据实际电路确定电压值用于转换计算
8. **分辨率支持**: ARCS 平台目前仅支持 10-bit 分辨率
9. **线程安全**: 驱动内部已实现线程保护,可在多线程环境中使用
10. **转换时间**: 每次读取会触发一次 ADC 转换,需等待转换完成(通常几微秒到几毫秒)
11. **输入范围**: 外部通道输入电压应在 0V 到参考电压之间,超出范围可能损坏硬件或得到错误结果
12. **未配置通道**: 如果读取未配置的通道将自动使用默认配置1.2V 参考电压10-bit 分辨率)

View File

@@ -0,0 +1,170 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_adc.h
* @brief LISA ADC 设备驱动接口示例
*/
#pragma once
#include "lisa_device.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ========================================================================
* ADC 类型定义
* ======================================================================== */
#define LISA_ADC_VALUE_INVALID 0xFFFFU /**< 无效的 ADC 采样占位值 */
/**
* @brief ADC 参考电压枚举
*
* 定义 ADC 支持的参考电压类型。
* - 固定电压类型直接在枚举名中标明电压值(如 LISA_ADC_REF_VDD_1V2)
* - 可变电压类型需要用户根据硬件电路确定实际电压值
*
* @warning 硬件限制: 实际可测量的外部电压不能超过 VDD_IO (默认 3.3V)。
* 超过此电压可能损坏芯片。请确保输入信号经过适当的分压或限幅处理。
*
* @note LISA_ADC_REF_VDD_IO_AUTO 比较特殊:
* 硬件会自动根据外部 VDD_IO 电压选择内部分压系数:
* - 当 VDD_IO ≤ 2.4V 时,分压系数为 1/2 (参考电压 = VDD_IO/2)
* - 当 VDD_IO > 2.4V 时,分压系数为 1/3 (参考电压 = VDD_IO/3)
*/
typedef enum {
LISA_ADC_REF_VDD_1V2 = 0, /**< 固定 1.2V 参考电压 (内部 Bandgap) */
LISA_ADC_REF_VDD_3V6 = 1, /**< 固定 3.6V 参考电压 (Vbg 1.2V + 3倍缓冲) */
LISA_ADC_REF_VDD_IO_AUTO = 2, /**< VDD_IO 自动分压: VDD_IO/2(≤2.4V) 或 VDD_IO/3(>2.4V) */
LISA_ADC_REF_VDD_IO_AUTO_MUL3 = 3,/**< VDD_IO 自动分压 + 3倍缓冲 */
LISA_ADC_REF_EXTERNAL = 4, /**< 外部参考电压 Vref_ext (依赖硬件外部参考) */
} lisa_adc_reference_t;
/**
* @brief ADC 分辨率枚举
*/
typedef enum {
LISA_ADC_RESOLUTION_10BIT = 10, /**< 10-bit 分辨率 (0-1023) */
} lisa_adc_resolution_t;
/**
* @brief ADC 通道配置结构体
*
* 用于配置单个 ADC 通道的采样参数,使不同通道可以使用不同的参考电压和分辨率。
*/
typedef struct {
lisa_adc_reference_t reference; /**< 参考电压选择 */
lisa_adc_resolution_t resolution; /**< ADC 分辨率 */
} lisa_adc_channel_config_t;
/* ========================================================================
* ADC 设备 API 结构体
* ======================================================================== */
typedef struct {
/**
* @brief 读取单次 ADC 转换结果
*
* @param dev ADC 设备指针
* @param channel 通道号(由具体驱动定义范围)
* @param value 输出采样值指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 驱动未实现 read 功能
* @return 其他负值 具体错误码,详见 lisa_device.h
*/
int (*read)(lisa_device_t *dev, uint32_t channel, uint16_t *value);
/**
* @brief 配置 ADC 通道参数 (可选)
*
* @param dev ADC 设备指针
* @param channel 通道号
* @param config 通道配置参数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_NOT_SUPPORT 驱动未实现或不支持该配置
* @return 其他负值 具体错误码
*/
int (*channel_setup)(lisa_device_t *dev, uint32_t channel,
const lisa_adc_channel_config_t *config);
} lisa_adc_api_t;
/* ========================================================================
* ADC 对外接口函数
* ======================================================================== */
/**
* @brief 配置 ADC 通道
*
* 在读取通道之前调用,用于设置该通道的参考电压和分辨率。
* 不同通道可以配置不同的参数,以适应不同的测量需求。
*
* @param dev ADC 设备指针
* @param channel 通道号
* @param config 通道配置参数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 驱动未实现或不支持该配置
*/
static inline int lisa_adc_channel_setup(lisa_device_t *dev, uint32_t channel,
const lisa_adc_channel_config_t *config)
{
if (!dev || !dev->api || !config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_adc_api_t *api = (lisa_adc_api_t *)dev->api;
if (!api->channel_setup) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->channel_setup(dev, channel, config);
}
/**
* @brief 读取单次 ADC 转换结果
*
* 该示例仅提供最小接口:通过驱动实现的 @ref lisa_adc_api_t::read
* 直接获取指定通道的原始采样值。
*
* @param dev ADC 设备指针
* @param channel 通道号(由具体驱动定义范围)
* @param value 输出采样值指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 驱动未实现 read 功能
* @return 其他负值 具体错误码,详见 lisa_device.h
*/
static inline int lisa_adc_read(lisa_device_t *dev, uint32_t channel, uint16_t *value)
{
if (!dev || !dev->api || !value) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_adc_api_t *api = (lisa_adc_api_t *)dev->api;
return api->read ? api->read(dev, channel, value) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 辅助宏: 将 ADC 原始值转换为电压(毫伏)
*
* @param raw_value ADC 原始采样值
* @param reference_mv 参考电压(毫伏)
* @param resolution 分辨率(bits)
*
* @return 电压值(毫伏)
*/
#define LISA_ADC_RAW_TO_MV(raw_value, reference_mv, resolution) \
(((uint32_t)(raw_value) * (reference_mv)) / (1UL << (resolution)))
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,307 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_adc_arcs.c
* @brief LISA ADC ARCS 平台适配层
*
* 此文件实现 ARCS 芯片平台的 ADC 硬件适配
*/
#include "lisa_adc.h"
#include "Driver_GPADC.h"
#include <stddef.h>
#include <string.h>
#include "lisa_mutex.h"
#include "board.h"
#define LOG_TAG "lisa_adc_arcs"
#include <lisa_log.h>
/* ADC 触发读取次数 */
#define LISA_ADC_TRIGGER_COUNT 3
#define DEVICE_LOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_lock(priv->mutex, LISA_OS_WAIT_FOREVER); \
} \
} while (0)
#define DEVICE_UNLOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_unlock(priv->mutex); \
} \
} while (0)
/* ===== ADC 通道映射定义 ===== */
#define CHANNEL_TO_SEL(ch) (1UL << (ch))
/* 定义特殊通道号 */
#define LISA_ADC_CHANNEL_VBAT (6) /* VBAT通道 */
#define LISA_ADC_CHANNEL_TEMP (7) /* 温度传感器通道 */
/* ===== 参考电压映射表 ===== */
typedef struct {
lisa_adc_reference_t ref_type; /* 枚举类型 */
uint32_t fixed_voltage_mv; /* 固定电压值(毫伏, 0表示依赖硬件) */
uint8_t vref_sel; /* ARCS硬件: vref_sel (0-3) */
uint8_t vin_buf_enable; /* ARCS硬件: vin_buf_enable */
} reference_map_t;
static const reference_map_t reference_map[] = {
/* 枚举类型 固定电压 vref_sel vin_buf 说明 */
{LISA_ADC_REF_VDD_1V2, 1200, 0, 0}, /* ref=0: Vbg 1.2V */
{LISA_ADC_REF_VDD_3V6, 3600, 0, 1}, /* ref=0: Vbg 1.2V, vin_buf=1 (采样值*3 = 3.6V) */
{LISA_ADC_REF_VDD_IO_AUTO, 0, 2, 0}, /* ref=2: 硬件自动选择分压系数 1/2(≤2.4V) 或 1/3(>2.4V) */
{LISA_ADC_REF_VDD_IO_AUTO_MUL3, 0, 2, 1}, /* ref=2: 自动分压 + vin_buf=1 (采样值*3) */
{LISA_ADC_REF_EXTERNAL, 0, 3, 0}, /* ref=3: Vref_ext 外部参考电压 */
};
/* ===== 通道配置条目 ===== */
typedef struct {
lisa_adc_reference_t reference; /* 参考电压类型 */
lisa_adc_resolution_t resolution; /* 分辨率 */
uint8_t vref_sel; /* 硬件配置: 参考电压选择 */
uint8_t vin_buf_enable; /* 硬件配置: 输入缓冲使能 */
bool configured; /* 是否已配置 */
} channel_config_entry_t;
/* ===== ADC 设备私有数据 ===== */
typedef struct {
void *hal_handler; /* HAL GPADC 句柄 */
lisa_mutex_t *mutex; /* 互斥锁 */
channel_config_entry_t channel_configs[8]; /* 每个通道的配置 */
} lisa_adc_priv_t;
/* ===== ADC 设备静态实例 ===== */
static lisa_adc_priv_t adc0_priv;
/* ===== 内部辅助函数 ===== */
/**
* @brief 根据参考电压枚举查找硬件配置
*/
static const reference_map_t *find_reference_map(lisa_adc_reference_t ref)
{
for (size_t i = 0; i < sizeof(reference_map) / sizeof(reference_map[0]); i++) {
if (reference_map[i].ref_type == ref) {
return &reference_map[i];
}
}
return NULL;
}
/**
* @brief 检查通道号有效性
*/
static inline int check_channel_valid(uint32_t channel)
{
/* GPADC支持的通道: 0-5(普通通道), 6(VBAT), 7(TEMP) */
if (channel <= CSK_GPADC_CHANNEL5) {
return LISA_DEVICE_OK; /* 普通通道 0-5 */
}
if (channel == LISA_ADC_CHANNEL_VBAT || channel == LISA_ADC_CHANNEL_TEMP) {
return LISA_DEVICE_OK; /* 特殊通道 VBAT/TEMP */
}
return LISA_DEVICE_ERR_RANGE;
}
/**
* @brief 将通道号转换为HAL通道选择位
*/
static uint32_t channel_to_hal_sel(uint32_t channel)
{
switch (channel) {
case 0:
return CSK_GPADC_CHANNEL_SEL_0;
case 1:
return CSK_GPADC_CHANNEL_SEL_1;
case 2:
return CSK_GPADC_CHANNEL_SEL_2;
case 3:
return CSK_GPADC_CHANNEL_SEL_3;
case 4:
return CSK_GPADC_CHANNEL_SEL_4;
case 5:
return CSK_GPADC_CHANNEL_SEL_5;
case LISA_ADC_CHANNEL_VBAT:
return CSK_GPADC_CHANNEL_SEL_VBAT;
case LISA_ADC_CHANNEL_TEMP:
return CSK_GPADC_CHANNEL_SEL_TEMP;
default:
return 0;
}
}
/* ===== ARCS平台ADC实现函数 ===== */
/**
* @brief 配置 ADC 通道
*/
static int arcs_adc_channel_setup(lisa_device_t *dev, uint32_t channel,
const lisa_adc_channel_config_t *config)
{
if (!lisa_device_is_initialized(dev) || !config) {
return LISA_DEVICE_ERR_INVALID;
}
if (check_channel_valid(channel) != LISA_DEVICE_OK) {
return LISA_DEVICE_ERR_RANGE;
}
/* 查找参考电压映射 */
const reference_map_t *ref_map = find_reference_map(config->reference);
if (!ref_map) {
LISA_LOGE(LOG_TAG, "Unsupported reference type: %d", config->reference);
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
/* 检查分辨率支持 (ARCS只支持10-bit) */
if (config->resolution != LISA_ADC_RESOLUTION_10BIT) {
LISA_LOGE(LOG_TAG, "Unsupported resolution: %d-bit (only 10-bit supported)",
config->resolution);
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
lisa_adc_priv_t *priv = (lisa_adc_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
/* 保存通道配置 */
channel_config_entry_t *entry = &priv->channel_configs[channel];
entry->reference = config->reference;
entry->resolution = config->resolution;
entry->vref_sel = ref_map->vref_sel;
entry->vin_buf_enable = ref_map->vin_buf_enable;
entry->configured = true;
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "Channel %lu configured: ref=%d, %u-bit, vref_sel=%u, vin_buf=%u",
channel, config->reference, config->resolution,
entry->vref_sel, entry->vin_buf_enable);
return LISA_DEVICE_OK;
}
/**
* @brief 读取单次 ADC 转换结果
*/
static int arcs_adc_read(lisa_device_t *dev, uint32_t channel, uint16_t *value)
{
if (!lisa_device_is_initialized(dev) || !value) {
return LISA_DEVICE_ERR_INVALID;
}
if (check_channel_valid(channel) != LISA_DEVICE_OK) {
return LISA_DEVICE_ERR_RANGE;
}
lisa_adc_priv_t *priv = (lisa_adc_priv_t *)dev->priv_data;
uint32_t hal_channel_sel = channel_to_hal_sel(channel);
if (hal_channel_sel == 0) {
LISA_LOGE(LOG_TAG, "Invalid channel %lu", channel);
return LISA_DEVICE_ERR_RANGE;
}
DEVICE_LOCK(priv);
/* 检查通道是否已配置 */
channel_config_entry_t *cfg = &priv->channel_configs[channel];
if (!cfg->configured) {
LISA_LOGW(LOG_TAG, "Channel %lu not configured, using default 1.2V reference", channel);
/* 使用默认配置: 1.2V 参考电压, 10-bit */
const reference_map_t *ref_map = find_reference_map(LISA_ADC_REF_VDD_1V2);
cfg->reference = LISA_ADC_REF_VDD_1V2;
cfg->resolution = LISA_ADC_RESOLUTION_10BIT;
cfg->vref_sel = ref_map->vref_sel;
cfg->vin_buf_enable = ref_map->vin_buf_enable;
cfg->configured = true;
}
HAL_GPADC_SetVrefSel(priv->hal_handler, cfg->vref_sel);
HAL_GPADC_SetVinBuf_Enable(priv->hal_handler, cfg->vin_buf_enable);
HAL_GPADC_Control(priv->hal_handler, hal_channel_sel | CSK_GPADC_DMA_ENABLE(0));
HAL_GPADC_SetTriggerNum(GPADC(), LISA_ADC_TRIGGER_COUNT);
/* 启动ADC转换 */
if (HAL_GPADC_Start(priv->hal_handler) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to start ADC");
return LISA_DEVICE_ERR_IO;
}
/* 等待转换完成 */
if (HAL_GPADC_PollForConversion(priv->hal_handler, 0) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "ADC conversion timeout");
return LISA_DEVICE_ERR_IO;
}
/* 连续读取 LISA_ADC_TRIGGER_COUNT 次,使用最后一次的值 */
uint16_t adc_value = 0;
for (int i = 0; i < LISA_ADC_TRIGGER_COUNT; i++) {
adc_value = HAL_GPADC_GetValue(priv->hal_handler, hal_channel_sel);
}
*value = adc_value;
DEVICE_UNLOCK(priv);
LISA_LOGD(LOG_TAG, "Channel %lu: raw value 0x%x (%u)", channel, adc_value, adc_value);
return LISA_DEVICE_OK;
}
/* ===== ARCS ADC API 实例 ===== */
static const lisa_adc_api_t arcs_adc_api = {
.read = arcs_adc_read,
.channel_setup = arcs_adc_channel_setup,
};
/* ===== 设备初始化函数 ===== */
static int arcs_adc0_init(void)
{
/* 清空私有数据 */
memset(&adc0_priv, 0, sizeof(lisa_adc_priv_t));
/* 获取 HAL GPADC 句柄 */
adc0_priv.hal_handler = GPADC();
if (!adc0_priv.hal_handler) {
LISA_LOGE(LOG_TAG, "Failed to get GPADC handler");
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* 创建互斥锁 */
adc0_priv.mutex = lisa_mutex_create();
if (!adc0_priv.mutex) {
LISA_LOGE(LOG_TAG, "Failed to create mutex");
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* 初始化 HAL GPADC */
if (HAL_GPADC_Initialize(adc0_priv.hal_handler) != 0) {
LISA_LOGE(LOG_TAG, "Failed to initialize GPADC");
return LISA_DEVICE_ERR_INIT_FAIL;
}
lisa_adc_pinmux();
LISA_LOGI(LOG_TAG, "ADC0 initialized successfully");
return LISA_DEVICE_OK;
}
/* ===== 设备注册 ===== */
LISA_DEVICE_REGISTER(adc0, /* 设备名称 */
&arcs_adc_api, /* API指针 */
&adc0_priv, /* 私有数据指针 */
NULL, /* 用户数据 */
arcs_adc0_init, /* 初始化函数 */
LISA_DEVICE_PRIORITY_NORMAL); /* 优先级 */

View File

@@ -0,0 +1,23 @@
# LISA Audio Driver
#
# Copyright (c) 2025, LISTENAI
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_LISA_AUDIO_DEVICE)
listenai_library_named(lisa_audio_device)
if(CONFIG_PORT_ARCS)
listenai_library_sources(
lisa_audio_arcs.c
port/arcs/lisa_audio_record.c
port/arcs/lisa_audio_play.c
port/arcs/audio_adc_init.c
port/arcs/audio_dac_init.c
)
listenai_include_directories(port/arcs)
endif()
listenai_include_directories(.)
endif() # CONFIG_LISA_AUDIO_DEVICE

View File

@@ -0,0 +1,194 @@
# SPDX-License-Identifier: Apache-2.0
menuconfig LISA_AUDIO_DEVICE
bool "Enable LISA Audio Driver"
default n
depends on LISA_DEVICE
help
Enable LISA Audio device driver for ADC/DAC operations.
Provides unified audio recording (ADC) and playback (DAC) interfaces.
if LISA_AUDIO_DEVICE
choice
prompt "Audio Implementation"
default PORT_ARCS
help
Select the audio driver implementation.
config PORT_ARCS
bool "Use ARCS port implementation"
help
Enable ARCS platform specific implementation.
endchoice
config LISA_AUDIO_INIT_PRIORITY
int "Audio device initialization priority"
default 50
range 0 99
help
Initialization priority for audio device (0-99, lower value = higher priority).
# ===== Dispatcher Configuration =====
menu "Audio Dispatcher Configuration"
config LISA_AUDIO_DISPATCH_TASK_STACK_SIZE
int "Dispatch task stack size (bytes)"
default 4096
help
Stack size for the unified audio dispatch task.
config LISA_AUDIO_DISPATCH_TASK_PRIORITY
int "Dispatch task priority"
default 2
help
Priority of the audio dispatch task
endmenu
# ===== Record (ADC) Configuration =====
menu "Record (ADC) Configuration"
config LISA_AUDIO_RECORD_USE_PIPO
bool "Enable Record PiPo (Ping-Pong) mode"
default y
help
Enable Ping-Pong buffer mode for continuous audio recording.
config LISA_AUDIO_RECORD_USE_DMIC
bool "Use digital microphone (DMIC)"
default n
help
Enable PDM mode for digital microphones.
Disable for analog microphones (ADC mode).
config LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
bool "Enable individual gain control on left/right channels"
default n
choice
prompt "LDOVA Output Voltage Level"
default LISA_AUDIO_RECORD_LDOVA_1V905
help
Select the LDOVA (Low Dropout Voltage for Analog) output voltage level.
This controls the analog power supply voltage for the ADC module.
config LISA_AUDIO_RECORD_LDOVA_1V68
bool "1.68V"
help
LDOVA output voltage: 1.68V (SEL_VOUT_LDOVA = 0x8)
config LISA_AUDIO_RECORD_LDOVA_1V785
bool "1.785V"
help
LDOVA output voltage: 1.785V (SEL_VOUT_LDOVA = 0xf)
config LISA_AUDIO_RECORD_LDOVA_1V8
bool "1.8V - Default"
help
LDOVA output voltage: 1.8V (SEL_VOUT_LDOVA = 0x0)
This is the default voltage level.
config LISA_AUDIO_RECORD_LDOVA_1V815
bool "1.815V"
help
LDOVA output voltage: 1.815V (SEL_VOUT_LDOVA = 0x1)
config LISA_AUDIO_RECORD_LDOVA_1V905
bool "1.905V"
help
LDOVA output voltage: 1.905V (SEL_VOUT_LDOVA = 0x7)
endchoice
endmenu
# ===== Play (DAC) Configuration =====
menu "Play (DAC) Configuration"
config LISA_AUDIO_PLAY_PA_ENABLE
bool "Enable power amplifier (PA) control"
default n
help
Enable GPIO control for external power amplifier.
PA will be enabled after playback output stabilizes to prevent pop noise.
if LISA_AUDIO_PLAY_PA_ENABLE
config LISA_AUDIO_PLAY_PA_PAD
int "PA GPIO pad (0=PAD_A, 1=PAD_B)"
default 0
range 0 1
help
GPIO pad selection for PA control pin.
0 = CSK_IOMUX_PAD_A, 1 = CSK_IOMUX_PAD_B
config LISA_AUDIO_PLAY_PA_PIN
int "PA GPIO pin number"
default 28
range 0 31
help
GPIO pin number for PA control (0-31).
Example: Pin 28 on PAD_A = GPIOA28
config LISA_AUDIO_PLAY_PA_PULSE_COUNT
int "PA enable pulse count"
default 3
range 0 10
help
Number of high-low pulses to send when enabling PA.
Some PA chips require pulse sequence for proper startup.
Set to 0 to simply set pin high without pulses.
config LISA_AUDIO_PLAY_PA_PULSE_US
int "PA pulse duration (microseconds)"
default 50
range 10 1000
help
Duration of each high/low level in PA enable pulse sequence.
endif # LISA_AUDIO_PLAY_PA_ENABLE
config LISA_AUDIO_PLAY_ECHO_ENABLE
bool "Enable Play Echo capture"
default n
help
Enable Echo capture during playback. Echo data is a copy of the
DAC output signal, useful for AEC (Acoustic Echo Cancellation).
IMPORTANT: Echo uses DMA channel 3 (GPDMA_ECHO_CHN), which conflicts
with Record stereo mode (GPDMA_ADC1_CHN = 3).
Cannot enable simultaneously with Record stereo configuration.
config LISA_AUDIO_RECORD_BUFFER_COUNT
int "Record buffer count"
default 32
help
Number of Record buffers for ping-pong reception.
More buffers reduce the risk of data loss but consume more RAM.
Minimum: 2 (for ping-pong mode)
Recommended: 4-32
Memory usage = buffer_count × buffer_samples × 2 bytes
config LISA_AUDIO_RECORD_BUFFER_SAMPLES
int "Record buffer samples per buffer"
default 256
range 64 2048
help
Number of audio samples (16-bit) per Record buffer.
Smaller buffers = lower latency, higher CPU overhead
Larger buffers = higher latency, lower CPU overhead
Example: 256 samples @ 16kHz = 16ms latency
Memory per buffer = samples × 2 bytes
endmenu
endif # LISA_AUDIO_DEVICE

View File

@@ -0,0 +1,314 @@
# Audio 驱动
基于 lisa_device 框架的 Audio 设备驱动,为 ARCS 平台提供统一的音频录音(Record)和播放(Play)接口。
## 功能特性
- **设备支持**: Audio0 音频设备
- **录音功能**: 支持多通道录音、增益控制、高通滤波、差分输入
- **播放功能**: 支持多通道播放、增益控制、缓冲区管理
- **音频格式**: 灵活配置采样率8kHz-96kHz、通道数单声道/立体声、采样位深16/24/32位
- **统一回调**: 提供统一的音频事件回调,同时接收录音数据和播放回声数据
- **相位补偿**: 支持录音和播放之间的相位调整
- **控制功能**: 支持启动、停止、暂停、恢复等控制操作
## 配置选项
`prj.conf` 中启用驱动:
```kconfig
CONFIG_LISA_AUDIO_DEVICE=y
CONFIG_LISA_AUDIO0=y # 启用 Audio0 设备
```
## API 接口
### 回调管理接口
```c
int lisa_audio_register_callback(lisa_device_t *dev, lisa_audio_callback_t callback, void *user_data);
int lisa_audio_unregister_callback(lisa_device_t *dev, lisa_audio_callback_t callback);
```
### 录音接口
```c
// 配置
int lisa_audio_record_config(lisa_device_t *dev, const lisa_audio_record_config_t *config);
// 控制
int lisa_audio_record_start(lisa_device_t *dev);
int lisa_audio_record_stop(lisa_device_t *dev);
int lisa_audio_record_pause(lisa_device_t *dev);
int lisa_audio_record_resume(lisa_device_t *dev);
int lisa_audio_record_set_gain(lisa_device_t *dev, const lisa_audio_gain_t *gain);
```
### 播放接口
```c
// 配置
int lisa_audio_play_config(lisa_device_t *dev, const lisa_audio_play_config_t *config);
// 数据传输
int lisa_audio_play_write(lisa_device_t *dev, const void *buffer, uint32_t samples);
int lisa_audio_play_get_buffer(lisa_device_t *dev, void **buffer, uint32_t timeout_ms);
// 控制
int lisa_audio_play_start(lisa_device_t *dev);
int lisa_audio_play_stop(lisa_device_t *dev);
int lisa_audio_play_flush(lisa_device_t *dev);
int lisa_audio_play_set_gain(lisa_device_t *dev, const lisa_audio_gain_t *gain);
```
### 通用接口
```c
int lisa_audio_set_phase_compensation(lisa_device_t *dev, const lisa_audio_phase_compensation_t *compensation);
int lisa_audio_get_phase_compensation(lisa_device_t *dev, lisa_audio_phase_compensation_t *compensation);
int lisa_audio_ioctl(lisa_device_t *dev, uint8_t cmd, void *arg);
```
## 使用示例
### 基本录音配置与启动
```c
#include "lisa_audio.h"
// 获取 Audio0 设备
lisa_device_t *audio0 = lisa_device_get_by_name("audio0");
if (!audio0) {
return -1;
}
// 配置录音参数
lisa_audio_record_config_t record_config = {
.format = {
.sample_rate = LISA_AUDIO_RATE_16K, // 16kHz 采样率
.channels = LISA_AUDIO_CH_LEFT, // 左声道
.sample_bits = LISA_AUDIO_BIT_16, // 16位采样
},
.gain = {
.analog_gain = 0, // 模拟增益 0dB
.digital_gain = 0, // 数字增益 0dB
},
.enable_hpf = true, // 启用高通滤波器
.differential_input = false, // 单端输入
.buffer_count = 2, // 2个缓冲区
.buffer_samples = 256, // 每个缓冲区256采样点
};
lisa_audio_record_config(audio0, &record_config);
// 启动录音
lisa_audio_record_start(audio0);
```
### 基本播放配置与数据写入
```c
// 配置播放参数
lisa_audio_play_config_t play_config = {
.format = {
.sample_rate = LISA_AUDIO_RATE_16K, // 16kHz 采样率
.channels = LISA_AUDIO_CH_STEREO, // 立体声
.sample_bits = LISA_AUDIO_BIT_16, // 16位采样
},
.gain = {
.analog_gain = 0, // 模拟增益 0dB
.digital_gain = 0, // 数字增益 0dB
},
.buffer_count = 4, // 4个缓冲区
.buffer_samples = 512, // 每个缓冲区512采样点
};
lisa_audio_play_config(audio0, &play_config);
// 启动播放
lisa_audio_play_start(audio0);
// 写入音频数据
int16_t audio_data[1024]; // 准备音频数据
// ... 填充 audio_data ...
int ret = lisa_audio_play_write(audio0, audio_data, 1024);
if (ret > 0) {
// 成功写入 ret 个采样点
}
// 等待播放完成
lisa_audio_play_flush(audio0);
// 停止播放
lisa_audio_play_stop(audio0);
```
### 使用统一回调接收音频数据
```c
// 音频数据回调函数
void audio_event_callback(const lisa_audio_event_t *event, void *user_data)
{
// 处理录音数据
if (event->record_buffer && event->record_samples > 0) {
const int16_t *record_data = (const int16_t *)event->record_buffer;
// 处理录音数据...
// 注意:回调在中断上下文中执行,应尽量简短快速
}
// 处理播放回声数据
if (event->echo_buffer && event->echo_samples > 0) {
const int16_t *echo_data = (const int16_t *)event->echo_buffer;
// 处理回声数据...
}
}
// 注册回调
lisa_audio_register_callback(audio0, audio_event_callback, NULL);
// 配置并启动录音和播放
lisa_audio_record_config(audio0, &record_config);
lisa_audio_play_config(audio0, &play_config);
lisa_audio_record_start(audio0);
lisa_audio_play_start(audio0);
// ... 运行一段时间 ...
// 停止并注销回调
lisa_audio_record_stop(audio0);
lisa_audio_play_stop(audio0);
lisa_audio_unregister_callback(audio0, audio_event_callback);
```
### 动态调整增益
```c
// 设置录音增益
lisa_audio_gain_t record_gain = {
.analog_gain = 6, // 模拟增益 +6dB
.digital_gain = 3, // 数字增益 +3dB
};
lisa_audio_record_set_gain(audio0, &record_gain);
// 设置播放增益
lisa_audio_gain_t play_gain = {
.analog_gain = -3, // 模拟增益 -3dB
.digital_gain = 0, // 数字增益 0dB
};
lisa_audio_play_set_gain(audio0, &play_gain);
```
### 相位补偿配置
```c
// 设置相位补偿(用于调整录音和播放之间的相位差)
lisa_audio_phase_compensation_t phase_comp = {
.record_skip_samples = 0, // 录音数据不丢弃
.echo_skip_samples = 32, // ECHO数据丢弃32个采样点
};
lisa_audio_set_phase_compensation(audio0, &phase_comp);
// 读取当前相位补偿配置
lisa_audio_phase_compensation_t current_comp;
lisa_audio_get_phase_compensation(audio0, &current_comp);
```
### 使用播放缓冲区零拷贝方式
```c
// 获取空闲播放缓冲区
void *buffer;
int buffer_size = lisa_audio_play_get_buffer(audio0, &buffer, 1000); // 超时1秒
if (buffer_size > 0) {
// 直接向缓冲区写入数据(零拷贝)
int16_t *audio_buf = (int16_t *)buffer;
for (int i = 0; i < buffer_size; i++) {
audio_buf[i] = /* 生成音频数据 */;
}
// 提交数据
lisa_audio_play_write(audio0, buffer, buffer_size);
}
```
### 暂停和恢复录音
```c
// 启动录音
lisa_audio_record_start(audio0);
// ... 录音一段时间 ...
// 暂停录音
lisa_audio_record_pause(audio0);
// ... 暂停期间不会产生录音数据 ...
// 恢复录音
lisa_audio_record_resume(audio0);
// 停止录音
lisa_audio_record_stop(audio0);
```
## 音频格式配置
### 采样率
支持的采样率:
- `LISA_AUDIO_RATE_8K` - 8kHz
- `LISA_AUDIO_RATE_16K` - 16kHz
- `LISA_AUDIO_RATE_24K` - 24kHz
- `LISA_AUDIO_RATE_32K` - 32kHz
- `LISA_AUDIO_RATE_48K` - 48kHz
- `LISA_AUDIO_RATE_96K` - 96kHz
### 通道配置
- `LISA_AUDIO_CH_LEFT` - 左声道
- `LISA_AUDIO_CH_RIGHT` - 右声道
- `LISA_AUDIO_CH_STEREO` - 立体声(左+右)
### 采样位深
- `LISA_AUDIO_BIT_16` - 16位采样 (2字节)
- `LISA_AUDIO_BIT_24` - 24位采样 (3字节)
- `LISA_AUDIO_BIT_32` - 32位采样 (4字节)
### 采样点计算
对于立体声音频,采样点数是**所有通道的总和**。例如:
- 单声道 256 个采样点 = 256 个样本
- 立体声 256 个采样点 = 每个声道 128 个样本
对于 16 位立体声:
- 缓冲区大小(字节) = 采样点数 × 2字节/采样) × 2声道 = 采样点数 × 4
## 状态定义
- `LISA_AUDIO_STATUS_IDLE` - 空闲状态
- `LISA_AUDIO_STATUS_RUNNING` - 运行中
- `LISA_AUDIO_STATUS_PAUSED` - 已暂停
- `LISA_AUDIO_STATUS_ERROR` - 错误状态
## 注意事项
1. **设备初始化**: 使用前需确保设备已正确初始化
2. **配置顺序**: 需先调用配置函数,再调用启动函数
3. **回调上下文**: 音频数据回调在中断上下文中执行,应尽量简短快速,不要执行耗时操作
4. **缓冲区管理**:
- 录音缓冲区由驱动内部管理
- 播放缓冲区数量影响延迟和缓冲深度
5. **采样点数**: 对于多通道音频,采样点数是所有通道的总和
6. **增益范围**: 增益单位为 dB具体范围取决于硬件支持
7. **相位补偿**: 用于调整录音和播放之间的时间对齐,具体值需根据实际测试确定
8. **线程安全**: 驱动内部使用互斥锁保护关键操作
## 文件说明
- `lisa_audio.h` - 驱动头文件,包含所有 API 和类型定义
- `lisa_audio_arcs.c` - ARCS 平台适配实现
- `lisa_audio_internal.h` - 内部实现头文件
- `CMakeLists.txt` - 构建配置
- `Kconfig` - 配置选项

View File

@@ -0,0 +1,702 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_audio.h
* @brief LISA Audio 设备驱动接口
*
* 基于 lisa_device 框架的音频设备驱动,提供统一的音频录音(Record)和播放(Play)接口。
* 支持音频数据回调、增益控制、相位补偿等功能。
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "lisa_device.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ========================================================================
* Audio 类型定义
* ======================================================================== */
/**
* @brief 音频设备状态
*/
typedef enum {
LISA_AUDIO_STATUS_IDLE = 0, /* 空闲状态 */
LISA_AUDIO_STATUS_RUNNING, /* 运行中 */
LISA_AUDIO_STATUS_PAUSED, /* 已暂停 */
LISA_AUDIO_STATUS_ERROR, /* 错误状态 */
} lisa_audio_status_t;
/**
* @brief 音频采样率
*/
typedef enum {
LISA_AUDIO_RATE_8K = 8000, /* 8kHz 采样率 */
LISA_AUDIO_RATE_16K = 16000, /* 16kHz 采样率 */
LISA_AUDIO_RATE_24K = 24000, /* 24kHz 采样率 */
LISA_AUDIO_RATE_32K = 32000, /* 32kHz 采样率 */
LISA_AUDIO_RATE_48K = 48000, /* 48kHz 采样率 */
LISA_AUDIO_RATE_96K = 96000, /* 96kHz 采样率 */
} lisa_audio_rate_t;
/**
* @brief 音频通道配置
*/
typedef enum {
LISA_AUDIO_CH_LEFT = 0x01, /* 左声道 */
LISA_AUDIO_CH_RIGHT = 0x02, /* 右声道 */
LISA_AUDIO_CH_STEREO = 0x03, /* 立体声(左+右) */
} lisa_audio_channel_t;
/**
* @brief 音频采样位深
*/
typedef enum {
LISA_AUDIO_BIT_16 = 2, /* 16位采样 (2字节) */
LISA_AUDIO_BIT_24 = 3, /* 24位采样 (3字节) */
LISA_AUDIO_BIT_32 = 4, /* 32位采样 (4字节) */
} lisa_audio_bits_t;
/**
* @brief 音频格式配置
*/
typedef struct {
lisa_audio_rate_t sample_rate; /* 采样率 */
lisa_audio_channel_t channels; /* 通道配置 */
lisa_audio_bits_t sample_bits; /* 采样位深 */
} lisa_audio_format_t;
/**
* @brief 音频增益配置
*/
typedef struct {
int8_t analog_gain; /* 模拟增益 (dB) */
int8_t digital_gain; /* 数字增益 (dB) */
} lisa_audio_gain_t;
/**
* @brief 音频事件结构
*
* 用于统一回调函数中传递音频数据事件,包含录音数据和回声数据
*/
typedef struct {
const void *record_buffer; /* 录音数据缓冲区,无数据时为 NULL */
const void *echo_buffer; /* 播放回声数据缓冲区,无数据时为 NULL */
uint32_t record_samples; /* 录音采样点数 */
uint32_t echo_samples; /* 回声采样点数 */
} lisa_audio_event_t;
/**
* @brief 相位补偿配置
*
* 用于调整录音和播音之间的相位差
*/
typedef struct {
uint16_t record_skip_samples; /* 录音数据丢弃的采样点数 */
uint16_t echo_skip_samples; /* ECHO数据丢弃的采样点数 */
} lisa_audio_phase_compensation_t;
/**
* @brief 音频数据回调函数类型
*
* 统一的音频事件回调,用于接收录音和播放回声数据
*
* @param event 音频事件结构,包含数据缓冲区指针
* @param user_data 用户自定义数据
*
* @note 回调函数在中断上下文中执行,应尽量简短快速
*/
typedef void (*lisa_audio_callback_t)(const lisa_audio_event_t *event, void *user_data);
/**
* @brief 录音配置结构
*/
typedef struct {
lisa_audio_format_t format; /* 音频格式(采样率、通道、位深) */
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
lisa_audio_gain_t gain_l; /* 左声道增益配置 */
lisa_audio_gain_t gain_r; /* 右声道增益配置 */
#else
lisa_audio_gain_t gain; /* 增益配置(模拟增益、数字增益) */
#endif
bool enable_hpf; /* 启用高通滤波器 */
bool differential_input; /* 启用差分输入 */
} lisa_audio_record_config_t;
/**
* @brief 播放配置结构
*/
typedef struct {
lisa_audio_format_t format; /* 音频格式(采样率、通道、位深) */
lisa_audio_gain_t gain; /* 增益配置(模拟增益、数字增益) */
uint8_t buffer_count; /* 缓冲区数量 */
uint16_t buffer_samples; /* 每个缓冲区采样点数 */
} lisa_audio_play_config_t;
/**
* @brief IOCTL 命令枚举
*/
typedef enum {
/* Record 命令 (0x00 ~ 0x1F) */
LISA_AUDIO_IOCTL_RECORD_START = 0x00, /* 启动录音 */
LISA_AUDIO_IOCTL_RECORD_STOP = 0x01, /* 停止录音 */
LISA_AUDIO_IOCTL_RECORD_PAUSE = 0x02, /* 暂停录音 */
LISA_AUDIO_IOCTL_RECORD_RESUME = 0x03, /* 恢复录音 */
LISA_AUDIO_IOCTL_RECORD_RESET = 0x04, /* 重置录音 */
LISA_AUDIO_IOCTL_RECORD_SET_GAIN = 0x05, /* 设置录音增益 */
LISA_AUDIO_IOCTL_RECORD_GET_STATUS = 0x06, /* 获取录音状态 */
LISA_AUDIO_IOCTL_RECORD_GET_CONFIG = 0x07, /* 获取录音配置 */
LISA_AUDIO_IOCTL_RECORD_SET_CONFIG = 0x08, /* 设置录音配置 */
/* Play 命令 (0x20 ~ 0x3F) */
LISA_AUDIO_IOCTL_PLAY_START = 0x20, /* 启动播放 */
LISA_AUDIO_IOCTL_PLAY_STOP = 0x21, /* 停止播放 */
LISA_AUDIO_IOCTL_PLAY_SET_GAIN = 0x22, /* 设置播放增益 */
LISA_AUDIO_IOCTL_PLAY_GET_STATUS = 0x23, /* 获取播放状态 */
LISA_AUDIO_IOCTL_PLAY_GET_CONFIG = 0x24, /* 获取播放配置 */
LISA_AUDIO_IOCTL_PLAY_SET_CONFIG = 0x25, /* 设置播放配置 */
LISA_AUDIO_IOCTL_PLAY_GET_BUFFER = 0x26, /* 获取播放缓冲区 */
LISA_AUDIO_IOCTL_PLAY_FLUSH = 0x27, /* 等待播放完成 */
/* 通用命令 (0x40 ~ 0x5F) */
LISA_AUDIO_IOCTL_SET_PHASE_COMPENSATION = 0x40, /* 设置相位补偿 */
LISA_AUDIO_IOCTL_GET_PHASE_COMPENSATION = 0x41, /* 获取相位补偿 */
} lisa_audio_ioctl_cmd_t;
/* ========================================================================
* Audio 设备 API 结构体
* ======================================================================== */
/**
* @brief Audio 设备 API 函数指针结构
*/
typedef struct {
/* 统一回调 */
int (*register_callback)(lisa_device_t *dev, lisa_audio_callback_t callback, void *user_data);
int (*unregister_callback)(lisa_device_t *dev, lisa_audio_callback_t callback);
/* Record 操作 */
int (*record_config)(lisa_device_t *dev, const lisa_audio_record_config_t *config);
int (*record_control)(lisa_device_t *dev, uint32_t cmd, void *arg);
/* Play 操作 */
int (*play_config)(lisa_device_t *dev, const lisa_audio_play_config_t *config);
int (*play_write)(lisa_device_t *dev, const void *buffer, uint32_t samples);
int (*play_get_buffer)(lisa_device_t *dev, void **buffer, uint32_t timeout_ms);
int (*play_control)(lisa_device_t *dev, uint32_t cmd, void *arg);
/* 通用操作 */
int (*ioctl)(lisa_device_t *dev, uint8_t cmd, void *arg);
} lisa_audio_api_t;
/* ========================================================================
* Audio 对外接口函数
* ======================================================================== */
/* ===== 回调管理接口 ===== */
/**
* @brief 注册统一音频数据回调
*
* 注册回调函数以接收音频事件,包括录音数据和播放回声数据。
* 回调函数会在中断上下文中执行。
*
* @param dev Audio设备指针
* @param callback 回调函数指针
* @param user_data 用户自定义数据,将传递给回调函数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 回调函数在中断上下文中执行,应尽量简短快速
*/
static inline int lisa_audio_register_callback(lisa_device_t *dev,
lisa_audio_callback_t callback,
void *user_data)
{
if (!dev || !dev->api || !callback) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->register_callback) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->register_callback(dev, callback, user_data);
}
/**
* @brief 注销统一音频数据回调
*
* @param dev Audio设备指针
* @param callback 要注销的回调函数指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_unregister_callback(lisa_device_t *dev, lisa_audio_callback_t callback)
{
if (!dev || !dev->api || !callback) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->unregister_callback) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->unregister_callback(dev, callback);
}
/* ===== Record 配置接口 ===== */
/**
* @brief 配置录音参数
*
* 配置录音的音频格式、增益、滤波器等参数。
*
* @param dev Audio设备指针
* @param config 录音配置参数结构体指针,包含:
* - format: 音频格式(采样率、通道、位深)
* - gain: 增益配置(模拟增益、数字增益)
* - enable_hpf: 是否启用高通滤波器
* - differential_input: 是否启用差分输入
* - buffer_count: 缓冲区数量
* - buffer_samples: 每个缓冲区采样点数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 配置前请确保设备已初始化
*/
static inline int lisa_audio_record_config(lisa_device_t *dev, const lisa_audio_record_config_t *config)
{
if (!dev || !dev->api || !config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_config) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_config(dev, config);
}
/* ===== Record 控制接口 ===== */
/**
* @brief 启动录音
*
* 启动音频录音功能,开始采集音频数据。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 启动前需先调用 lisa_audio_record_config() 配置录音参数
*/
static inline int lisa_audio_record_start(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_control(dev, LISA_AUDIO_IOCTL_RECORD_START, NULL);
}
/**
* @brief 停止录音
*
* 停止音频录音功能。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_record_stop(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_control(dev, LISA_AUDIO_IOCTL_RECORD_STOP, NULL);
}
/**
* @brief 暂停录音
*
* 暂停音频录音,可通过 lisa_audio_record_resume() 恢复。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_record_pause(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_control(dev, LISA_AUDIO_IOCTL_RECORD_PAUSE, NULL);
}
/**
* @brief 恢复录音
*
* 恢复之前暂停的音频录音。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_record_resume(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_control(dev, LISA_AUDIO_IOCTL_RECORD_RESUME, NULL);
}
/**
* @brief 设置录音增益
*
* 动态调整录音的模拟和数字增益。
*
* @param dev Audio设备指针
* @param gain 增益配置结构体指针包含模拟增益和数字增益单位dB
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_record_set_gain(lisa_device_t *dev, const lisa_audio_gain_t *gain)
{
if (!dev || !dev->api || !gain) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->record_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->record_control(dev, LISA_AUDIO_IOCTL_RECORD_SET_GAIN, (void *)gain);
}
/* ===== Play 配置接口 ===== */
/**
* @brief 配置播放参数
*
* 配置播放的音频格式、增益等参数。
*
* @param dev Audio设备指针
* @param config 播放配置参数结构体指针,包含:
* - format: 音频格式(采样率、通道、位深)
* - gain: 增益配置(模拟增益、数字增益)
* - buffer_count: 缓冲区数量
* - buffer_samples: 每个缓冲区采样点数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 配置前请确保设备已初始化
*/
static inline int lisa_audio_play_config(lisa_device_t *dev, const lisa_audio_play_config_t *config)
{
if (!dev || !dev->api || !config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_config) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_config(dev, config);
}
/* ===== Play 数据传输接口 ===== */
/**
* @brief 写入播放数据
*
* 向播放设备写入音频数据。
*
* @param dev Audio设备指针
* @param buffer 音频数据缓冲区指针
* @param samples 采样点数(所有通道总和)
*
* @return >=0 实际写入的采样点数
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 写入前需先调用 lisa_audio_play_config() 配置播放参数
*/
static inline int lisa_audio_play_write(lisa_device_t *dev, const void *buffer, uint32_t samples)
{
if (!dev || !dev->api || !buffer) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_write) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_write(dev, buffer, samples);
}
/**
* @brief 获取播放空闲缓冲区
*
* 获取一个可用的播放缓冲区指针,用于直接写入音频数据。
*
* @param dev Audio设备指针
* @param buffer 输出参数,用于接收缓冲区指针
* @param timeout_ms 超时时间(毫秒)
*
* @return >=0 缓冲区大小(采样点数)
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return LISA_DEVICE_ERR_TIMEOUT 获取超时
* @return <0 其他错误
*
* @note 获取缓冲区后需要调用 lisa_audio_play_write() 提交数据
*/
static inline int lisa_audio_play_get_buffer(lisa_device_t *dev, void **buffer, uint32_t timeout_ms)
{
if (!dev || !dev->api || !buffer) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_get_buffer) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_get_buffer(dev, buffer, timeout_ms);
}
/* ===== Play 控制接口 ===== */
/**
* @brief 启动播放
*
* 启动音频播放功能,开始输出音频数据。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 启动前需先调用 lisa_audio_play_config() 配置播放参数
*/
static inline int lisa_audio_play_start(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_control(dev, LISA_AUDIO_IOCTL_PLAY_START, NULL);
}
/**
* @brief 停止播放
*
* 停止音频播放功能。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_play_stop(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_control(dev, LISA_AUDIO_IOCTL_PLAY_STOP, NULL);
}
/**
* @brief 等待播放完成
*
* 阻塞等待,直到所有已写入的音频数据播放完毕。
*
* @param dev Audio设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 此函数会阻塞直到播放完成
*/
static inline int lisa_audio_play_flush(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_control(dev, LISA_AUDIO_IOCTL_PLAY_FLUSH, NULL);
}
/**
* @brief 设置播放增益
*
* 动态调整播放的模拟和数字增益。
*
* @param dev Audio设备指针
* @param gain 增益配置结构体指针包含模拟增益和数字增益单位dB
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_play_set_gain(lisa_device_t *dev, const lisa_audio_gain_t *gain)
{
if (!dev || !dev->api || !gain) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->play_control) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->play_control(dev, LISA_AUDIO_IOCTL_PLAY_SET_GAIN, (void *)gain);
}
/* ===== 通用控制接口 ===== */
/**
* @brief 设置相位补偿
*
* 配置录音和播放之间的相位补偿,用于调整数据同步。
*
* @param dev Audio设备指针
* @param compensation 相位补偿配置结构体指针,包含:
* - record_skip_samples: 录音数据丢弃的采样点数
* - echo_skip_samples: ECHO数据丢弃的采样点数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_set_phase_compensation(lisa_device_t *dev,
const lisa_audio_phase_compensation_t *compensation)
{
if (!dev || !dev->api || !compensation) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->ioctl) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->ioctl(dev, LISA_AUDIO_IOCTL_SET_PHASE_COMPENSATION, (void *)compensation);
}
/**
* @brief 获取相位补偿
*
* 读取当前的相位补偿配置。
*
* @param dev Audio设备指针
* @param compensation 输出参数,用于接收相位补偿配置
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_get_phase_compensation(lisa_device_t *dev,
lisa_audio_phase_compensation_t *compensation)
{
if (!dev || !dev->api || !compensation) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->ioctl) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->ioctl(dev, LISA_AUDIO_IOCTL_GET_PHASE_COMPENSATION, (void *)compensation);
}
/**
* @brief 通用 IOCTL 接口
*
* 提供通用的设备控制接口,用于执行各种控制命令。
*
* @param dev Audio设备指针
* @param cmd 控制命令,参见 lisa_audio_ioctl_cmd_t
* @param arg 命令参数,根据具体命令而定
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_audio_ioctl(lisa_device_t *dev, uint8_t cmd, void *arg)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_audio_api_t *api = (lisa_audio_api_t *)dev->api;
if (!api->ioctl) {
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
return api->ioctl(dev, cmd, arg);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,369 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "lisa_audio_arcs"
#include "lisa_log.h"
#include <string.h>
#include "lisa_audio.h"
#include "lisa_audio_internal.h"
#define MAX_AUDIO_OBSERVERS 4
#define WAIT_DATA_TIMEOUT (20)
#define DISPATCH_QUEUE_SIZE (CONFIG_LISA_AUDIO_RECORD_BUFFER_COUNT - 1)
// Observer for the unified callback
typedef struct {
lisa_audio_callback_t callback;
void *user_data;
} audio_observer_t;
typedef struct {
/* Driver sub-modules */
lisa_audio_record_priv_t record;
lisa_audio_play_priv_t play;
/* Unified Dispatcher State */
lisa_mutex_t *mutex;
TaskHandle_t dispatch_task;
/* Queues for pairing record and echo events */
QueueHandle_t record_queue;
QueueHandle_t echo_queue;
/* Phase compensation (samples to skip) */
lisa_audio_phase_compensation_t phase_comp;
/* Unified Callback Observers */
audio_observer_t observers[MAX_AUDIO_OBSERVERS];
uint8_t observer_count;
/* Track which streams are active */
bool record_running;
bool play_running;
uint32_t record_drop_samples;
uint32_t echo_drop_samples;
volatile uint32_t pending_record_drops; // Number of record frames to drop for sync
volatile uint32_t pending_echo_drops; // Number of echo frames to drop for
} lisa_audio_priv_t;
static int audio_record_config(lisa_device_t *dev, const lisa_audio_record_config_t *config)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
return arcs_audio_record_config(&priv->record, config);
}
static int audio_record_control(lisa_device_t *dev, uint32_t cmd, void *arg)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
int ret = arcs_audio_record_control(&priv->record, cmd, arg);
/* Track record running state */
if (ret == LISA_DEVICE_OK) {
lisa_mutex_lock(priv->mutex, -1);
if (cmd == LISA_AUDIO_IOCTL_RECORD_START) {
xQueueReset(priv->record_queue);
priv->record_running = true;
priv->record_drop_samples = 0;
} else if (cmd == LISA_AUDIO_IOCTL_RECORD_STOP) {
priv->record_running = false;
xQueueReset(priv->record_queue);
}
lisa_mutex_unlock(priv->mutex);
}
return ret;
}
static int audio_play_config(lisa_device_t *dev, const lisa_audio_play_config_t *config)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
return arcs_audio_play_config(&priv->play, config);
}
static int audio_play_write(lisa_device_t *dev, const void *buffer, uint32_t samples)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
return arcs_audio_play_write(&priv->play, buffer, samples);
}
static int audio_play_get_buffer(lisa_device_t *dev, void **buffer, uint32_t timeout_ms)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
return arcs_audio_play_get_buffer(&priv->play, buffer, timeout_ms);
}
static int audio_play_control(lisa_device_t *dev, uint32_t cmd, void *arg)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
int ret = arcs_audio_play_control(&priv->play, cmd, arg);
/* Track play running state */
if (ret == LISA_DEVICE_OK) {
lisa_mutex_lock(priv->mutex, -1);
if (cmd == LISA_AUDIO_IOCTL_PLAY_START) {
xQueueReset(priv->echo_queue);
priv->play_running = true;
priv->echo_drop_samples = 0;
} else if (cmd == LISA_AUDIO_IOCTL_PLAY_STOP) {
priv->play_running = false;
xQueueReset(priv->echo_queue);
}
lisa_mutex_unlock(priv->mutex);
}
return ret;
}
static int audio_ioctl(lisa_device_t *dev, uint8_t cmd, void *arg)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
switch (cmd) {
case LISA_AUDIO_IOCTL_SET_PHASE_COMPENSATION:
if (!arg) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_mutex_lock(priv->mutex, -1);
priv->phase_comp = *(lisa_audio_phase_compensation_t *)arg;
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_OK;
case LISA_AUDIO_IOCTL_GET_PHASE_COMPENSATION:
if (!arg) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_mutex_lock(priv->mutex, -1);
*(lisa_audio_phase_compensation_t *)arg = priv->phase_comp;
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_OK;
default:
return LISA_DEVICE_ERR_NOT_SUPPORT;
}
}
static void audio_dispatch_thread(void *arg);
static int audio_register_callback(lisa_device_t *dev, lisa_audio_callback_t callback, void *user_data)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
lisa_mutex_lock(priv->mutex, -1);
if (priv->observer_count >= MAX_AUDIO_OBSERVERS) {
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_ERR_NO_MEM;
}
for (int i = 0; i < priv->observer_count; i++) {
if (priv->observers[i].callback == callback) {
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_OK;
}
}
priv->observers[priv->observer_count].callback = callback;
priv->observers[priv->observer_count].user_data = user_data;
priv->observer_count++;
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_OK;
}
static int audio_unregister_callback(lisa_device_t *dev, lisa_audio_callback_t callback)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)dev->priv_data;
lisa_mutex_lock(priv->mutex, -1);
int pos = -1;
for (int i = 0; i < priv->observer_count; i++) {
if (priv->observers[i].callback == callback) {
pos = i;
break;
}
}
if (pos != -1) {
for (int i = pos; i < priv->observer_count - 1; i++) {
priv->observers[i] = priv->observers[i + 1];
}
priv->observer_count--;
}
lisa_mutex_unlock(priv->mutex);
return LISA_DEVICE_OK;
}
static const lisa_audio_api_t audio_api = {
.register_callback = audio_register_callback,
.unregister_callback = audio_unregister_callback,
.record_config = audio_record_config,
.record_control = audio_record_control,
.play_config = audio_play_config,
.play_write = audio_play_write,
.play_get_buffer = audio_play_get_buffer,
.play_control = audio_play_control,
.ioctl = audio_ioctl,
};
static lisa_audio_priv_t audio_priv = { 0 };
int audio_submit_event_from_isr(internal_audio_event_t *event)
{
int ret = 0;
if (!event) return LISA_DEVICE_ERR_INVALID;
BaseType_t yield = pdFALSE;
/* Select target queue and skip counter based on event type */
if (event->type == AUDIO_EVENT_TYPE_RECORD) {
if(audio_priv.pending_record_drops > 0){
audio_priv.pending_record_drops--;
return 0;
}
if(audio_priv.phase_comp.record_skip_samples > audio_priv.record_drop_samples){
audio_priv.record_drop_samples += event->samples;
return 0;
}
/* Send to target queue */
if (xQueueSendFromISR(audio_priv.record_queue, event, &yield) != pdPASS) {
/* Queue full - frame dropped (logged in dispatch thread) */
audio_priv.pending_echo_drops++;
LOGE("Record queue full, frame dropped");
ret = LISA_DEVICE_ERR_NO_MEM;
}
} else if (event->type == AUDIO_EVENT_TYPE_ECHO) {
if(audio_priv.pending_echo_drops > 0){
audio_priv.pending_echo_drops--;
return 0;
}
if(audio_priv.phase_comp.echo_skip_samples > audio_priv.echo_drop_samples){
audio_priv.echo_drop_samples += event->samples;
return 0;
}
/* Send to target queue */
if (xQueueSendFromISR(audio_priv.echo_queue, event, &yield) != pdPASS) {
/* Queue full - frame dropped (logged in dispatch thread) */
audio_priv.pending_record_drops++;
LOGE("Echo queue full, frame dropped");
ret = LISA_DEVICE_ERR_NO_MEM;
}
}
portYIELD_FROM_ISR(yield);
return ret;
}
static int audio_init(void)
{
int ret;
memset(&audio_priv, 0, sizeof(audio_priv));
audio_priv.mutex = lisa_mutex_create();
if (!audio_priv.mutex) {
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* Create queues for record and echo events */
audio_priv.record_queue = xQueueCreate(DISPATCH_QUEUE_SIZE, sizeof(internal_audio_event_t));
audio_priv.echo_queue = xQueueCreate(DISPATCH_QUEUE_SIZE, sizeof(internal_audio_event_t));
if (!audio_priv.record_queue || !audio_priv.echo_queue) {
if (audio_priv.record_queue) vQueueDelete(audio_priv.record_queue);
if (audio_priv.echo_queue) vQueueDelete(audio_priv.echo_queue);
lisa_mutex_delete(audio_priv.mutex);
return LISA_DEVICE_ERR_NO_MEM;
}
if (xTaskCreate(audio_dispatch_thread, "audio_dispatch",
CONFIG_LISA_AUDIO_DISPATCH_TASK_STACK_SIZE / sizeof(StackType_t),
&audio_priv,
configMAX_PRIORITIES - CONFIG_LISA_AUDIO_DISPATCH_TASK_PRIORITY - 1,
&audio_priv.dispatch_task) != pdPASS) {
vQueueDelete(audio_priv.record_queue);
vQueueDelete(audio_priv.echo_queue);
lisa_mutex_delete(audio_priv.mutex);
return LISA_DEVICE_ERR_INIT_FAIL;
}
ret = arcs_audio_record_init(&audio_priv.record);
if (ret != LISA_DEVICE_OK) {
return ret;
}
ret = arcs_audio_play_init(&audio_priv.play);
if (ret != LISA_DEVICE_OK) {
return ret;
}
return LISA_DEVICE_OK;
}
static void audio_dispatch_thread(void *arg)
{
lisa_audio_priv_t *priv = (lisa_audio_priv_t *)arg;
bool has_data;
while (1) {
internal_audio_event_t rec_evt = {0}, echo_evt = {0};
has_data = false;
if ((priv->record_running) || (priv->play_running)) {
if (priv->record_running) {
if(pdPASS != xQueueReceive(priv->record_queue, &rec_evt, pdMS_TO_TICKS(WAIT_DATA_TIMEOUT))){
continue;
}
}
if (priv->play_running) {
if(pdPASS != xQueueReceive(priv->echo_queue, &echo_evt, pdMS_TO_TICKS(WAIT_DATA_TIMEOUT))){
continue;
}
}
lisa_audio_event_t out_event = {
.record_buffer = rec_evt.buffer,
.record_samples = rec_evt.samples,
.echo_buffer = echo_evt.buffer,
.echo_samples = echo_evt.samples,
};
/* Dispatch the event (paired or single) */
for (int i = 0; i < priv->observer_count; i++) {
priv->observers[i].callback(&out_event, priv->observers[i].user_data);
}
}
/*waiting for running*/
else {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
}
LISA_DEVICE_REGISTER(audio0,
&audio_api,
&audio_priv,
NULL,
audio_init,
CONFIG_LISA_AUDIO_INIT_PRIORITY);

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "audio_adc_init.h"
#include "arcs_ap.h"
/**
* @brief ADC GPIO 引脚配置
*
* 配置 MIC 引脚为差分输入模式
*/
int audio_adc_gpio_init(void)
{
/* MIC0 使用 GPIOA_30 (INP) + GPIOA_31 (INN) - 差分输入 */
/* MIC1 使用 GPIOA_28 (INP) + GPIOA_29 (INN) - 差分输入 */
/* GPIOA_28 配置 - MIC1_INP */
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_OEN_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_OEN_REG = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_IE_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_IE_REG = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_PULL_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_PULL_UP = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_PULL_DN = 0x0;
/* GPIOA_29 配置 - MIC1_INN */
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_OEN_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_OEN_REG = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_IE_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_IE_REG = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_PULL_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_PULL_UP = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_PULL_DN = 0x0;
/* GPIOA_30 配置 - MIC0_INP */
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_OEN_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_OEN_REG = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_IE_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_IE_REG = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_PULL_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_PULL_UP = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_PULL_DN = 0x0;
/* GPIOA_31 配置 - MIC0_INN */
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_OEN_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_OEN_REG = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_IE_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_IE_REG = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_PULL_FRC = 0x1;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_PULL_UP = 0x0;
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_PULL_DN = 0x0;
/* 功能选择 - MIC 功能 (FSEL=21) */
IP_CMN_IOMUX->REG_PAD_GPIOA_30.bit.PAD_GPIOA_30_FSEL = 21; /* MIC0_INP */
IP_CMN_IOMUX->REG_PAD_GPIOA_31.bit.PAD_GPIOA_31_FSEL = 21; /* MIC0_INN */
IP_CMN_IOMUX->REG_PAD_GPIOA_28.bit.PAD_GPIOA_28_FSEL = 21; /* MIC1_INP */
IP_CMN_IOMUX->REG_PAD_GPIOA_29.bit.PAD_GPIOA_29_FSEL = 21; /* MIC1_INN */
return 0;
}
/**
* @brief ADC 平台初始化
*/
int audio_adc_platform_init(void)
{
/* 配置 GPIO 引脚 */
audio_adc_gpio_init();
/* ADC/PDM 时钟和电源在 HAL 层初始化时自动配置 */
/* 配置 LDOVA 电压等级(根据 Kconfig 配置) */
#if defined(CONFIG_LISA_AUDIO_RECORD_LDOVA_1V68)
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0x8;
#elif defined(CONFIG_LISA_AUDIO_RECORD_LDOVA_1V785)
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0xf;
#elif defined(CONFIG_LISA_AUDIO_RECORD_LDOVA_1V8)
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0x0;
#elif defined(CONFIG_LISA_AUDIO_RECORD_LDOVA_1V815)
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0x1;
#elif defined(CONFIG_LISA_AUDIO_RECORD_LDOVA_1V905)
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0x7;
#else
/* 默认使用 1.8V (0x0) */
IP_AON_CTRL->REG_AON_TUNE1.bit.TUNE_LDOVA = 0x7;
#endif
return 0;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief ADC 平台初始化
*
* 配置 GPIO/IOMUX 引脚,初始化时钟和电源
*
* @return 0 成功, 负数失败
*/
int audio_adc_platform_init(void);
/**
* @brief ADC GPIO 引脚配置
*
* 配置 MIC 引脚为差分输入模式
* GPIOA_28: MIC1_INP
* GPIOA_29: MIC1_INN
* GPIOA_30: MIC0_INP
* GPIOA_31: MIC0_INN
*
* @return 0 成功, 负数失败
*/
int audio_adc_gpio_init(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "audio_dac_init.h"
/**
* @brief DAC 平台初始化
*
* DAC 时钟和电源在 HAL 层初始化时自动配置
* PA 功放控制已禁用,不需要 GPIO 配置
*/
int audio_dac_platform_init(void)
{
/* DAC 时钟和电源在 HAL 层自动配置 */
/* 不包含 PA 控制,无需 GPIO 配置 */
return 0;
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief DAC 平台初始化
*
* 初始化 DAC 时钟和电源
*
* @return 0 成功, 负数失败
*/
int audio_dac_platform_init(void);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "lisa_audio.h"
#include "lisa_mutex.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "event_groups.h"
/* ===== Internal Dispatcher Structures ===== */
typedef enum {
AUDIO_EVENT_TYPE_RECORD,
AUDIO_EVENT_TYPE_ECHO,
} audio_event_type_t;
// Event from ISR to dispatcher
typedef struct {
audio_event_type_t type;
void *buffer;
uint32_t samples;
uint64_t timestamp;
} internal_audio_event_t;
/* ===== Record Internal Structures ===== */
typedef struct {
lisa_mutex_t *mutex;
void *hdrv; /* ADC_PDM 驱动句柄 */
lisa_audio_record_config_t config;
lisa_audio_status_t status;
/* 缓冲区管理 */
void *queue; /* x_queue_t */
void **buffers; /* 缓冲区指针数组 */
uint8_t buffer_count;
uint16_t buffer_samples;
uint32_t buffer_size;
int current_index;
bool is_running;
bool initialized;
bool adc_initialized;
} lisa_audio_record_priv_t;
/* ===== Play Internal Structures ===== */
typedef struct {
void *addr;
uint32_t size;
} play_item_t;
typedef enum {
PLAY_STATE_IDLE = 0,
PLAY_STATE_PLAY_REQ,
PLAY_STATE_PLAY_RUN,
PLAY_STATE_STOP_REQ
} play_state_t;
typedef struct {
lisa_mutex_t *mutex;
void *hdrv; /* DAC 驱动句柄 */
lisa_audio_play_config_t config;
lisa_audio_status_t status;
/* Ping-Pong 缓冲 */
void *ping_addr;
void *pong_addr;
/* 缓冲池 */
QueueHandle_t play_queue;
QueueHandle_t free_queue;
uint8_t *buffer_pool;
uint8_t buffer_count;
uint16_t buffer_samples;
uint32_t buffer_size;
/* 状态机 */
play_state_t state;
EventGroupHandle_t event;
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
/* Echo feature fields */
void **echo_fifo;
int echo_xpos;
uint32_t echo_buffer_size;
uint16_t echo_buffer_samples;
uint8_t echo_buffer_count;
#endif
bool initialized;
} lisa_audio_play_priv_t;
/* ===== Internal Function Prototypes ===== */
/* Record Functions */
int arcs_audio_record_init(lisa_audio_record_priv_t *priv);
int arcs_audio_record_deinit(lisa_audio_record_priv_t *priv);
int arcs_audio_record_config(lisa_audio_record_priv_t *priv, const lisa_audio_record_config_t *config);
int arcs_audio_record_control(lisa_audio_record_priv_t *priv, uint32_t cmd, void *arg);
/* Play Functions */
int arcs_audio_play_init(lisa_audio_play_priv_t *priv);
int arcs_audio_play_deinit(lisa_audio_play_priv_t *priv);
int arcs_audio_play_config(lisa_audio_play_priv_t *priv, const lisa_audio_play_config_t *config);
int arcs_audio_play_write(lisa_audio_play_priv_t *priv, const void *buffer, uint32_t samples);
int arcs_audio_play_get_buffer(lisa_audio_play_priv_t *priv, void **buffer, uint32_t timeout_ms);
int arcs_audio_play_control(lisa_audio_play_priv_t *priv, uint32_t cmd, void *arg);

View File

@@ -0,0 +1,745 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "lisa_audio_play"
#include <string.h>
#include "lisa_audio_internal.h"
#include "audio_dac_init.h"
#include "lisa_log.h"
#include "Driver_DAC.h"
#include "Driver_Common.h"
#include "dma.h"
#include "cache.h"
#include "Driver_GPIO.h"
#include "IOMuxManager.h"
#include "systick.h"
#include "lisa_mem.h"
/* Must be defined in lisa_audio_arcs.c */
extern int audio_submit_event_from_isr(internal_audio_event_t *event);
/* DMA 通道定义 */
#define GPDMA_DAC0_CHN (2)
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
#define GPDMA_ECHO_CHN (3)
#define ECHO_BUFFER_COUNT CONFIG_LISA_AUDIO_RECORD_BUFFER_COUNT
#define ECHO_BUFFER_SAMPLES CONFIG_LISA_AUDIO_RECORD_BUFFER_SAMPLES
#endif
/* PA 控制配置 */
#ifdef CONFIG_LISA_AUDIO_PLAY_PA_ENABLE
#if CONFIG_LISA_AUDIO_PLAY_PA_PAD == 0
#define PA_GPIO_PAD CSK_IOMUX_PAD_A
#define PA_GPIO_DEV GPIOA()
#else
#define PA_GPIO_PAD CSK_IOMUX_PAD_B
#define PA_GPIO_DEV GPIOB()
#endif
#define PA_PIN_NUM CONFIG_LISA_AUDIO_PLAY_PA_PIN
#define PA_GPIO_PIN (1 << PA_PIN_NUM)
#define PA_PULSE_COUNT CONFIG_LISA_AUDIO_PLAY_PA_PULSE_COUNT
#define PA_PULSE_US CONFIG_LISA_AUDIO_PLAY_PA_PULSE_US
#endif
/* Play 事件标志 */
#define PLAY_EVT_DONE (1 << 0)
/* 音频格式常量 */
#define STEREO_CHANNELS (2)
#define MONO_CHANNELS (1)
#define BYTES_PER_SAMPLE_16 (2)
/* 前向声明 */
static void play_event_callback(uint32_t event, uint32_t user);
static inline uint32_t channel_to_bitmap(lisa_audio_channel_t channels)
{
return (uint32_t)channels;
}
/* DAC 采样率转换(支持 8K/16K/24K/32K/48K/96K */
static uint32_t play_sample_rate_to_ctrl(lisa_audio_rate_t rate)
{
switch (rate) {
case LISA_AUDIO_RATE_8K: return CSK_DAC_SR_8KHZ;
case LISA_AUDIO_RATE_16K: return CSK_DAC_SR_16KHZ;
case LISA_AUDIO_RATE_24K: return CSK_DAC_SR_24KHZ;
case LISA_AUDIO_RATE_32K: return CSK_DAC_SR_32KHZ;
case LISA_AUDIO_RATE_48K: return CSK_DAC_SR_48KHZ;
case LISA_AUDIO_RATE_96K: return CSK_DAC_SR_96KHZ;
default: return CSK_DAC_SR_16KHZ;
}
}
static uint32_t play_get_osr_for_rate(lisa_audio_rate_t rate)
{
/* ≤24kHz 使用 OSR_250, >24kHz 使用 OSR_125 */
if (rate <= LISA_AUDIO_RATE_24K) {
return CSK_DAC_OSR_250;
} else {
return CSK_DAC_OSR_125;
}
}
/* ===== PA 控制 ===== */
#ifdef CONFIG_LISA_AUDIO_PLAY_PA_ENABLE
static void play_pa_control(bool enable)
{
static bool pa_initialized = false;
/* 初始化GPIO(仅一次) */
if (!pa_initialized) {
IOMuxManager_PinConfigure(PA_GPIO_PAD, PA_PIN_NUM, CSK_IOMUX_FUNC_DEFAULT);
GPIO_Initialize(PA_GPIO_DEV, NULL, NULL);
GPIO_Control(PA_GPIO_DEV, CSK_GPIO_DEBOUNCE_DISABLE, PA_GPIO_PIN);
GPIO_SetDir(PA_GPIO_DEV, PA_GPIO_PIN, CSK_GPIO_DIR_OUTPUT);
pa_initialized = true;
}
if (enable) {
/* PA使能:发送脉冲序列(如果配置) */
if (PA_PULSE_COUNT > 0) {
for (volatile int i = 0; i < PA_PULSE_COUNT; i++) {
GPIO_PinWrite(PA_GPIO_DEV, PA_GPIO_PIN, 0);
SysTick_Delay_Us(PA_PULSE_US);
GPIO_PinWrite(PA_GPIO_DEV, PA_GPIO_PIN, 1);
SysTick_Delay_Us(PA_PULSE_US);
}
} else {
/* 无脉冲模式:直接拉高 */
GPIO_PinWrite(PA_GPIO_DEV, PA_GPIO_PIN, 1);
}
} else {
/* PA关闭:拉低并延迟 */
GPIO_PinWrite(PA_GPIO_DEV, PA_GPIO_PIN, 0);
SysTick_Delay_Us(PA_PULSE_US);
}
}
#endif
/* ===== Play 事件回调辅助函数 ===== */
static inline bool get_next_play_buffer(lisa_audio_play_priv_t *priv,
play_item_t *item,
BaseType_t *yield)
{
if (xQueueReceiveFromISR(priv->play_queue, item, yield) == pdPASS) {
return true;
}
/* 队列空,使用空闲 buffer 填充静音 */
if (xQueueReceiveFromISR(priv->free_queue, item, yield) == pdPASS) {
memset(item->addr, 0, priv->buffer_size);
return true;
}
/* 极端情况:无可用 buffer (buffer_count 配置过小) */
item->addr = NULL;
return false;
}
static inline void submit_buffer_to_dac(lisa_audio_play_priv_t *priv, const play_item_t *item)
{
HAL_FlushDCache_by_Addr(item->addr, priv->buffer_size);
PIPO_OUT_BLOCK block = {
.sample_data = item->addr,
.sample_cnt = priv->buffer_samples,
.flags = 0
};
uint8_t block_count = 1;
uint32_t channel_bitmap = channel_to_bitmap(priv->config.format.channels);
DAC_Send_PiPo(priv->hdrv, &block, &block_count, channel_bitmap, DAC_TX_FLAG_START_NOW);
}
static inline void recycle_completed_buffer(lisa_audio_play_priv_t *priv,
void *completed_addr,
BaseType_t *yield)
{
if (completed_addr) {
play_item_t recycled = {.addr = completed_addr, .size = 0};
xQueueSendToBackFromISR(priv->free_queue, &recycled, yield);
}
}
/* ===== Play 事件回调 ===== */
static void play_event_callback(uint32_t event, uint32_t user)
{
lisa_audio_play_priv_t *priv = (lisa_audio_play_priv_t *)user;
BaseType_t yield = pdFALSE;
if (event & CSK_DAC_EVENT_SEND_COMPLETE) {
priv->state = PLAY_STATE_IDLE;
xEventGroupSetBitsFromISR(priv->event, PLAY_EVT_DONE, &yield);
}
/* 处理 Ping-Pong buffer 切换事件 */
if (event & (CSK_DAC_EVENT_BLOCK_COMPLETE | CSK_DAC_EVENT_SEND_COMPLETE)) {
play_item_t next_item = {.addr = NULL, .size = 0};
void *completed_addr = NULL;
if (priv->state == PLAY_STATE_PLAY_REQ) {
#ifdef CONFIG_LISA_AUDIO_PLAY_PA_ENABLE
/* 第一个静音块完成DAC 输出稳定,启用 PA */
play_pa_control(true);
#endif
priv->state = PLAY_STATE_PLAY_RUN;
}
if (priv->state == PLAY_STATE_PLAY_RUN) {
if (get_next_play_buffer(priv, &next_item, &yield)) {
if (next_item.addr) {
submit_buffer_to_dac(priv, &next_item);
}
}
}
if (event & CSK_DAC_EVENT_TX_PING_DONE) {
completed_addr = priv->ping_addr;
priv->ping_addr = next_item.addr;
} else if (event & CSK_DAC_EVENT_TX_PONG_DONE) {
completed_addr = priv->pong_addr;
priv->pong_addr = next_item.addr;
}
recycle_completed_buffer(priv, completed_addr, &yield);
}
if (event & CSK_DAC_EVENT_TX_FIFO_EMPTY) {
LOGW("FIFO empty");
}
if (event & CSK_DAC_EVENT_TX_FIFO_UNDERRUN) {
LOGE("FIFO underrun");
}
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
if (event & (CSK_DAC_EVENT_ECHO_RX_COMPLETE | CSK_DAC_EVENT_ECHO_BLOCK_COMPLETE)) {
int ret = CSK_DRIVER_OK;
void *recv = priv->echo_fifo[priv->echo_xpos];
dcache_invalidate_range((uint32_t)recv, (uint32_t)recv + priv->echo_buffer_size);
/* 先尝试提交事件,只有成功时才更新索引,避免数据错乱 */
internal_audio_event_t new_event = {
.type = AUDIO_EVENT_TYPE_ECHO,
.buffer = recv,
.samples = priv->echo_buffer_samples,
.timestamp = SysTimeMsGet() * 1000000ULL,
};
int submit_ret = audio_submit_event_from_isr(&new_event);
/* 只有在事件成功提交后才更新 echo_xpos */
if (submit_ret == 0) {
if (++priv->echo_xpos >= priv->echo_buffer_count) {
priv->echo_xpos = 0;
}
} else {
/* 事件提交失败,保持 echo_xpos 不变,下次继续使用同一个 buffer */
LOGW("Echo event submit failed, reusing buffer %d", priv->echo_xpos);
}
/* 计算下一个用于 DMA 的 buffer 索引 */
int ipos = priv->echo_xpos;
if (++ipos >= priv->echo_buffer_count) {
ipos = 0;
}
ret = DAC_Echo_Receive_PiPo(priv->hdrv,
&(PIPO_IN_BLOCK){ .sample_data = priv->echo_fifo[ipos], .sample_cnt = priv->echo_buffer_samples, .flags = 0 },
&(uint8_t){1},
channel_to_bitmap(LISA_AUDIO_CH_LEFT));
if (CSK_DRIVER_OK != ret){
LOGE("DAC_Echo_Receive_PiPo:%d", ret);
}
}
#endif
portYIELD_FROM_ISR(yield);
}
int arcs_audio_play_config(lisa_audio_play_priv_t *priv, const lisa_audio_play_config_t *config)
{
int ret = 0;
if (!config) {
return LISA_DEVICE_ERR_INVALID;
}
memcpy(&priv->config, config, sizeof(lisa_audio_play_config_t));
#ifdef CONFIG_LISA_AUDIO_PLAY_PA_ENABLE
/* 确保PA处于关闭状态 */
play_pa_control(false);
#endif
/* 注意: 即使不启用Echo,也必须设置echo通道,否则会导致杂音 */
DAC_DMA_CHS dmach = {
.dma_ch_out_left = GPDMA_DAC0_CHN,
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
.dma_ch_echo_left = GPDMA_ECHO_CHN,
#endif
};
DAC_Uninitialize(priv->hdrv);
/* 强制使用单声道配置底层硬件 */
uint32_t hw_channels = LISA_AUDIO_CH_LEFT;
uint32_t flags = (channel_to_bitmap(hw_channels) << DAC_BMP_FLAG_OUT_POS) |
DAC_BMP_FLAG_USE_16BITS;
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
flags |= (channel_to_bitmap(hw_channels) << DAC_BMP_FLAG_ECHO_POS);
#endif
ret = DAC_Initialize(priv->hdrv,
play_event_callback,
(uint32_t)priv,
flags,
&dmach);
if (ret != 0) {
LOGE("DAC_Initialize failed: %d", ret);
goto exit;
}
ret = DAC_PowerControl(priv->hdrv, CSK_POWER_FULL);
if (ret != 0) {
LOGE("DAC_PowerControl failed: %d", ret);
goto exit;
}
uint32_t sr_ctrl = play_sample_rate_to_ctrl(config->format.sample_rate);
uint32_t osr_ctrl = play_get_osr_for_rate(config->format.sample_rate);
ret = DAC_Control(priv->hdrv,
sr_ctrl | osr_ctrl | CSK_DAC_SOFT_MUTE_SET,
CSK_DAC_ARG_SOFT_MUTE_EN | CSK_DAC_ARG_SOFT_MUTE_SPD(3));
if (ret != 0) {
LOGE("DAC_Control failed: %d", ret);
goto exit;
}
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
ECHO_PARAMS echo_params = { 0 };
echo_params.echo_mixed = 0; // 1; // only 1 ECHO channel for only 1 DAC channel
echo_params.samp_rate = config->format.sample_rate;
echo_params.trim_16bits = 1; // 16bits echo?
ret = DAC_Control(priv->hdrv, CSK_DAC_SET_ECHO_PARAMS, (uint32_t)&echo_params);
if (CSK_DRIVER_OK != ret){
LOGE("DAC_Control for ECHO failed: %d", ret);
goto exit;
}
#endif
uint32_t dev_bitmap = channel_to_bitmap(hw_channels);
ret = DAC_SetMute(priv->hdrv, dev_bitmap, dev_bitmap);
if (ret != 0) {
LOGE("DAC_SetMute failed: %d", ret);
goto exit;
}
uint32_t gain_a = DAC_GAIN_A_VAL(config->gain.analog_gain);
uint32_t gain_d = DAC_GAIN_D_VAL(config->gain.digital_gain);
uint32_t vol_flag = 0;
if (hw_channels & LISA_AUDIO_CH_LEFT) {
vol_flag |= DAC_VOL_FLAG_A_LEFT | DAC_VOL_FLAG_D_LEFT;
}
/* 如果硬件只有左声道,忽略右声道增益配置 */
ret = DAC_SetVolume(priv->hdrv, gain_a, gain_d, vol_flag);
if (ret != 0) {
LOGE("DAC_SetVolume failed: %d", ret);
goto exit;
}
/* 计算缓冲区参数 (强制单声道输出) */
priv->buffer_count = config->buffer_count;
priv->buffer_samples = config->buffer_samples * MONO_CHANNELS;
priv->buffer_size = priv->buffer_samples * (uint8_t)config->format.sample_bits;
if (priv->play_queue) vQueueDelete(priv->play_queue);
if (priv->free_queue) vQueueDelete(priv->free_queue);
if (priv->event) vEventGroupDelete(priv->event);
if (priv->buffer_pool) lisa_mem_free(priv->buffer_pool);
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
if (priv->echo_fifo) {
if (priv->echo_fifo[0]) {
lisa_mem_free(priv->echo_fifo[0]);
}
lisa_mem_free(priv->echo_fifo);
}
#endif
priv->play_queue = NULL;
priv->free_queue = NULL;
priv->event = NULL;
priv->buffer_pool = NULL;
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
priv->echo_fifo = NULL;
#endif
const uint32_t pool_alignment = 32;
const uint32_t pool_size = priv->buffer_count * priv->buffer_size;
priv->buffer_pool = lisa_mem_align_alloc(pool_alignment, pool_size);
if (!priv->buffer_pool) {
LOGE("Failed to allocate buffer pool: %u bytes", pool_size);
ret = LISA_DEVICE_ERR_NO_MEM;
goto exit;
}
priv->play_queue = xQueueCreate(priv->buffer_count, sizeof(play_item_t));
priv->free_queue = xQueueCreate(priv->buffer_count, sizeof(play_item_t));
priv->event = xEventGroupCreate();
if (!priv->play_queue || !priv->free_queue || !priv->event) {
LOGE("Failed to create queues/events");
ret = LISA_DEVICE_ERR_NO_MEM;
goto exit;
}
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
priv->echo_buffer_count = ECHO_BUFFER_COUNT;
priv->echo_buffer_samples = ECHO_BUFFER_SAMPLES;
priv->echo_buffer_size = priv->echo_buffer_samples * BYTES_PER_SAMPLE_16;
const uint32_t echo_pool_size = priv->echo_buffer_count * priv->echo_buffer_size;
void *echo_buffer_pool = lisa_mem_align_alloc(pool_alignment, echo_pool_size);
if (!echo_buffer_pool) {
LOGE("Failed to allocate echo buffer pool: %u bytes", echo_pool_size);
ret = LISA_DEVICE_ERR_NO_MEM;
goto exit;
}
priv->echo_fifo = lisa_mem_alloc(sizeof(void *) * priv->echo_buffer_count);
if (!priv->echo_fifo) {
lisa_mem_free(echo_buffer_pool);
ret = LISA_DEVICE_ERR_NO_MEM;
goto exit;
}
priv->echo_fifo[0] = echo_buffer_pool;
for (int i = 1; i < priv->echo_buffer_count; i++) {
priv->echo_fifo[i] = (uint8_t *)echo_buffer_pool + i * priv->echo_buffer_size;
}
priv->echo_xpos = 0;
#endif
for (int i = 0; i < priv->buffer_count; i++) {
play_item_t item = {
.addr = priv->buffer_pool + i * priv->buffer_size,
.size = 0
};
xQueueSendToBack(priv->free_queue, &item, 0);
}
priv->ping_addr = NULL;
priv->pong_addr = NULL;
priv->state = PLAY_STATE_IDLE;
priv->status = LISA_AUDIO_STATUS_IDLE;
LOGI("Play configured: rate=%d, gain=%d/%d dB, buffers=%d×%d",
config->format.sample_rate,
config->gain.analog_gain,
config->gain.digital_gain,
config->buffer_count,
config->buffer_samples);
exit:
return ret;
}
static void audio_downmix_stereo_to_mono_16bit(const int16_t *src, int16_t *dst, uint32_t frames)
{
for (uint32_t i = 0; i < frames; i++) {
int32_t left = src[STEREO_CHANNELS * i];
int32_t right = src[STEREO_CHANNELS * i + 1];
dst[i] = (int16_t)((left + right) / 2);
}
}
static inline void pad_buffer_with_silence(void *buffer, uint32_t used_size, uint32_t total_size)
{
if (used_size < total_size) {
memset((uint8_t *)buffer + used_size, 0, total_size - used_size);
}
}
static inline uint32_t calc_stereo_process_samples(uint32_t max_mono_samples, uint32_t samples_left)
{
uint32_t max_stereo_samples = max_mono_samples * STEREO_CHANNELS;
uint32_t samples = (samples_left > max_stereo_samples) ? max_stereo_samples : samples_left;
/* 确保采样数为偶数 (左右声道配对) */
return samples & ~1U;
}
static uint32_t process_stereo_data(const int16_t *input,
void *output_buffer,
uint32_t samples_to_process,
uint32_t buffer_size)
{
uint32_t frames = samples_to_process / STEREO_CHANNELS;
audio_downmix_stereo_to_mono_16bit(input, (int16_t *)output_buffer, frames);
uint32_t used_size = frames * BYTES_PER_SAMPLE_16;
pad_buffer_with_silence(output_buffer, used_size, buffer_size);
return used_size;
}
static uint32_t process_mono_data(const void *input,
void *output_buffer,
uint32_t samples_to_process,
uint32_t bytes_per_sample,
uint32_t buffer_size)
{
uint32_t copy_size = samples_to_process * bytes_per_sample;
memcpy(output_buffer, input, copy_size);
pad_buffer_with_silence(output_buffer, copy_size, buffer_size);
return copy_size;
}
int arcs_audio_play_write(lisa_audio_play_priv_t *priv, const void *buffer, uint32_t samples)
{
if (!buffer || samples == 0) {
return LISA_DEVICE_ERR_INVALID;
}
const TickType_t wait_timeout = portMAX_DELAY;
const bool is_stereo_input = (priv->config.format.channels == LISA_AUDIO_CH_STEREO);
const uint32_t input_bytes_per_sample = (uint8_t)priv->config.format.sample_bits;
const uint32_t max_mono_samples = priv->buffer_size / BYTES_PER_SAMPLE_16;
const uint8_t *input_ptr = (const uint8_t *)buffer;
uint32_t remaining_samples = samples;
uint32_t total_written = 0;
while (remaining_samples > 0) {
play_item_t item;
if (xQueueReceive(priv->free_queue, &item, wait_timeout) != pdPASS) {
LOGW("Wait free buffer timeout");
break;
}
uint32_t samples_to_process;
if (is_stereo_input) {
samples_to_process = calc_stereo_process_samples(max_mono_samples, remaining_samples);
} else {
samples_to_process = (remaining_samples > max_mono_samples) ? max_mono_samples : remaining_samples;
}
if (samples_to_process == 0) {
xQueueSendToBack(priv->free_queue, &item, 0);
break;
}
if (is_stereo_input) {
item.size = process_stereo_data((const int16_t *)input_ptr,
item.addr,
samples_to_process,
priv->buffer_size);
} else {
item.size = process_mono_data(input_ptr,
item.addr,
samples_to_process,
input_bytes_per_sample,
priv->buffer_size);
}
HAL_FlushDCache_by_Addr(item.addr, priv->buffer_size);
if (xQueueSendToBack(priv->play_queue, &item, wait_timeout) != pdPASS) {
xQueueSendToBack(priv->free_queue, &item, 0);
LOGE("Failed to enqueue buffer");
break;
}
uint32_t consumed_bytes = samples_to_process * input_bytes_per_sample;
input_ptr += consumed_bytes;
remaining_samples -= samples_to_process;
total_written += samples_to_process;
}
return total_written;
}
int arcs_audio_play_get_buffer(lisa_audio_play_priv_t *priv, void **buffer, uint32_t timeout_ms)
{
if (!buffer) {
return LISA_DEVICE_ERR_INVALID;
}
play_item_t item;
TickType_t ticks = (timeout_ms == 0xFFFFFFFF) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
if (xQueueReceive(priv->free_queue, &item, ticks) == pdPASS) {
*buffer = item.addr;
return priv->buffer_samples;
}
*buffer = NULL;
return 0;
}
int arcs_audio_play_control(lisa_audio_play_priv_t *priv, uint32_t cmd, void *arg)
{
int ret = 0;
switch (cmd) {
case LISA_AUDIO_IOCTL_PLAY_START:
if (priv->state == PLAY_STATE_IDLE) {
priv->state = PLAY_STATE_PLAY_REQ;
uint32_t dev_bitmap = channel_to_bitmap(LISA_AUDIO_CH_LEFT);
play_item_t item1 = {0}, item2 = {0};
if (xQueueReceive(priv->play_queue, &item1, 0) != pdPASS) {
if (xQueueReceive(priv->free_queue, &item1, 0) == pdPASS) {
memset(item1.addr, 0, priv->buffer_size);
}
}
if (item1.addr) {
if (xQueueReceive(priv->play_queue, &item2, 0) != pdPASS) {
if (xQueueReceive(priv->free_queue, &item2, 0) == pdPASS) {
memset(item2.addr, 0, priv->buffer_size);
}
}
}
if (item1.addr && item2.addr) {
HAL_FlushDCache_by_Addr(item1.addr, priv->buffer_size);
HAL_FlushDCache_by_Addr(item2.addr, priv->buffer_size);
PIPO_OUT_BLOCK pipo[] = {
[0] = { .sample_data = item1.addr, .sample_cnt = priv->buffer_samples, .flags = 0 },
[1] = { .sample_data = item2.addr, .sample_cnt = priv->buffer_samples, .flags = 0 },
};
priv->ping_addr = item1.addr;
priv->pong_addr = item2.addr;
ret = DAC_Send_PiPo(priv->hdrv, pipo, &(uint8_t){2}, dev_bitmap, DAC_TX_FLAG_START_NOW);
if (ret == 0) {
#ifdef CONFIG_LISA_AUDIO_PLAY_ECHO_ENABLE
priv->echo_xpos = 0;
ret = DAC_Echo_Receive_PiPo(priv->hdrv, (PIPO_IN_BLOCK[]){
{ .sample_data = priv->echo_fifo[0], .sample_cnt = priv->echo_buffer_samples, .flags = 0 },
{ .sample_data = priv->echo_fifo[1], .sample_cnt = priv->echo_buffer_samples, .flags = 0 },
}, &(uint8_t){2}, dev_bitmap);
if (ret != 0) {
LOGE("DAC_Echo_Receive_PiPo failed: %d", ret);
}
#endif
DAC_SetMute(priv->hdrv, 0, dev_bitmap);
priv->status = LISA_AUDIO_STATUS_RUNNING;
LOGI("Play started");
} else {
LOGE("DAC_Send_PiPo failed: %d", ret);
priv->state = PLAY_STATE_IDLE;
xQueueSendToBack(priv->free_queue, &item1, 0);
xQueueSendToBack(priv->free_queue, &item2, 0);
}
} else {
LOGE("Failed to get start buffers");
priv->state = PLAY_STATE_IDLE;
if (item1.addr) xQueueSendToBack(priv->free_queue, &item1, 0);
}
}
break;
case LISA_AUDIO_IOCTL_PLAY_STOP:
if (priv->state != PLAY_STATE_IDLE) {
uint32_t dev_bitmap = channel_to_bitmap(LISA_AUDIO_CH_LEFT);
DAC_SetMute(priv->hdrv, dev_bitmap, dev_bitmap);
#ifdef CONFIG_LISA_AUDIO_PLAY_PA_ENABLE
play_pa_control(false);
#endif
DAC_Abort(priv->hdrv, dev_bitmap, dev_bitmap);
priv->state = PLAY_STATE_IDLE;
play_item_t item;
while (xQueueReceive(priv->play_queue, &item, 0) == pdPASS) {
xQueueSendToBack(priv->free_queue, &item, 0);
}
if (priv->ping_addr) {
item.addr = priv->ping_addr;
item.size = 0;
xQueueSendToBack(priv->free_queue, &item, 0);
priv->ping_addr = NULL;
}
if (priv->pong_addr) {
item.addr = priv->pong_addr;
item.size = 0;
xQueueSendToBack(priv->free_queue, &item, 0);
priv->pong_addr = NULL;
}
priv->status = LISA_AUDIO_STATUS_IDLE;
}
break;
case LISA_AUDIO_IOCTL_PLAY_SET_GAIN:
if (arg) {
lisa_audio_gain_t *gain = (lisa_audio_gain_t *)arg;
uint32_t gain_a = DAC_GAIN_A_VAL(gain->analog_gain);
uint32_t gain_d = DAC_GAIN_D_VAL(gain->digital_gain);
uint32_t vol_flag = DAC_VOL_FLAG_A_LEFT | DAC_VOL_FLAG_D_LEFT;
ret = DAC_SetVolume(priv->hdrv, gain_a, gain_d, vol_flag);
LOGI("Play gain set: %d/%d dB", gain->analog_gain, gain->digital_gain);
}
break;
case LISA_AUDIO_IOCTL_PLAY_FLUSH:
if (priv->state == PLAY_STATE_PLAY_RUN) {
while (uxQueueMessagesWaiting(priv->play_queue) > 0) {
vTaskDelay(pdMS_TO_TICKS(10));
}
/* 等待最后一个buffer播完 (估算时间) */
uint32_t buffer_ms = priv->buffer_samples * 1000 / priv->config.format.sample_rate;
vTaskDelay(pdMS_TO_TICKS(buffer_ms + 10));
}
break;
case LISA_AUDIO_IOCTL_PLAY_GET_STATUS:
if (arg) {
*(lisa_audio_status_t *)arg = priv->status;
}
break;
default:
ret = LISA_DEVICE_ERR_NOT_SUPPORT;
break;
}
return ret;
}
int arcs_audio_play_init(lisa_audio_play_priv_t *priv)
{
memset(priv, 0, sizeof(lisa_audio_play_priv_t));
priv->hdrv = DAC01();
priv->status = LISA_AUDIO_STATUS_IDLE;
priv->state = PLAY_STATE_IDLE;
priv->initialized = true;
LOGI("LISA Audio Play initialized");
return LISA_DEVICE_OK;
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_TAG "lisa_audio_record"
#include <string.h>
#include "lisa_audio_internal.h"
#include "audio_adc_init.h"
#include "lisa_log.h"
#include "lisa_mem.h"
#include "Driver_ADC_PDM.h"
#include "Driver_Common.h"
#include "dma.h"
#include "cache.h"
#include "systick.h"
/* Must be defined in lisa_audio_arcs.c */
extern int audio_submit_event_from_isr(internal_audio_event_t *event);
/* DMA 通道定义 */
#define GPDMA_ADC0_CHN (1)
#define GPDMA_ADC1_CHN (3)
/* 前向声明 */
static void record_event_callback(uint32_t event, uint32_t user);
#define RECORD_SAMPLE_BITS 16
/* ===== 辅助函数 ===== */
static inline uint32_t channel_to_bitmap(lisa_audio_channel_t channels)
{
return (uint32_t)channels;
}
static inline uint8_t channel_count(lisa_audio_channel_t channels)
{
if (channels == LISA_AUDIO_CH_STEREO) return 2;
return 1;
}
/* ADC 采样率转换(仅支持 8K/16K/48K */
static uint32_t record_sample_rate_to_ctrl(lisa_audio_rate_t rate)
{
switch (rate) {
case LISA_AUDIO_RATE_8K: return CSK_ADCPDM_SR_8KHZ;
case LISA_AUDIO_RATE_16K: return CSK_ADCPDM_SR_16KHZ;
case LISA_AUDIO_RATE_48K: return CSK_ADCPDM_SR_48KHZ;
/* 不支持的采样率映射到 16K */
default:
LOGW("Record unsupported rate %d, fallback to 16K", rate);
return CSK_ADCPDM_SR_16KHZ;
}
}
static uint32_t record_get_osr_for_rate(lisa_audio_rate_t rate)
{
/* SR * OSR 必须是 4M 或 12M */
switch (rate) {
case LISA_AUDIO_RATE_8K:
/* 8000 * 500 = 4M */
return CSK_ADCPDM_OSR_500;
case LISA_AUDIO_RATE_16K:
case LISA_AUDIO_RATE_24K:
/* 16000 * 250 = 4M, 24000 * 250 = 6M */
return CSK_ADCPDM_OSR_250;
case LISA_AUDIO_RATE_48K:
/* 48000 * 250 = 12M */
return CSK_ADCPDM_OSR_250;
default:
/* 默认使用 OSR_250 */
return CSK_ADCPDM_OSR_250;
}
}
/* ===== Record 事件回调 ===== */
#ifndef DMA_CHANNEL_ANY
#define DMA_CHANNEL_ANY (0xFF)
#endif
static void record_event_callback(uint32_t event, uint32_t user)
{
lisa_audio_record_priv_t *priv = (lisa_audio_record_priv_t *)user;
int32_t ret;
int submit_ret = -1;
if (event & (CSK_ADCPDM_EVENT_RECEIVE_COMPLETE | CSK_ADCPDM_EVENT_BLOCK_COMPLETE)) {
/* 获取已完成的缓冲区 */
void *completed_buf = priv->buffers[priv->current_index];
/* Invalidate cache to ensure CPU reads fresh data from DMA */
dcache_invalidate_range((uint32_t)completed_buf, (uint32_t)completed_buf + priv->buffer_size);
/* 先尝试提交事件,只有成功时才更新索引,避免数据错乱 */
if (priv->is_running) {
internal_audio_event_t new_event = {
.type = AUDIO_EVENT_TYPE_RECORD,
.buffer = completed_buf,
.samples = priv->buffer_samples,
.timestamp = SysTimeMsGet() * 1000000ULL,
};
submit_ret = audio_submit_event_from_isr(&new_event);
}
/* 只有在事件成功提交后才更新 current_index */
if (submit_ret == 0) {
if (++priv->current_index >= priv->buffer_count) {
priv->current_index = 0;
}
} else if (submit_ret != 0 && priv->is_running) {
/* 事件提交失败,保持 current_index 不变,下次继续使用同一个 buffer */
LOGW("Event submit failed, reusing buffer %d", priv->current_index);
}
/* 计算下一个用于 DMA 的 buffer 索引 */
int next_index = priv->current_index;
if (++next_index >= priv->buffer_count) {
next_index = 0;
}
uint32_t dev_bitmap = channel_to_bitmap(priv->config.format.channels);
#ifdef CONFIG_LISA_AUDIO_RECORD_USE_PIPO
/* PiPo 模式 */
ret = ADC_PDM_Receive_PiPo(priv->hdrv,
&(PIPO_IN_BLOCK){
.sample_data = priv->buffers[next_index],
.sample_cnt = priv->buffer_samples,
.flags = 0
},
&(uint8_t){1},
dev_bitmap,
ADC_PDM_RX_FLAG_START_NOW);
#else
/* 标准模式 */
ret = ADC_PDM_Receive(priv->hdrv,
priv->buffers[next_index],
priv->buffer_samples,
dev_bitmap,
ADC_PDM_RX_FLAG_START_NOW);
#endif
if(ret != CSK_DRIVER_OK){
CLOG("record_event_callback ADC_PDM_Receive ret:%d", ret);
}
}
if (event & CSK_ADCPDM_EVENT_RX_FIFO_FULL) {
LOGW("Record FIFO full");
}
if (event & CSK_ADCPDM_EVENT_RX_FIFO_OVERRUN) {
LOGE("Record FIFO overrun");
}
}
/* ===== Record API 实现 ===== */
int arcs_audio_record_config(lisa_audio_record_priv_t *priv, const lisa_audio_record_config_t *config)
{
int ret = 0;
if (!config) {
return LISA_DEVICE_ERR_INVALID;
}
memcpy(&priv->config, config, sizeof(lisa_audio_record_config_t));
/* 配置 DMA 通道 */
ADC_PDM_DMA_CHS dmach = {
.dma_ch_in_left = DMA_CHANNEL_ANY,
.dma_ch_in_right = DMA_CHANNEL_ANY
};
if (config->format.channels & LISA_AUDIO_CH_LEFT) {
dmach.dma_ch_in_left = GPDMA_ADC0_CHN;
}
if (config->format.channels & LISA_AUDIO_CH_RIGHT) {
dmach.dma_ch_in_right = GPDMA_ADC1_CHN;
}
/* 初始化 ADC 驱动 */
uint32_t flags = ADC_PDM_BMP_FLAG_USE_16BITS | channel_to_bitmap(config->format.channels);
#if CONFIG_LISA_AUDIO_RECORD_USE_DMIC
flags |= ADC_PDM_BMP_FLAG_USE_PDM;
#endif
if (priv->adc_initialized) {
ADC_PDM_Uninitialize(priv->hdrv);
priv->adc_initialized = false;
}
ret = ADC_PDM_Initialize(priv->hdrv,
record_event_callback,
(uint32_t)priv,
flags,
&dmach);
if (ret != 0) {
LOGE("ADC_PDM_Initialize failed: %d", ret);
goto exit;
}
priv->adc_initialized = true;
ret = ADC_PDM_PowerControl(priv->hdrv, CSK_POWER_FULL);
if (ret != 0) {
LOGE("ADC_PDM_PowerControl failed: %d", ret);
goto exit;
}
/* 采样率和 OSR */
uint32_t sr_ctrl = record_sample_rate_to_ctrl(config->format.sample_rate);
uint32_t osr_ctrl = record_get_osr_for_rate(config->format.sample_rate);
ret = ADC_PDM_Control(priv->hdrv, sr_ctrl | osr_ctrl, 0);
if (ret != 0) {
LOGE("ADC_PDM_Control SR/OSR failed: %d", ret);
goto exit;
}
uint32_t rxcfg = (config->format.channels == LISA_AUDIO_CH_STEREO) ?
CSK_ADCPDM_RXCFG_MIXED : CSK_ADCPDM_RXCFG_SEPA;
ret = ADC_PDM_Control(priv->hdrv, rxcfg, 0);
if (ret != 0) {
LOGE("ADC_PDM_Control RXCFG failed: %d", ret);
goto exit;
}
/* HPF 配置 */
if (config->enable_hpf) {
ret = ADC_PDM_Control(priv->hdrv,
CSK_ADCPDM_HPF_SET,
CSK_ADCPDM_ARG_HPF1_EN | CSK_ADCPDM_ARG_HPF2_EN | CSK_ADCPDM_ARG_HPF2_CUT(3));
if (ret != 0) {
LOGE("ADC_PDM_Control HPF failed: %d", ret);
goto exit;
}
}
/* PGA 输入模式 */
uint32_t pga_mode;
if (config->differential_input) {
pga_mode = (CSK_ADCPDM_ARG_LPGA_INPUT_DIFFER | CSK_ADCPDM_ARG_RPGA_INPUT_DIFFER);
} else {
pga_mode = (CSK_ADCPDM_ARG_LPGA_INPUT_SINGLE | CSK_ADCPDM_ARG_RPGA_INPUT_SINGLE);
}
ret = ADC_PDM_Control(priv->hdrv, CSK_ADCPDM_PGA_INPUT_SET, pga_mode);
if (ret != 0) {
LOGE("ADC_PDM_Control PGA failed: %d", ret);
goto exit;
}
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
uint32_t gain_a = ADC_PDM_GAIN_A_VAL(config->gain_l.analog_gain);
uint32_t gain_d = ADC_PDM_GAIN_D_VAL(config->gain_l.digital_gain);
#else
uint32_t gain_a = ADC_PDM_GAIN_A_VAL(config->gain.analog_gain);
uint32_t gain_d = ADC_PDM_GAIN_D_VAL(config->gain.digital_gain);
#endif
uint32_t vol_flag = 0;
if (config->format.channels & LISA_AUDIO_CH_LEFT) {
vol_flag |= ADC_PDM_VOL_FLAG_A_LEFT | ADC_PDM_VOL_FLAG_D_LEFT;
}
if (config->format.channels & LISA_AUDIO_CH_RIGHT) {
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
gain_a |= ADC_PDM_GAIN_A_VAL(config->gain_r.analog_gain) << 16;
gain_d |= ADC_PDM_GAIN_D_VAL(config->gain_r.digital_gain) << 16;
#else
gain_a |= ADC_PDM_GAIN_A_VAL(config->gain.analog_gain) << 16;
gain_d |= ADC_PDM_GAIN_D_VAL(config->gain.digital_gain) << 16;
#endif
vol_flag |= ADC_PDM_VOL_FLAG_A_RIGHT | ADC_PDM_VOL_FLAG_D_RIGHT;
}
ret = ADC_PDM_SetVolume(priv->hdrv, gain_a, gain_d, vol_flag);
if (ret != 0) {
LOGE("ADC_PDM_SetVolume failed: %d", ret);
goto exit;
}
/* Unmute configured channels */
uint32_t mute_channels = channel_to_bitmap(config->format.channels);
ret = ADC_PDM_SetMute(priv->hdrv, 0, mute_channels);
if (ret != 0) {
LOGE("ADC_PDM_SetMute failed: %d", ret);
ret = LISA_DEVICE_ERR_INIT_FAIL;
goto exit;
}
priv->current_index = 0;
priv->status = LISA_AUDIO_STATUS_IDLE;
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
LOGI("Record configured: rate=%d, gain_l=%d/%d dB, gain_r=%d/%d dB",
config->format.sample_rate,
config->gain_l.analog_gain,
config->gain_l.digital_gain,
config->gain_r.analog_gain,
config->gain_r.digital_gain);
#else
LOGI("Record configured: rate=%d, gain=%d/%d dB",
config->format.sample_rate,
config->gain.analog_gain,
config->gain.digital_gain);
#endif
exit:
return ret;
}
static int record_start_locked(lisa_audio_record_priv_t *priv)
{
int ret = LISA_DEVICE_OK;
if (!priv->is_running) {
priv->buffer_count = CONFIG_LISA_AUDIO_RECORD_BUFFER_COUNT;
priv->buffer_samples = CONFIG_LISA_AUDIO_RECORD_BUFFER_SAMPLES * channel_count(priv->config.format.channels);
priv->buffer_size = priv->buffer_samples * (RECORD_SAMPLE_BITS / 8);
if (!priv->buffers) {
priv->buffers = lisa_mem_alloc(sizeof(void *) * priv->buffer_count);
if (!priv->buffers) {
LOGE("Failed to allocate buffer array, size: %d", sizeof(void *) * priv->buffer_count);
return LISA_DEVICE_ERR_NO_MEM;
}
for (int i = 0; i < priv->buffer_count; i++) {
priv->buffers[i] = lisa_mem_align_alloc(32, priv->buffer_size);
if (!priv->buffers[i]) {
LOGE("Failed to allocate buffer %d", i);
return LISA_DEVICE_ERR_NO_MEM;
}
}
}
priv->current_index = 0;
uint32_t dev_bitmap = channel_to_bitmap(priv->config.format.channels);
#ifdef CONFIG_LISA_AUDIO_RECORD_USE_PIPO
/* PiPo 双缓冲启动 */
ret = ADC_PDM_Receive_PiPo(priv->hdrv, (PIPO_IN_BLOCK[]){
{ .sample_data = priv->buffers[0], .sample_cnt = priv->buffer_samples, .flags = 0 },
{ .sample_data = priv->buffers[1], .sample_cnt = priv->buffer_samples, .flags = 0 },
}, &(uint8_t){2}, dev_bitmap, ADC_PDM_RX_FLAG_START_NOW);
#else
/* 标准单缓冲启动 */
ret = ADC_PDM_Receive(priv->hdrv,
priv->buffers[0],
priv->buffer_samples,
dev_bitmap,
ADC_PDM_RX_FLAG_START_NOW);
#endif
if (ret == 0) {
priv->is_running = true;
priv->status = LISA_AUDIO_STATUS_RUNNING;
LOGI("Record started");
} else {
LOGE("ADC_PDM_Receive failed: %d", ret);
/* 错误处理: 如果启动失败,应考虑释放资源,这里暂时保持原有逻辑 */
return LISA_DEVICE_ERR_IO;
}
}
return ret;
}
static int record_stop_locked(lisa_audio_record_priv_t *priv)
{
if (priv->is_running) {
ADC_PDM_Abort(priv->hdrv, channel_to_bitmap(priv->config.format.channels));
priv->is_running = false;
priv->status = LISA_AUDIO_STATUS_IDLE;
if (priv->buffers) {
for (int i = 0; i < priv->buffer_count; i++) {
if (priv->buffers[i]) {
lisa_mem_free(priv->buffers[i]);
priv->buffers[i] = NULL;
}
}
lisa_mem_free(priv->buffers);
priv->buffers = NULL;
}
}
return LISA_DEVICE_OK;
}
int arcs_audio_record_control(lisa_audio_record_priv_t *priv, uint32_t cmd, void *arg)
{
int ret = 0;
switch (cmd) {
case LISA_AUDIO_IOCTL_RECORD_START:
ret = record_start_locked(priv);
break;
case LISA_AUDIO_IOCTL_RECORD_STOP:
ret = record_stop_locked(priv);
break;
case LISA_AUDIO_IOCTL_RECORD_PAUSE:
if (priv->is_running) {
priv->is_running = false;
ADC_PDM_Disable(priv->hdrv, channel_to_bitmap(priv->config.format.channels));
priv->status = LISA_AUDIO_STATUS_PAUSED;
LOGI("Record paused");
}
break;
case LISA_AUDIO_IOCTL_RECORD_RESUME:
if (!priv->is_running && priv->status == LISA_AUDIO_STATUS_PAUSED) {
priv->is_running = true;
ADC_PDM_Enable(priv->hdrv, channel_to_bitmap(priv->config.format.channels));
priv->status = LISA_AUDIO_STATUS_RUNNING;
LOGI("Record resumed");
}
break;
case LISA_AUDIO_IOCTL_RECORD_RESET:
LOGI("Record queue reset");
break;
case LISA_AUDIO_IOCTL_RECORD_SET_GAIN:
if (arg) {
lisa_audio_record_config_t *config = &priv->config;
lisa_audio_gain_t *gain = (lisa_audio_gain_t *)arg;
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
uint32_t gain_a = ADC_PDM_GAIN_A_VAL(gain[0].analog_gain);
uint32_t gain_d = ADC_PDM_GAIN_D_VAL(gain[0].digital_gain);
#else
uint32_t gain_a = ADC_PDM_GAIN_A_VAL(gain->analog_gain);
uint32_t gain_d = ADC_PDM_GAIN_D_VAL(gain->digital_gain);
#endif
uint32_t vol_flag = 0;
if (config->format.channels & LISA_AUDIO_CH_LEFT) {
vol_flag |= ADC_PDM_VOL_FLAG_A_LEFT | ADC_PDM_VOL_FLAG_D_LEFT;
}
if (config->format.channels & LISA_AUDIO_CH_RIGHT) {
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
gain_a |= ADC_PDM_GAIN_A_VAL(gain[1].analog_gain) << 16;
gain_d |= ADC_PDM_GAIN_D_VAL(gain[1].digital_gain) << 16;
#else
gain_a |= ADC_PDM_GAIN_A_VAL(gain->analog_gain) << 16;
gain_d |= ADC_PDM_GAIN_D_VAL(gain->digital_gain) << 16;
#endif
vol_flag |= ADC_PDM_VOL_FLAG_A_RIGHT | ADC_PDM_VOL_FLAG_D_RIGHT;
}
ret = ADC_PDM_SetVolume(priv->hdrv, gain_a, gain_d, vol_flag);
#ifdef CONFIG_LISA_AUDIO_RECORD_INDIVIDUAL_GAIN
LOGI("Record gain set: L=%d/%d dB, R=%d/%d dB",
gain[0].analog_gain, gain[0].digital_gain,
gain[1].analog_gain, gain[1].digital_gain);
#else
LOGI("Record gain set: %d/%d dB", gain->analog_gain, gain->digital_gain);
#endif
}
break;
case LISA_AUDIO_IOCTL_RECORD_GET_STATUS:
if (arg) {
*(lisa_audio_status_t *)arg = priv->status;
}
break;
default:
ret = LISA_DEVICE_ERR_NOT_SUPPORT;
break;
}
return ret;
}
int arcs_audio_record_init(lisa_audio_record_priv_t *priv)
{
memset(priv, 0, sizeof(lisa_audio_record_priv_t));
audio_adc_platform_init();
priv->hdrv = ADC_PDM01();
priv->status = LISA_AUDIO_STATUS_IDLE;
priv->is_running = false;
priv->initialized = true;
LOGI("LISA Audio Record initialized");
return LISA_DEVICE_OK;
}

View File

@@ -0,0 +1,15 @@
if(CONFIG_LISA_CAMERA_DEVICE)
listenai_library_named(lisa_camera_arcs)
listenai_library_sources(
lisa_camera_arcs.c
)
listenai_include_directories(${CMAKE_CURRENT_SOURCE_DIR}/)
add_subdirectory(sensors)
add_subdirectory(bus)
endif()

View File

@@ -0,0 +1,42 @@
menu "LISA Camera"
config LISA_CAMERA_DEVICE
bool "LISA Camera support"
default n
help
Enable LISA Camera support
if LISA_CAMERA_DEVICE
choice LISA_CAMERA_BUS_TYPE
prompt "Select bus type"
default LISA_CAMERA_BUS_SPI
config LISA_CAMERA_BUS_SPI
bool "SPI Bus"
select LISA_SPI_DEVICE
help
Use SPI bus for camera data.
config LISA_CAMERA_BUS_DVP
bool "DVP Bus"
select LISA_DVP_DEVICE
help
Use DVP bus for camera data.
endchoice
config LISA_CAMERA_SENSOR_BF3901
bool "BF3901"
default n
config LISA_CAMERA_SENSOR_GC032A
bool "GC032A"
default n
config LISA_CAMERA_SENSOR_GC0328
bool "GC0328"
default n
endif #LISA_CAMERA_DEVICE
endmenu

View File

@@ -0,0 +1,589 @@
# Camera 驱动
基于 lisa_device 框架的摄像头设备驱动,为 ARCS 平台提供统一的图像采集接口。
## 功能特性
- **传感器支持**: GC032A、GC0328、BF3901 等多种图像传感器自动探测
- **总线接口**: 支持 DVP (并行) 和 SPI (串行) 两种数据总线
- **像素格式**: 支持 RGB565、RGB888、YUV422、YUV420、灰度、JPEG、RAW 格式
- **帧缓冲管理**: 多缓冲队列机制,支持异步捕获和回调通知
- **图像控制**: 支持水平镜像、垂直翻转、裁剪窗口设置
- **线程安全**: 使用队列机制管理帧缓冲区,可在多线程环境中使用
## 配置选项
`prj.conf` 中启用驱动:
```kconfig
CONFIG_LISA_CAMERA_DEVICE=y # 启用 Camera 驱动
# 选择总线类型 (二选一)
CONFIG_LISA_CAMERA_BUS_DVP=y # 使用 DVP 并行总线
# CONFIG_LISA_CAMERA_BUS_SPI=y # 使用 SPI 串行总线
# 选择传感器 (可多选)
CONFIG_LISA_CAMERA_SENSOR_GC032A=y # 启用 GC032A 传感器
CONFIG_LISA_CAMERA_SENSOR_GC0328=y # 启用 GC0328 传感器
CONFIG_LISA_CAMERA_SENSOR_BF3901=y # 启用 BF3901 传感器
```
## API 接口
### 初始化与配置接口
```c
int lisa_camera_setup(lisa_device_t *dev, const lisa_camera_config_t *config);
int lisa_camera_attach_bus(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config);
int lisa_camera_get_capabilities(lisa_device_t *dev, lisa_camera_capabilities_t *caps);
```
**重要**: 必须按顺序调用 `lisa_camera_setup()``lisa_camera_attach_bus()` 后,才能启动摄像头。
### 捕获控制接口
```c
int lisa_camera_start(lisa_device_t *dev);
int lisa_camera_stop(lisa_device_t *dev);
int lisa_camera_capture(lisa_device_t *dev, lisa_camera_fb_t **fb);
int lisa_camera_release_fb(lisa_device_t *dev, lisa_camera_fb_t *fb);
```
### 参数设置接口
```c
int lisa_camera_set_pixformat(lisa_device_t *dev, lisa_camera_pixel_format_t format);
int lisa_camera_set_hmirror(lisa_device_t *dev, bool enable);
int lisa_camera_set_vflip(lisa_device_t *dev, bool enable);
int lisa_camera_set_crop(lisa_device_t *dev, const lisa_camera_crop_t *crop);
int lisa_camera_set_callback(lisa_device_t *dev, lisa_camera_frame_callback_t callback, void *user_data);
```
### 状态查询接口
```c
int lisa_camera_get_framesize(lisa_device_t *dev, uint16_t *width, uint16_t *height);
lisa_camera_pixel_format_t lisa_camera_get_pixformat(lisa_device_t *dev);
```
## 使用示例
### 基础图像捕获
```c
#include "lisa_device.h"
#include "lisa_camera.h"
int camera_example(void)
{
int ret;
// 1. 获取设备
lisa_device_t *camera_dev = lisa_device_get("camera");
if (!lisa_device_ready(camera_dev)) {
return -1;
}
lisa_device_t *i2c_dev = lisa_device_get("i2c1");
// 2. 配置摄像头
lisa_camera_config_t config = {
.hw_config = {
.mclk_pad = CSK_IOMUX_PAD_A,
.mclk_pin = 26,
.pwdn_gpio_dev = lisa_device_get("gpiob"),
.pwdn_pin = 9,
.pwdn_delay_us = 1000,
.xclk_delay_us = 1000,
.i2c_dev = i2c_dev,
},
.xclk_freq_hz = 18000000, // 18MHz 外部时钟
.fb_count = 3, // 3 个帧缓冲区
.enable_hmirror = false,
.enable_vflip = false,
};
ret = lisa_camera_setup(camera_dev, &config);
if (ret != LISA_DEVICE_OK) {
return ret;
}
// 3. 配置 SPI 总线
lisa_camera_bus_config_t bus_config = {
.bus_type = LISA_CAMERA_BUS_SPI,
.dma_channel = 2,
.config.spi = {
.spi_dev = lisa_device_get("spi0"),
.cs_gpio = lisa_device_get("gpioa"),
.cs_pin = 22,
.spi_bit_order = 1, // LSB first
.spi_mode = 1, // CPOL=0, CPHA=1
}
};
lisa_camera_get_framesize(camera_dev, &bus_config.width, &bus_config.height);
bus_config.pixel_format = lisa_camera_get_pixformat(camera_dev);
ret = lisa_camera_attach_bus(camera_dev, &bus_config);
if (ret != LISA_DEVICE_OK) {
return ret;
}
// 4. 启动摄像头
ret = lisa_camera_start(camera_dev);
if (ret != LISA_DEVICE_OK) {
return ret;
}
// 5. 捕获图像
lisa_camera_fb_t *fb = NULL;
ret = lisa_camera_capture(camera_dev, &fb);
if (ret == LISA_DEVICE_OK && fb != NULL) {
// 处理图像数据
// fb->buf: 图像数据指针
// fb->len: 数据长度
// fb->width, fb->height: 分辨率
// 6. 释放帧缓冲区
lisa_camera_release_fb(camera_dev, fb);
}
// 7. 停止摄像头
lisa_camera_stop(camera_dev);
return 0;
}
```
### DVP 总线配置
```c
// DVP 并行总线配置
lisa_camera_bus_config_t bus_config = {
.bus_type = LISA_CAMERA_BUS_DVP,
.dma_channel = 2,
.config.dvp = {
.dvp_dev = lisa_device_get("dvp0"),
.data_align = 1, // 左对齐 [bit11~4]
.line_offset = 0,
.pixel_offset = 0,
.pclk_polarity = 0, // 下降沿采样
.vsync_polarity = 1, // 高电平有效
.hsync_polarity = 1, // 高电平有效
}
};
lisa_camera_get_framesize(camera_dev, &bus_config.width, &bus_config.height);
bus_config.pixel_format = lisa_camera_get_pixformat(camera_dev);
lisa_camera_attach_bus(camera_dev, &bus_config);
```
### 连续捕获回调模式
```c
// 帧完成回调函数
void frame_callback(const lisa_camera_fb_t *fb, void *user_data)
{
lisa_device_t *dev = (lisa_device_t *)user_data;
// 处理帧数据 (在 ISR 上下文中,应尽快返回)
process_frame(fb);
}
// 设置回调
lisa_camera_set_callback(camera_dev, frame_callback, camera_dev);
// 启动后,每帧完成时自动调用回调
lisa_camera_start(camera_dev);
```
## 关键数据结构
### lisa_camera_config_t
摄像头配置结构体:
| 字段 | 类型 | 说明 |
|------|------|------|
| `hw_config` | `lisa_camera_hw_config_t` | 硬件配置 (引脚、I2C 等) |
| `xclk_freq_hz` | `uint32_t` | 外部时钟频率 (Hz) |
| `fb_count` | `uint8_t` | 帧缓冲区数量 (建议 2-3) |
| `enable_hmirror` | `bool` | 水平镜像开关 |
| `enable_vflip` | `bool` | 垂直翻转开关 |
### lisa_camera_fb_t
帧缓冲区结构体:
| 字段 | 类型 | 说明 |
|------|------|------|
| `buf` | `uint8_t *` | 图像数据指针 |
| `len` | `uint32_t` | 数据长度 (字节) |
| `width` | `uint16_t` | 图像宽度 |
| `height` | `uint16_t` | 图像高度 |
| `format` | `lisa_camera_pixel_format_t` | 像素格式 |
| `timestamp` | `uint32_t` | 时间戳 (毫秒) |
## 像素格式说明
| 枚举值 | 格式 | 字节/像素 | 说明 |
|-------|------|----------|------|
| `LISA_CAMERA_PIXFMT_RGB565` | RGB565 | 2 | 常用 LCD 显示格式 |
| `LISA_CAMERA_PIXFMT_RGB888` | RGB888 | 3 | 真彩色 |
| `LISA_CAMERA_PIXFMT_YUV422` | YUV422 | 2 | 视频编码常用 |
| `LISA_CAMERA_PIXFMT_YUV420` | YUV420 | 1.5 | 视频编码常用 |
| `LISA_CAMERA_PIXFMT_GRAY` | 灰度 | 1 | 单通道灰度图 |
| `LISA_CAMERA_PIXFMT_JPEG` | JPEG | 可变 | 硬件压缩格式 |
| `LISA_CAMERA_PIXFMT_RAW` | RAW | 可变 | Bayer 原始数据 |
## 硬件配置
### 引脚复用配置
Camera 驱动需要在板型目录中配置相关引脚复用函数。
**配置位置**:
- **定义**: `boards/<板型名>/pinmux.c` 中实现函数
- **声明**: `boards/<板型名>/pinmux.h` 中声明函数
- **调用时机**: 设备初始化时自动调用
**SPI 总线引脚示例** (参考 `boards/arcs_evb/pinmux.c`):
```c
#define CAM_SPI_CS_PIN 22
#define CAM_SPI_MOSI_PIN 24
#define CAM_SPI_SCK_PIN 25
void lisa_spi0_pinmux(void)
{
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_SPI_SCK_PIN, CSK_IOMUX_FUNC_ALTER5);
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_SPI_MOSI_PIN, CSK_IOMUX_FUNC_ALTER5);
}
void lisa_gpioa_pinmux(void)
{
// CS 引脚配置为 GPIO
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_SPI_CS_PIN, CSK_IOMUX_FUNC_DEFAULT);
}
```
**DVP 总线引脚示例**:
```c
#define CAM_HSYNC_PIN 10
#define CAM_VSYNC_PIN 11
#define CAM_PCLK_PIN 12
#define CAM_D0_PIN 13
// ... D1-D7
void lisa_dvp_pinmux(void)
{
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_HSYNC_PIN, CSK_IOMUX_FUNC_ALTER16);
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_VSYNC_PIN, CSK_IOMUX_FUNC_ALTER16);
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_PCLK_PIN, CSK_IOMUX_FUNC_ALTER16);
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_A, CAM_D0_PIN, CSK_IOMUX_FUNC_ALTER16);
// ... 配置其他数据引脚
}
```
### I2C 配置
传感器通过 I2C (SCCB 协议) 进行寄存器配置:
```c
void lisa_i2c1_pinmux(void)
{
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_B, 0, 8); // SDA
IOMuxManager_PinConfigure(CSK_IOMUX_PAD_B, 1, 8); // SCL
}
```
### 控制引脚
| 引脚 | 功能 | 说明 |
|-----|------|------|
| PWDN | Power Down | 低电平工作,高电平休眠 |
| MCLK | 外部时钟 | DVP 控制器输出,驱动传感器 |
## 注意事项
1. **初始化顺序**: 必须按 `setup``attach_bus``start` 顺序调用
2. **帧缓冲区释放**: 每次 `lisa_camera_capture()` 成功后,必须调用 `lisa_camera_release_fb()` 释放帧缓冲区
3. **缓冲区数量**: `fb_count` 建议设置为 2-3过多会占用大量内存
4. **内存对齐**: 帧缓冲区内部按 32 字节对齐分配,支持 DMA 传输
5. **Cache 一致性**: 驱动内部会调用 `HAL_InvalidateDCache_by_Addr()` 刷新 Cache
6. **回调上下文**: `lisa_camera_set_callback()` 设置的回调函数在 ISR 上下文中执行,应尽快返回
7. **捕获超时**: `lisa_camera_capture()` 阻塞等待最多 1000ms超时返回 `LISA_DEVICE_ERR_TIMEOUT`
8. **总线选择**: DVP 适合高分辨率/高帧率 (占用 10+ GPIO)SPI 适合低分辨率 (占用 4 GPIO)
9. **传感器探测**: 驱动会自动遍历所有已启用的传感器进行探测,确保 I2C 配置正确
10. **时钟频率**: `xclk_freq_hz` 需根据传感器规格设置,常用值为 18MHz 或 24MHz
## 传感器移植适配
### 移植步骤
移植新传感器需要完成以下 5 个步骤:
#### 1. 添加传感器信息定义
`sensors/sensor.h` 中添加传感器枚举和地址定义:
```c
// 在 camera_model_t 枚举中添加
typedef enum {
// ... 已有传感器
CAMERA_XXXX, // 新增传感器型号
CAMERA_MODEL_MAX,
} camera_model_t;
// 在 camera_sccb_addr_t 枚举中添加 I2C 地址
typedef enum {
// ... 已有地址
XXXX_SCCB_ADDR = 0x21, // 新传感器 I2C 地址 (7位)
} camera_sccb_addr_t;
// 在 camera_pid_t 枚举中添加 PID
typedef enum {
// ... 已有 PID
XXXX_PID = 0x1234, // 新传感器 Product ID
} camera_pid_t;
```
#### 2. 注册传感器信息表
`sensors/sensor.c``camera_sensor[]` 数组中添加传感器信息:
```c
const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
// ... 已有传感器
{CAMERA_XXXX, "XXXX", XXXX_SCCB_ADDR, XXXX_PID, 640, 480,
(PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
};
```
#### 3. 实现传感器驱动
创建 `sensors/src/xxxx.c``sensors/inc/xxxx.h` 文件:
**头文件** (`sensors/inc/xxxx.h`):
```c
#ifndef __XXXX_H__
#define __XXXX_H__
#include "sensor.h"
/**
* @brief 探测传感器
* @param slv_addr I2C 地址
* @param id 输出传感器 ID
* @return 非零表示探测成功0 表示失败
*/
int xxxx_detect(int slv_addr, sensor_id_t *id);
/**
* @brief 初始化传感器函数指针
* @param sensor 传感器结构体指针
* @return 0
*/
int xxxx_init(sensor_t *sensor);
#endif
```
**源文件** (`sensors/src/xxxx.c`):
```c
#include "sensor.h"
#include "xxxx.h"
#include "xxxx_regs.h" // 寄存器定义
#include "xxxx_settings.h" // 初始化寄存器表
// 探测函数:读取 PID 寄存器验证传感器
int xxxx_detect(int slv_addr, sensor_id_t *id)
{
if (XXXX_SCCB_ADDR == slv_addr) {
uint8_t pid_h = CAMERA_READ_REG8(slv_addr, PID_REG_H);
uint8_t pid_l = CAMERA_READ_REG8(slv_addr, PID_REG_L);
uint16_t pid = (pid_h << 8) | pid_l;
if (XXXX_PID == pid) {
id->PID = pid;
return pid; // 返回非零表示成功
}
}
return 0; // 探测失败
}
// 复位函数:软复位并写入初始化寄存器
static int reset(sensor_t *sensor)
{
// 1. 软复位
CAMERA_WRITE_REG8(sensor->slv_addr, RESET_REG, 0x01);
CAMERA_DELAY_MS(100);
// 2. 写入初始化寄存器表
// write_regs(sensor->slv_addr, xxxx_init_regs);
return 0;
}
// 设置像素格式
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
switch (pixformat) {
case PIXFORMAT_RGB565:
// 配置 RGB565 输出
break;
case PIXFORMAT_YUV422:
// 配置 YUV422 输出
break;
default:
return -1;
}
sensor->pixformat = pixformat;
return 0;
}
// 获取当前像素格式
static pixformat_t get_pixformat(sensor_t *sensor)
{
return sensor->pixformat;
}
// 设置窗口 (分辨率/裁剪)
static int set_window(sensor_t *sensor, int16_t x, int16_t y, uint16_t w, uint16_t h)
{
// 配置输出窗口寄存器
return 0;
}
// 获取当前窗口大小
static int get_window(sensor_t *sensor, uint16_t *w, uint16_t *h)
{
*w = 640; // 读取或返回默认值
*h = 480;
return 0;
}
// 设置水平镜像
static int set_hmirror(sensor_t *sensor, int enable)
{
// 配置镜像寄存器
return 0;
}
// 设置垂直翻转
static int set_vflip(sensor_t *sensor, int enable)
{
// 配置翻转寄存器
return 0;
}
// 初始化函数:填充传感器函数指针表
int xxxx_init(sensor_t *sensor)
{
// 必须实现的函数
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->get_pixformat = get_pixformat;
sensor->set_window = set_window;
sensor->get_window = get_window;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
// 可选函数 (不支持则设为 NULL 或 dummy)
sensor->set_brightness = NULL;
sensor->set_contrast = NULL;
sensor->set_saturation = NULL;
// ... 其他可选函数
return 0;
}
```
#### 4. 添加 Kconfig 配置
`Kconfig` 中添加传感器选项:
```kconfig
config LISA_CAMERA_SENSOR_XXXX
bool "XXXX"
default n
help
Enable XXXX camera sensor support.
```
#### 5. 注册到驱动
`lisa_camera_arcs.c` 中添加传感器引用:
```c
// 头文件引用
#if CONFIG_LISA_CAMERA_SENSOR_XXXX
#include "xxxx.h"
#endif
// camera_sensors 数组中添加
static const sensor_func_t camera_sensors[] = {
// ... 已有传感器
#if CONFIG_LISA_CAMERA_SENSOR_XXXX
{CAMERA_XXXX, xxxx_detect, xxxx_init},
#endif
};
```
### sensor_t 函数指针说明
| 函数指针 | 必需 | 说明 |
|---------|------|------|
| `reset` | **是** | 软复位并加载初始化配置 |
| `set_pixformat` | **是** | 设置像素格式 (RGB565/YUV422 等) |
| `get_pixformat` | **是** | 获取当前像素格式 |
| `set_window` | **是** | 设置输出窗口大小 |
| `get_window` | **是** | 获取当前窗口大小 |
| `set_hmirror` | 推荐 | 水平镜像控制 |
| `set_vflip` | 推荐 | 垂直翻转控制 |
| `set_colorbar` | 可选 | 测试模式 (彩条) |
| `set_brightness` | 可选 | 亮度调节 |
| `set_contrast` | 可选 | 对比度调节 |
| `set_saturation` | 可选 | 饱和度调节 |
| `set_gainceiling` | 可选 | 增益上限设置 |
### I2C 寄存器读写宏
驱动提供统一的寄存器操作宏:
```c
// 8 位寄存器地址
CAMERA_READ_REG8(addr, reg) // 读取寄存器
CAMERA_WRITE_REG8(addr, reg, value) // 写入寄存器
// 16 位寄存器地址 (如 OV5640)
CAMERA_READ_REG16(addr, reg)
CAMERA_WRITE_REG16(addr, reg, value)
// 延时
CAMERA_DELAY_MS(ms)
// 日志
CAMERA_LOGI("info message");
CAMERA_LOGE("error message");
```
### 移植验证
1.`prj.conf` 中启用新传感器:`CONFIG_LISA_CAMERA_SENSOR_XXXX=y`
2. 编译并烧录固件
3. 检查日志输出 `Sensor detected: PID=0xXXXX`
4. 测试图像捕获功能
## 文件说明
- `lisa_camera.h` - 公共 API 头文件
- `lisa_camera_arcs.c` - ARCS 平台驱动实现
- `Kconfig` - 配置选项
- `bus/lisa_camera_bus.h` - 总线接口定义
- `bus/lisa_camera_bus_dvp.c` - DVP 总线实现
- `bus/lisa_camera_bus_spi.c` - SPI 总线实现
- `sensors/` - 传感器驱动目录 (gc032a.c, gc0328.c, bf3901.c 等)

View File

@@ -0,0 +1,6 @@
listenai_library_sources_ifdef(CONFIG_LISA_CAMERA_BUS_SPI
lisa_camera_bus_spi.c
)
listenai_library_sources_ifdef(CONFIG_LISA_CAMERA_BUS_DVP
lisa_camera_bus_dvp.c
)

View File

@@ -0,0 +1,86 @@
#ifndef __LISA_CAMERA_BUS_H__
#define __LISA_CAMERA_BUS_H__
#include "lisa_camera.h"
#ifdef __cplusplus
extern "C" {
#endif
// 前向声明总线接口结构体
typedef struct lisa_camera_bus_if lisa_camera_bus_if_t;
/**
* @brief 获取空闲帧缓冲区回调函数类型(普通上下文)
*/
typedef lisa_camera_fb_t *(*lisa_camera_get_free_fb_t)(void *ctx);
/**
* @brief 获取空闲帧缓冲区回调函数类型ISR 上下文)
*/
typedef lisa_camera_fb_t *(*lisa_camera_get_free_fb_from_isr_t)(void *ctx);
/**
* @brief Camera 总线操作函数指针结构体
*
* 定义了一组标准化的总线操作接口由具体的总线驱动DVP, SPI等实现。
* lisa_camera_core 驱动通过这些接口与底层硬件总线交互。
*/
struct lisa_camera_bus_if {
/**
* @brief 初始化总线
*
* @param dev lisa_camera 设备指针
* @param bus_config 总线配置
* @return 0 成功, <0 失败
*/
int (*init)(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config);
/**
* @brief 设置帧大小
*
* @param dev lisa_camera 设备指针
* @param width 帧宽度
* @param height 帧高度
* @return 0 成功, <0 失败
*/
int (*set_framesize)(lisa_device_t *dev, uint16_t width, uint16_t height);
/**
* @brief 设置像素格式
*
* @param dev lisa_camera 设备指针
* @param pixformat 像素格式
* @return 0 成功, <0 失败
*/
int (*set_pixformat)(lisa_device_t *dev, uint16_t pixformat);
/**
* @brief 启动数据捕获
*
* @param dev lisa_camera 设备指针
* @param callback 帧完成回调函数
* @param get_free_fb 获取空闲帧缓冲区回调(普通上下文)
* @param get_free_fb_from_isr 获取空闲帧缓冲区回调ISR 上下文)
* @param user_data 用户数据,将传递给回调函数
* @return 0 成功, <0 失败
*/
int (*start_capture)(lisa_device_t *dev, lisa_camera_frame_callback_t callback,
lisa_camera_get_free_fb_t get_free_fb,
lisa_camera_get_free_fb_from_isr_t get_free_fb_from_isr,
void *user_data);
/**
* @brief 停止数据捕获
*
* @param dev lisa_camera 设备指针
* @return 0 成功, <0 失败
*/
int (*stop_capture)(lisa_device_t *dev);
};
#ifdef __cplusplus
}
#endif
#endif // __LISA_CAMERA_BUS_H__

View File

@@ -0,0 +1,295 @@
#include <stdint.h>
#include <string.h>
#include "lisa_camera.h"
#include "lisa_camera_bus.h"
#include "lisa_dvp.h"
#include "board.h"
#define TAG "camera_bus_dvp"
#include "lisa_log.h"
/* 定义是否使用 PingPong DMA 模式,默认使用普通模式 */
// #define DVP_USE_PINGPONG_DMA
/**
* @brief 将 lisa_camera 像素格式转换为 DVP 输入格式
*/
static inline lisa_dvp_input_format_t convert_to_dvp_pixel_format(lisa_camera_pixel_format_t format)
{
switch (format) {
case LISA_CAMERA_PIXFMT_GRAY:
return LISA_DVP_INPUT_FORM_LUMINA_8BIT;
case LISA_CAMERA_PIXFMT_RGB565:
case LISA_CAMERA_PIXFMT_YUV422:
default:
return LISA_DVP_INPUT_FORM_YUV422_Y0CBY1CR;
}
}
/* DVP 驱动上下文结构体 */
typedef struct {
lisa_device_t *dvp_dev; /* DVP 设备句柄 */
uint8_t gpdma_ch; /* DMA 通道 */
lisa_camera_frame_callback_t callback; /* 帧完成回调 */
lisa_camera_get_free_fb_t get_free_fb; /* 获取空闲帧回调 */
lisa_camera_get_free_fb_from_isr_t get_free_fb_isr; /* 获取空闲帧回调(ISR) */
void *user_data; /* 用户数据 */
volatile uint8_t stop_flag; /* 停止标志 */
lisa_camera_fb_t *ping_fb; /* Ping 缓冲区 */
lisa_camera_fb_t *pong_fb; /* Pong 缓冲区 */
lisa_camera_fb_t *current_fb; /* 当前帧缓冲区 */
} camera_bus_dvp_priv_t;
static camera_bus_dvp_priv_t camera_bus_dvp_priv = {
.dvp_dev = NULL,
.gpdma_ch = 0,
.callback = NULL,
.get_free_fb = NULL,
.get_free_fb_isr = NULL,
.user_data = NULL,
.stop_flag = 1,
#ifdef DVP_USE_PINGPONG_DMA
.ping_fb = NULL,
.pong_fb = NULL,
#else
.current_fb = NULL,
#endif
};
static void dvp_event_callback(lisa_dvp_event_t event, void *user_data)
{
camera_bus_dvp_priv_t *priv = (camera_bus_dvp_priv_t *)user_data;
if (priv->stop_flag) {
return;
}
LOGI("%s: event:%d", __func__, event);
lisa_camera_fb_t *completed_fb = NULL;
lisa_camera_fb_t *next_fb = NULL;
if (event & LISA_DVP_EVENT_PING_DONE) {
/* Ping 块完成 */
completed_fb = priv->ping_fb;
/* 通过回调获取新的帧缓冲区替换 Ping */
if (priv->get_free_fb_isr) {
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区替换 Ping */
priv->ping_fb = next_fb;
/* 重新加载 Ping 缓冲区 */
lisa_dvp_reload_pingpong(priv->dvp_dev, next_fb->buf);
} else {
/* 没有空闲缓冲区,复用当前 Ping 缓冲区继续接收,丢弃本帧数据 */
LOGW("No free fb for ping reload, reuse current buffer and drop frame");
lisa_dvp_reload_pingpong(priv->dvp_dev, priv->ping_fb->buf);
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
} else if (event & LISA_DVP_EVENT_PONG_DONE) {
/* Pong 块完成 */
completed_fb = priv->pong_fb;
/* 通过回调获取新的帧缓冲区替换 Pong */
if (priv->get_free_fb_isr) {
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区替换 Pong */
priv->pong_fb = next_fb;
/* 重新加载 Pong 缓冲区 */
lisa_dvp_reload_pingpong(priv->dvp_dev, next_fb->buf);
} else {
/* 没有空闲缓冲区,复用当前 Pong 缓冲区继续接收,丢弃本帧数据 */
LOGW("No free fb for pong reload, reuse current buffer and drop frame");
lisa_dvp_reload_pingpong(priv->dvp_dev, priv->pong_fb->buf);
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
}
else if (event & LISA_DVP_EVENT_DONE) {
/* 普通传输完成 */
completed_fb = priv->current_fb;
/* 通过回调获取下一个空闲帧缓冲区并继续接收 */
if (priv->get_free_fb_isr) {
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区继续接收 */
priv->current_fb = next_fb;
lisa_dvp_reload(priv->dvp_dev, priv->current_fb->buf, priv->current_fb->len);
} else {
/* 没有空闲缓冲区,复用当前缓冲区继续接收,丢弃本帧数据 */
LOGW("No free frame buffer available, reuse current buffer and drop frame");
lisa_dvp_reload(priv->dvp_dev, priv->current_fb->buf, priv->current_fb->len);
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
}
/* 通知上层帧已完成 (仅当成功获取到新缓冲区时才交付数据) */
if (completed_fb && priv->callback) {
priv->callback(completed_fb, priv->user_data);
}
}
static int lisa_camera_bus_dvp_init(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config)
{
int32_t ret = 0;
const lisa_camera_bus_dvp_config_t *dvp_config = &bus_config->config.dvp;
camera_bus_dvp_priv_t *priv = (camera_bus_dvp_priv_t *)dev->priv_data;
priv->gpdma_ch = bus_config->dma_channel;
priv->dvp_dev = dvp_config->dvp_dev;
lisa_dvp_config_t dvp_cfg = {
.dvp_hal_config = {
.frame_width = bus_config->width,
.frame_height = bus_config->height,
.pixel_offset = dvp_config->pixel_offset,
.line_offset = dvp_config->line_offset,
.input_format = convert_to_dvp_pixel_format(bus_config->pixel_format),
.pclk_polarity = (dvp_config->pclk_polarity == 1) ? LISA_DVP_POL_RISING : LISA_DVP_POL_FALLING,
.vsync_polarity = (dvp_config->vsync_polarity == 1) ? LISA_DVP_POL_RISING : LISA_DVP_POL_FALLING,
.hsync_polarity = (dvp_config->hsync_polarity == 1) ? LISA_DVP_POL_RISING : LISA_DVP_POL_FALLING,
.data_align = (dvp_config->data_align == 1) ? LISA_DVP_DATA_ALIGN_LEFT : LISA_DVP_DATA_ALIGN_RIGHT,
},
.gpdma_ch = bus_config->dma_channel,
};
ret = lisa_dvp_setup(priv->dvp_dev, &dvp_cfg, dvp_event_callback, priv);
if (ret != 0) {
LOGE("lisa_dvp_setup failed %d", ret);
return -1;
}
ret = lisa_dvp_enable_clockout(priv->dvp_dev, dvp_config->dvp_freq);
if (ret != 0) {
LOGE("lisa_dvp_enable_clockout failed %d", ret);
return -1;
}
return 0;
}
static int lisa_camera_bus_dvp_start_capture(lisa_device_t *dev, lisa_camera_frame_callback_t callback,
lisa_camera_get_free_fb_t get_free_fb,
lisa_camera_get_free_fb_from_isr_t get_free_fb_from_isr,
void *data)
{
int32_t ret = 0;
camera_bus_dvp_priv_t *priv = (camera_bus_dvp_priv_t *)dev->priv_data;
if (!priv->stop_flag) {
LOGW("camera dvp already start.");
return 0;
}
/* 保存回调函数 */
priv->callback = callback;
priv->get_free_fb = get_free_fb;
priv->get_free_fb_isr = get_free_fb_from_isr;
priv->user_data = data;
#ifdef DVP_USE_PINGPONG_DMA
/* PingPong 模式:通过回调获取两个缓冲区 */
priv->ping_fb = get_free_fb ? get_free_fb(data) : NULL;
if (!priv->ping_fb) {
LOGE("No free frame buffer for ping");
return -1;
}
priv->pong_fb = get_free_fb ? get_free_fb(data) : NULL;
if (!priv->pong_fb) {
LOGE("No free frame buffer for pong");
lisa_camera_release_fb(dev, priv->ping_fb);
priv->ping_fb = NULL;
return -1;
}
priv->stop_flag = 0;
/* 启动 PingPong DVP 传输 */
ret = lisa_dvp_start_pingpong(priv->dvp_dev, priv->ping_fb->buf, priv->pong_fb->buf, priv->ping_fb->len);
if (ret != 0) {
LOGE("lisa_dvp_start_pingpong failed %d", ret);
priv->stop_flag = 1;
return -1;
}
LOGI("DVP PingPong capture started");
#else
/* 普通模式:通过回调获取一个缓冲区 */
priv->current_fb = get_free_fb ? get_free_fb(data) : NULL;
if (!priv->current_fb) {
LOGE("No free frame buffer available");
return -1;
}
priv->stop_flag = 0;
/* 启动普通 DVP 传输 */
ret = lisa_dvp_start(priv->dvp_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != 0) {
LOGE("lisa_dvp_start failed %d", ret);
priv->stop_flag = 1;
return -1;
}
LOGI("DVP Normal capture started");
#endif
return 0;
}
static int lisa_camera_bus_dvp_stop_capture(lisa_device_t *dev)
{
camera_bus_dvp_priv_t *priv = (camera_bus_dvp_priv_t *)dev->priv_data;
if (priv->stop_flag) {
LOGW("camera dvp already stop.");
return 0;
}
priv->stop_flag = 1;
lisa_dvp_stop(priv->dvp_dev);
#ifdef DVP_USE_PINGPONG_DMA
/* 释放 Ping 缓冲区 */
if (priv->ping_fb != NULL) {
lisa_camera_release_fb(dev, priv->ping_fb);
priv->ping_fb = NULL;
}
/* 释放 Pong 缓冲区 */
if (priv->pong_fb != NULL) {
lisa_camera_release_fb(dev, priv->pong_fb);
priv->pong_fb = NULL;
}
LOGI("DVP PingPong capture stopped");
#else
/* 释放当前帧缓冲区 */
if (priv->current_fb != NULL) {
lisa_camera_release_fb(dev, priv->current_fb);
priv->current_fb = NULL;
}
LOGI("DVP Normal capture stopped");
#endif
return 0;
}
const lisa_camera_bus_if_t lisa_camera_bus_dvp_if = {
.init = lisa_camera_bus_dvp_init,
.start_capture = lisa_camera_bus_dvp_start_capture,
.stop_capture = lisa_camera_bus_dvp_stop_capture,
};
static int camera_bus_dvp_init(void)
{
return 0;
}
LISA_DEVICE_REGISTER(camera_bus, &lisa_camera_bus_dvp_if, &camera_bus_dvp_priv, NULL, camera_bus_dvp_init, LISA_DEVICE_PRIORITY_HIGH);

View File

@@ -0,0 +1,339 @@
#include <stdint.h>
#include <string.h>
#include "lisa_device.h"
#include "lisa_spi.h"
#include <lisa_semaphore.h>
#include "Driver_SPI.h"
#include "Driver_GPIO.h"
#include "lisa_gpio.h"
#include "lisa_camera_bus.h"
#include "dma.h"
#define TAG "camera_bus_spi"
#include "lisa_log.h"
/* SPI 驱动上下文结构体 */
typedef struct {
void *spi_dev; /* SPI 设备句柄 */
lisa_device_t *cs_gpio_dev; /* CS GPIO lisa 设备 */
uint8_t cs_pin; /* CS 引脚 */
uint8_t dma_channel; /* DMA 通道 */
lisa_semaphore_t *stop_sem; /* 停止信号量 */
lisa_camera_frame_callback_t callback; /* 帧完成回调 */
lisa_camera_get_free_fb_t get_free_fb; /* 获取空闲帧回调 */
lisa_camera_get_free_fb_from_isr_t get_free_fb_isr; /* 获取空闲帧回调(ISR) */
void *user_data; /* 用户数据 */
lisa_camera_fb_t *current_fb; /* 当前帧缓冲区 */
volatile uint8_t stop_flag; /* 停止标志 */
} caemra_bus_spi_priv_t;
static caemra_bus_spi_priv_t camera_bus_spi_priv = {
.spi_dev = NULL,
.cs_gpio_dev = NULL,
.cs_pin = 0,
.dma_channel = 0,
.stop_sem = NULL,
.callback = NULL,
.get_free_fb = NULL,
.get_free_fb_isr = NULL,
.user_data = NULL,
.current_fb = NULL,
.stop_flag = 1,
};
static void _spi_drv_event(uint32_t event, uint32_t usr_param)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)usr_param;
lisa_camera_fb_t *completed_fb = NULL;
lisa_camera_fb_t *next_fb = NULL;
if (event != CSK_SPI_EVENT_TRANSFER_COMPLETE) {
LOGW("spi event: %x", (unsigned int)event);
}
if ((event & CSK_SPI_EVENT_TRANSFER_COMPLETE) && !priv->cs_gpio_dev) {
/* 有 CS 模式: SPI 传输完成即表示一帧结束 */
if (priv->stop_flag) {
lisa_semaphore_give(priv->stop_sem);
return;
}
completed_fb = priv->current_fb;
/* 通过回调获取下一个空闲帧缓冲区并继续接收 */
if (priv->get_free_fb_isr) {
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区继续接收 */
priv->current_fb = next_fb;
SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
} else {
/* 没有空闲缓冲区,复用当前缓冲区继续接收,丢弃本帧数据 */
LOGW("No free frame buffer available, reuse current buffer and drop frame");
SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
/* 通知上层帧已完成 (仅当成功获取到新缓冲区时才交付数据) */
if (priv->callback && completed_fb) {
// completed_fb->len = SPI_GetDataCount(priv->spi_dev);
priv->callback(completed_fb, priv->user_data);
}
}
}
/* NOCS 模式: CS 引脚控制回调 */
_FAST_TEXT static void _spi_cs_set_cb(void *spi_dev, uint8_t level)
{
SPI_Pull_CS(spi_dev, level);
}
/* NOCS 模式: GPIO 中断处理函数,检测 CS 上升沿表示帧结束 */
_FAST_TEXT static void _gpio_drv_event(uint32_t pin, void *user_data)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)user_data;
lisa_camera_fb_t *completed_fb = NULL;
lisa_camera_fb_t *next_fb = NULL;
if (priv->stop_flag) {
lisa_gpio_disable_irq(priv->cs_gpio_dev, priv->cs_pin);
lisa_semaphore_give(priv->stop_sem);
return;
}
/* 获取实际接收的数据长度 */
if (priv->current_fb) {
// priv->current_fb->len = SPI_GetDataCount(priv->spi_dev);
}
SPI_Control(priv->spi_dev, CSK_SPI_ABORT_TRANSFER, 0);
completed_fb = priv->current_fb;
/* 通过回调获取下一个空闲帧缓冲区 */
if (priv->get_free_fb_isr) {
int ret;
next_fb = priv->get_free_fb_isr(priv->user_data);
if (next_fb) {
/* 有空闲缓冲区,使用新缓冲区继续接收 */
priv->current_fb = next_fb;
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
}
} else {
/* 没有空闲缓冲区,复用当前缓冲区继续接收,丢弃本帧数据 */
LOGW("No free frame buffer available, reuse current buffer and drop frame");
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
}
completed_fb = NULL; /* 不通知上层,丢弃本帧 */
}
}
/* 通知上层帧已完成 (仅当成功获取到新缓冲区时才交付数据) */
if (priv->callback && completed_fb) {
priv->callback(completed_fb, priv->user_data);
}
}
static int lisa_camera_bus_spi_init(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config)
{
int ret = 0;
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
const lisa_camera_bus_spi_config_t *spi_config = &bus_config->config.spi;
if (bus_config->bus_type != LISA_CAMERA_BUS_SPI) {
LOGE("Invalid bus type %d", bus_config->bus_type);
return LISA_DEVICE_ERR_INVALID;
}
if (strcmp(spi_config->spi_dev->name, "spi0") == 0) {
priv->spi_dev = SPI0();
}
else if (strcmp(spi_config->spi_dev->name, "spi1") == 0) {
priv->spi_dev = SPI1();
}
else if (strcmp(spi_config->spi_dev->name, "spi2") == 0) {
priv->spi_dev = SPI2();
}
priv->dma_channel = bus_config->dma_channel;
priv->stop_sem = lisa_semaphore_create(1);
SPI_Uninitialize(priv->spi_dev);
if (bus_config->config.spi.cs_gpio) {
ret = SPI_Initialize_NCS(priv->spi_dev, _spi_drv_event, priv, _spi_cs_set_cb);
}
else {
ret = SPI_Initialize(priv->spi_dev, _spi_drv_event, priv);
}
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Initialize failed %d", ret);
return ret;
}
ret = SPI_PowerControl(priv->spi_dev, CSK_POWER_FULL);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_PowerControl failed %d", ret);
return ret;
}
uint32_t control = CSK_SPI_MODE_SLAVE | CSK_SPI_DATA_BITS(8);
if (spi_config->spi_bit_order == 0) {
control |= CSK_SPI_MSB_LSB; /* MSB first */
}
else {
control |= CSK_SPI_LSB_MSB; /* LSB first */
}
switch (spi_config->spi_mode) {
case 0:
control |= CSK_SPI_CPOL0_CPHA0;
break;
case 1:
control |= CSK_SPI_CPOL0_CPHA1;
break;
case 2:
control |= CSK_SPI_CPOL1_CPHA0;
break;
case 3:
control |= CSK_SPI_CPOL1_CPHA1;
break;
default:
LOGE("Invalid SPI mode %d", spi_config->spi_mode);
return LISA_DEVICE_ERR_INVALID;
}
ret = SPI_Control(priv->spi_dev, control, spi_config->spi_freq);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Control failed %d", ret);
return ret;
}
SPI_ADV_ATTR attr = {0};
attr.flags = SPI_ATTR_RX_NSYNCA | SPI_ATTR_RX_DMACH_PRIO | SPI_ATTR_RX_DMACH_RSVD;
attr.rx_nsynca = 1;
attr.rx_dmach_prio = 6;
attr.rx_dmach_rsvd = priv->dma_channel;
ret = SPI_Control(priv->spi_dev, CSK_SPI_SET_ADV_ATTR, (uint32_t)&attr);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Control ADV_ATTR failed %d", ret);
return ret;
}
dma_channel_unreserve(attr.rx_dmach_rsvd);
if (spi_config->cs_gpio) {
SPI_Enable_Pull_CS(priv->spi_dev, spi_config->cs_pin);
priv->cs_gpio_dev = spi_config->cs_gpio;
priv->cs_pin = spi_config->cs_pin;
lisa_gpio_configure(spi_config->cs_gpio, spi_config->cs_pin, LISA_GPIO_CONFIG_INPUT_PULLUP);
lisa_gpio_configure_irq(spi_config->cs_gpio, spi_config->cs_pin, LISA_GPIO_IRQ_EDGE_RISING,
_gpio_drv_event, priv);
}
LOGI("SPI camera bus init success (NOCS=%d)", spi_config->cs_pin);
return ret;
}
static int lisa_camera_bus_spi_start_capture(lisa_device_t *dev, lisa_camera_frame_callback_t callback,
lisa_camera_get_free_fb_t get_free_fb,
lisa_camera_get_free_fb_from_isr_t get_free_fb_from_isr,
void *data)
{
int ret = 0;
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
if (!priv->stop_flag) {
LOGW("camera spi already start.");
return 0;
}
/* 保存回调函数 */
priv->callback = callback;
priv->get_free_fb = get_free_fb;
priv->get_free_fb_isr = get_free_fb_from_isr;
priv->user_data = data;
/* 通过回调获取第一个空闲帧缓冲区 */
priv->current_fb = get_free_fb ? get_free_fb(data) : NULL;
if (!priv->current_fb) {
LOGE("No free frame buffer available");
return -1;
}
priv->stop_flag = 0;
SPI_ADV_ATTR attr = {
.flags = SPI_ATTR_RX_DMACH_RSVD,
.rx_dmach_rsvd = priv->dma_channel,
};
SPI_Control(priv->spi_dev, CSK_SPI_SET_ADV_ATTR, (uint32_t)&attr);
if (priv->cs_gpio_dev) {
/* NOCS 模式: 使用 SPI_Receive_NEnd 并启用 GPIO 中断 */
ret = SPI_Receive_NEnd(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive_NEnd failed %d", ret);
priv->stop_flag = 1;
return ret;
}
if (priv->cs_gpio_dev) {
lisa_gpio_enable_irq(priv->cs_gpio_dev, priv->cs_pin);
}
}
else {
/* 有 CS 模式: 使用 SPI_Receive */
ret = SPI_Receive(priv->spi_dev, priv->current_fb->buf, priv->current_fb->len);
if (ret != CSK_DRIVER_OK) {
LOGE("SPI_Receive failed %d", ret);
priv->stop_flag = 1;
}
}
return ret;
}
static int lisa_camera_bus_spi_stop_capture(lisa_device_t *dev)
{
caemra_bus_spi_priv_t *priv = (caemra_bus_spi_priv_t*)dev->priv_data;
if (priv->stop_flag) {
LOGW("camera spi already stop.");
return 0;
}
priv->stop_flag = 1;
if (priv->cs_gpio_dev) {
/* NOCS 模式: 等待 GPIO 中断处理完成 */
lisa_semaphore_take(priv->stop_sem, LISA_WAIT_FOREVER);
}
SPI_Control(priv->spi_dev, CSK_SPI_ABORT_TRANSFER, 0);
if (priv->current_fb != NULL) {
lisa_camera_release_fb(dev, priv->current_fb);
priv->current_fb = NULL;
}
dma_channel_unreserve(priv->dma_channel);
return 0;
}
const lisa_camera_bus_if_t lisa_camera_bus_spi_if = {
.init = lisa_camera_bus_spi_init,
.start_capture = lisa_camera_bus_spi_start_capture,
.stop_capture = lisa_camera_bus_spi_stop_capture,
};
static int camera_bus_spi_init(void)
{
return 0;
}
LISA_DEVICE_REGISTER(camera_bus, &lisa_camera_bus_spi_if, &camera_bus_spi_priv, NULL, camera_bus_spi_init, LISA_DEVICE_PRIORITY_HIGH);

View File

@@ -0,0 +1,559 @@
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_camera.h
* @brief LISA Camera 摄像头设备驱动接口
*/
#pragma once
#include "lisa_device.h"
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/* ========================================================================
* Camera 类型定义
* ======================================================================== */
/**
* @brief Camera 像素格式
*/
typedef enum {
LISA_CAMERA_PIXFMT_RGB565 = 0, /* RGB565 格式 (2 字节/像素) */
LISA_CAMERA_PIXFMT_RGB888 = 1, /* RGB888 格式 (3 字节/像素) */
LISA_CAMERA_PIXFMT_YUV422 = 2, /* YUV422 格式 (2 字节/像素) */
LISA_CAMERA_PIXFMT_YUV420 = 3, /* YUV420 格式 (1.5 字节/像素) */
LISA_CAMERA_PIXFMT_GRAY = 4, /* 灰度格式 (1 字节/像素) */
LISA_CAMERA_PIXFMT_JPEG = 5, /* JPEG 压缩格式 */
LISA_CAMERA_PIXFMT_RAW = 6, /* RAW 格式 (Bayer) */
} lisa_camera_pixel_format_t;
/**
* @brief Camera 分辨率
*/
typedef enum {
LISA_CAMERA_FRAMESIZE_QQVGA = 0, /* 160x120 */
LISA_CAMERA_FRAMESIZE_QCIF = 1, /* 176x144 */
LISA_CAMERA_FRAMESIZE_QVGA = 2, /* 320x240 */
LISA_CAMERA_FRAMESIZE_CIF = 3, /* 400x296 */
LISA_CAMERA_FRAMESIZE_VGA = 4, /* 640x480 */
LISA_CAMERA_FRAMESIZE_SVGA = 5, /* 800x600 */
LISA_CAMERA_FRAMESIZE_XGA = 6, /* 1024x768 */
LISA_CAMERA_FRAMESIZE_HD = 7, /* 1280x720 */
LISA_CAMERA_FRAMESIZE_SXGA = 8, /* 1280x1024 */
LISA_CAMERA_FRAMESIZE_UXGA = 9, /* 1600x1200 */
LISA_CAMERA_FRAMESIZE_FHD = 10, /* 1920x1080 */
} lisa_camera_framesize_t;
/**
* @brief Camera 总线接口类型
*/
typedef enum {
LISA_CAMERA_BUS_DVP = 0, /* DVP (Digital Video Port) 并行接口 */
LISA_CAMERA_BUS_SPI = 1, /* SPI 串行接口 */
} lisa_camera_bus_type_e;
/**
* @brief Camera DVP 总线配置
*/
typedef struct {
lisa_device_t *dvp_dev; /* DVP设备指针 */
uint32_t dvp_freq; /* DVP频率 */
uint16_t pixel_offset; /* 像素偏移 */
uint16_t line_offset; /* 行偏移 */
uint8_t pclk_polarity; /* 像素时钟极性 (0: 下降沿, 1: 上升沿) */
uint8_t vsync_polarity; /* 垂直同步极性 (0: 低电平, 1: 高电平) */
uint8_t hsync_polarity; /* 水平同步极性 (0: 低电平, 1: 高电平) */
uint8_t data_align; /* 数据对齐方式 (0: right aligned[bit7~0], 1: left aligned[bit11~4])*/
lisa_device_t *dma_dev; /* DMA设备指针可选*/
} lisa_camera_bus_dvp_config_t;
/**
* @brief Camera SPI 总线配置
*/
typedef struct {
lisa_device_t *spi_dev; /* SPI设备指针 */
uint32_t spi_freq; /* SPI频率 */
uint8_t spi_mode; /* SPI模式 (0-3) */
uint8_t spi_bit_order; /* SPI位序 (0: MSB, 1: LSB) */
lisa_device_t *cs_gpio; /* 片选GPIO设备可选*/
uint32_t cs_pin; /* 片选引脚号 */
lisa_device_t *dma_dev; /* DMA设备指针可选*/
} lisa_camera_bus_spi_config_t;
/**
* @brief Camera 总线配置联合体
*/
typedef union {
lisa_camera_bus_dvp_config_t dvp;
lisa_camera_bus_spi_config_t spi;
} lisa_camera_bus_config_u;
/**
* @brief Camera 总线配置结构体
*/
typedef struct {
lisa_camera_bus_type_e bus_type; /* 总线类型 */
lisa_camera_bus_config_u config; /* 总线配置 */
lisa_camera_pixel_format_t pixel_format; /* 像素格式 */
uint8_t dma_channel; /* DMA通道号 */
uint16_t width; /* 图像宽度 */
uint16_t height; /* 图像高度 */
} lisa_camera_bus_config_t;
/**
* @brief Camera 硬件配置结构体
*/
typedef struct {
uint8_t mclk_pad;
uint8_t mclk_pin;
lisa_device_t *pwdn_gpio_dev; /* PWDN GPIO 设备指针 */
uint8_t pwdn_pin; /* PWDN 引脚号 */
uint32_t pwdn_delay_us; /* PWDN 延时 (微秒) */
uint32_t xclk_delay_us; /* 时钟输出后延时 (微秒) */
lisa_device_t *i2c_dev; /* I2C 设备 */
} lisa_camera_hw_config_t;
/**
* @brief Camera 配置结构体
*/
typedef struct {
lisa_camera_hw_config_t hw_config; /* 硬件配置 */
uint32_t xclk_freq_hz; /* 外部时钟频率 (Hz) */
uint8_t jpeg_quality; /* JPEG质量 (0-100, 仅JPEG格式有效) */
uint8_t fb_count; /* 帧缓冲区数量 */
bool enable_hmirror; /* 水平镜像 */
bool enable_vflip; /* 垂直翻转 */
bool enable_colorbar; /* 测试模式 (colorbar) */
} lisa_camera_config_t;
/**
* @brief Camera 帧缓冲区结构体
*/
typedef struct {
uint8_t *buf; /* 缓冲区指针 */
uint32_t len; /* 数据长度(字节)*/
uint16_t width; /* 图像宽度 */
uint16_t height; /* 图像高度 */
lisa_camera_pixel_format_t format; /* 像素格式 */
uint32_t timestamp; /* 时间戳(毫秒)*/
} lisa_camera_fb_t;
/**
* @brief Camera 裁剪区域
*/
typedef struct {
int16_t x; /* 起始 X 坐标 */
int16_t y; /* 起始 Y 坐标 */
uint16_t width; /* 裁剪宽度 */
uint16_t height; /* 裁剪高度 */
} lisa_camera_crop_t;
/**
* @brief Camera 能力结构体
*/
typedef struct {
uint16_t max_width; /* 最大宽度 */
uint16_t max_height; /* 最大高度 */
uint32_t supported_formats; /* 支持的像素格式位掩码 */
} lisa_camera_capabilities_t;
/**
* @brief Camera 帧回调函数类型
*
* @param fb 帧缓冲区指针
* @param user_data 用户数据指针
*
* @note 如果需要访问设备,可以通过 user_data 传入设备指针
*/
typedef void (*lisa_camera_frame_callback_t)(const lisa_camera_fb_t *fb, void *user_data);
/* ========================================================================
* Camera 设备 API 结构体
* ======================================================================== */
typedef struct {
int (*setup)(lisa_device_t *dev, const lisa_camera_config_t *config);
int (*start)(lisa_device_t *dev);
int (*stop)(lisa_device_t *dev);
int (*capture)(lisa_device_t *dev, lisa_camera_fb_t **fb);
int (*release_fb)(lisa_device_t *dev, lisa_camera_fb_t *fb);
int (*get_capabilities)(lisa_device_t *dev, lisa_camera_capabilities_t *caps);
int (*attach_bus)(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config);
int (*set_hmirror)(lisa_device_t *dev, bool enable);
int (*set_vflip)(lisa_device_t *dev, bool enable);
int (*set_crop)(lisa_device_t *dev, const lisa_camera_crop_t *crop);
int (*get_framesize)(lisa_device_t *dev, uint16_t *width, uint16_t *height);
int (*set_pixformat)(lisa_device_t *dev, lisa_camera_pixel_format_t format);
int (*set_reg)(lisa_device_t *dev, int reg, int mask, int value);
int (*get_reg)(lisa_device_t *dev, int reg, int mask);
int (*set_callback)(lisa_device_t *dev, lisa_camera_frame_callback_t callback, void *user_data);
lisa_camera_pixel_format_t (*get_pixformat)(lisa_device_t *dev);
} lisa_camera_api_t;
/* ========================================================================
* Camera 对外接口函数
* ======================================================================== */
/**
* @brief 配置摄像头设备
*
* @param dev Camera设备指针
* @param config 配置参数
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_setup(lisa_device_t *dev, const lisa_camera_config_t *config)
{
if (!dev || !dev->api || !config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->setup ? api->setup(dev, config) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 启动摄像头
*
* @param dev Camera设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_start(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->start ? api->start(dev) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 停止摄像头
*
* @param dev Camera设备指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_stop(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->stop ? api->stop(dev) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 捕获一帧图像
*
* @param dev Camera设备指针
* @param fb 输出参数,帧缓冲区指针的指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 捕获后需要调用 lisa_camera_release_fb() 释放帧缓冲区
*/
static inline int lisa_camera_capture(lisa_device_t *dev, lisa_camera_fb_t **fb)
{
if (!dev || !dev->api || !fb) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->capture ? api->capture(dev, fb) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 释放帧缓冲区
*
* @param dev Camera设备指针
* @param fb 帧缓冲区指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_release_fb(lisa_device_t *dev, lisa_camera_fb_t *fb)
{
if (!dev || !dev->api || !fb) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->release_fb ? api->release_fb(dev, fb) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 获取摄像头设备能力
*
* @param dev Camera设备指针
* @param caps 输出参数,用于接收设备能力信息
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_get_capabilities(lisa_device_t *dev, lisa_camera_capabilities_t *caps)
{
if (!dev || !dev->api || !caps) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->get_capabilities ? api->get_capabilities(dev, caps) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 附加总线接口
*
* 用于配置摄像头总线接口DVP/SPI等及相关引脚。
*
* @param dev Camera设备指针
* @param bus_config 总线配置结构体
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_attach_bus(lisa_device_t *dev, const lisa_camera_bus_config_t *bus_config)
{
if (!dev || !dev->api || !bus_config) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->attach_bus ? api->attach_bus(dev, bus_config) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置水平镜像
*
* @param dev Camera设备指针
* @param enable 是否启用水平镜像
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_set_hmirror(lisa_device_t *dev, bool enable)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_hmirror ? api->set_hmirror(dev, enable) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置垂直翻转
*
* @param dev Camera设备指针
* @param enable 是否启用垂直翻转
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_set_vflip(lisa_device_t *dev, bool enable)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_vflip ? api->set_vflip(dev, enable) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置裁剪区域
*
* 设置摄像头的裁剪窗口,只输出指定区域的图像数据。
* 可以节省内存和带宽,提高帧率。
*
* @param dev Camera设备指针
* @param crop 裁剪区域配置传入NULL则取消裁剪
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*
* @note 裁剪区域必须在当前分辨率范围内
*/
static inline int lisa_camera_set_crop(lisa_device_t *dev, const lisa_camera_crop_t *crop)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_crop ? api->set_crop(dev, crop) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 获取当前帧分辨率
*
* @param dev Camera设备指针
* @param width 输出帧宽度
* @param height 输出帧高度
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_get_framesize(lisa_device_t *dev, uint16_t *width, uint16_t *height)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->get_framesize ? api->get_framesize(dev, width, height) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
static inline lisa_camera_pixel_format_t lisa_camera_get_pixformat(lisa_device_t *dev)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->get_pixformat ? api->get_pixformat(dev) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置像素格式
*
* @param dev Camera设备指针
* @param format 像素格式
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_set_pixformat(lisa_device_t *dev, lisa_camera_pixel_format_t format)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_pixformat ? api->set_pixformat(dev, format) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置camera寄存器
*
* @param dev Camera设备指针
* @param reg 寄存器地址
* @param mask 寄存器掩码
* @param value 寄存器值
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_set_reg(lisa_device_t *dev, int reg, int mask, int value)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_reg ? api->set_reg(dev, reg, mask, value) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 获取camera寄存器的值
*
* @param dev Camera设备指针
* @param reg 寄存器地址
* @param mask 寄存器掩码
*
* @return 寄存器值
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_get_reg(lisa_device_t *dev, int reg, int mask)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->get_reg ? api->get_reg(dev, reg, mask) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/**
* @brief 设置帧回调函数
*
* @param dev Camera设备指针
* @param callback 回调函数指针
* @param user_data 用户数据指针
*
* @return 0 成功
* @return LISA_DEVICE_ERR_INVALID 参数无效
* @return LISA_DEVICE_ERR_NOT_SUPPORT 不支持该操作
* @return <0 其他错误
*/
static inline int lisa_camera_set_callback(lisa_device_t *dev, lisa_camera_frame_callback_t callback, void *user_data)
{
if (!dev || !dev->api) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_camera_api_t *api = (lisa_camera_api_t *)dev->api;
return api->set_callback ? api->set_callback(dev, callback, user_data) : LISA_DEVICE_ERR_NOT_SUPPORT;
}
/* ========================================================================
* 便捷宏定义
* ======================================================================== */
/**
* @brief 像素格式支持位掩码
*/
#define LISA_CAMERA_PIXFMT_MASK_RGB565 (1 << LISA_CAMERA_PIXFMT_RGB565)
#define LISA_CAMERA_PIXFMT_MASK_RGB888 (1 << LISA_CAMERA_PIXFMT_RGB888)
#define LISA_CAMERA_PIXFMT_MASK_YUV422 (1 << LISA_CAMERA_PIXFMT_YUV422)
#define LISA_CAMERA_PIXFMT_MASK_YUV420 (1 << LISA_CAMERA_PIXFMT_YUV420)
#define LISA_CAMERA_PIXFMT_MASK_GRAY (1 << LISA_CAMERA_PIXFMT_GRAY)
#define LISA_CAMERA_PIXFMT_MASK_JPEG (1 << LISA_CAMERA_PIXFMT_JPEG)
#define LISA_CAMERA_PIXFMT_MASK_RAW (1 << LISA_CAMERA_PIXFMT_RAW)
/**
* @brief 分辨率支持位掩码
*/
#define LISA_CAMERA_FRAMESIZE_MASK_QQVGA (1 << LISA_CAMERA_FRAMESIZE_QQVGA)
#define LISA_CAMERA_FRAMESIZE_MASK_QCIF (1 << LISA_CAMERA_FRAMESIZE_QCIF)
#define LISA_CAMERA_FRAMESIZE_MASK_QVGA (1 << LISA_CAMERA_FRAMESIZE_QVGA)
#define LISA_CAMERA_FRAMESIZE_MASK_CIF (1 << LISA_CAMERA_FRAMESIZE_CIF)
#define LISA_CAMERA_FRAMESIZE_MASK_VGA (1 << LISA_CAMERA_FRAMESIZE_MASK_VGA)
#define LISA_CAMERA_FRAMESIZE_MASK_SVGA (1 << LISA_CAMERA_FRAMESIZE_SVGA)
#define LISA_CAMERA_FRAMESIZE_MASK_XGA (1 << LISA_CAMERA_FRAMESIZE_XGA)
#define LISA_CAMERA_FRAMESIZE_MASK_HD (1 << LISA_CAMERA_FRAMESIZE_HD)
#define LISA_CAMERA_FRAMESIZE_MASK_SXGA (1 << LISA_CAMERA_FRAMESIZE_SXGA)
#define LISA_CAMERA_FRAMESIZE_MASK_UXGA (1 << LISA_CAMERA_FRAMESIZE_UXGA)
#define LISA_CAMERA_FRAMESIZE_MASK_FHD (1 << LISA_CAMERA_FRAMESIZE_FHD)
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
listenai_library_sources(sensor.c)
listenai_library_sources_ifdef(CONFIG_LISA_CAMERA_SENSOR_BF3901 src/bf3901.c)
listenai_library_sources_ifdef(CONFIG_LISA_CAMERA_SENSOR_GC032A src/gc032a.c)
listenai_library_sources_ifdef(CONFIG_LISA_CAMERA_SENSOR_GC0328 src/gc0328.c)
listenai_include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/
${CMAKE_CURRENT_SOURCE_DIR}/inc
)

View File

@@ -0,0 +1,27 @@
#ifndef __BF20A6_H__
#define __BF20A6_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int bf20a6_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int bf20a6_init(sensor_t *sensor);
#endif // __BF20A6_H__

View File

@@ -0,0 +1,12 @@
/*
* BF20A6 register definitions.
*/
#ifndef __BF20A6_REG_REGS_H__
#define __BF20A6_REG_REGS_H__
#define SENSOR_ID_HIGH 0XFC
#define SENSOR_ID_LOW 0XFD
#define RESET_RELATED 0XF2
#endif //__BF20A6_REG_REGS_H__

View File

@@ -0,0 +1,158 @@
#include <stdint.h>
#define REG_DLY 0xffff
#define REGLIST_TAIL 0xffff /* Array end token */
static const uint16_t bf20a6_default_init_regs[][2] = {
{0xf2,0x01},
{0x12,0x20},
{0x3a,0x00},
{0xe1,0x92},
{0xe3,0x12},// PLL Control, important for framerate(choice: 0x02\0x12\0x22\0x32\0x82)
{0xe0,0x00},
{0x2a,0x98},
{0xcd,0x17},
{0xc0,0x10},
{0xc6,0x1d},
{0x10,0x35},
{0xe2,0x09},
{0xe4,0x72},
{0xe5,0x22},
{0xe6,0x24},
{0xe7,0x64},
{0xe8,0xa2}, // DVP:a2}, SPI:f2 VDDIO=1.8V,E8[2]=1},VDDIO=2.8V,E8[2]=0},
{0x4a,0x00},
{0x00,0x03},
{0x1f,0x02},
{0x22,0x02},
{0x0c,0x31},
{0x00,0x00},
{0x60,0x81},
{0x61,0x81},
{0xa0,0x08},
{0x01,0x1a},
// {0x01,0x1a},
// {0x01,0x1a},
// {0x02,0x15},
// {0x02,0x15},
{0x02,0x15},
{0x13,0x08},
{0x8a,0x96},
{0x8b,0x06},
{0x87,0x18},
{0x34,0x48}, // lens
{0x35,0x40},
{0x36,0x40},
{0x71,0x44},
{0x72,0x48},
{0x74,0xa2},
{0x75,0xa9},
{0x78,0x12},
{0x79,0xa0},
{0x7a,0x94},
{0x7c,0x97},
{0x40,0x30},
{0x41,0x30},
{0x42,0x28},
{0x43,0x1f},
{0x44,0x1c},
{0x45,0x16},
{0x46,0x13},
{0x47,0x10},
{0x48,0x0D},
{0x49,0x0C},
{0x4B,0x0A},
{0x4C,0x0B},
{0x4E,0x09},
{0x4F,0x08},
{0x50,0x08},
{0x5f,0x29},
{0x23,0x33},
{0xa1,0x10}, // AWB
{0xa2,0x0d},
{0xa3,0x30},
{0xa4,0x06},
{0xa5,0x22},
{0xa6,0x56},
{0xa7,0x18},
{0xa8,0x1a},
{0xa9,0x12},
{0xaa,0x12},
{0xab,0x16},
{0xac,0xb1},
{0xba,0x12},
{0xbb,0x12},
{0xad,0x12},
{0xae,0x56},
{0xaf,0x0a},
{0x3b,0x30},
{0x3c,0x12},
{0x3d,0x22},
{0x3e,0x3f},
{0x3f,0x28},
{0xb8,0xc3},
{0xb9,0xa3},
{0x39,0x47}, // pure color threshold
{0x26,0x13},
{0x27,0x16},
{0x28,0x14},
{0x29,0x18},
{0xee,0x0d},
{0x13,0x05},
{0x24,0x3C},
{0x81,0x20},
{0x82,0x40},
{0x83,0x30},
{0x84,0x58},
{0x85,0x30},
{0x92,0x08},
{0x86,0x80},
{0x8a,0x96},
{0x91,0xff},
{0x94,0x62},
{0x9a,0x18}, // outdoor threshold
{0xf0,0x45}, // integral time control, important for framerate(choice: 0x46\0x45\0x44..)
{0x51,0x17}, // color normal
{0x52,0x03},
{0x53,0x5F},
{0x54,0x47},
{0x55,0x66},
{0x56,0x0F},
{0x7e,0x14},
{0x57,0x36}, // color
{0x58,0x2A},
{0x59,0xAA},
{0x5a,0xA8},
{0x5b,0x43},
{0x5c,0x10},
{0x5d,0x00},
{0x7d,0x36},
{0x5e,0x10},
{0xd6,0x88}, // contrast
{0xd5,0x20}, // bright
{0xb0,0x84}, // low light ctrl in gray section
{0xb5,0x08}, // the threshold of GLB_GAIN
{0xb1,0xc8}, // saturation
{0xb2,0xc0},
{0xb3,0xd0},
{0xb4,0xB0},
{0x32,0x10},
// {0x8a,0x00},
// {0x8b,0x10},
{0xa0,0x09},
{0x00,0x03},
{0x0b,0x02},
{REGLIST_TAIL, 0x00},
};

View File

@@ -0,0 +1,33 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* BF3005 driver.
*
*/
#ifndef __BF3005_H__
#define __BF3005_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int bf3005_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int bf3005_init(sensor_t *sensor);
#endif // __BF3005_H__

View File

@@ -0,0 +1,337 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* BF3005 register definitions.
*/
#ifndef __REG_REGS_H__
#define __REG_REGS_H__
#if 0
#define GAIN 0x00 /* AGC ¨C Gain control gain setting */
#define BLUE 0x01 /* AWB ¨C Blue channel gain setting */
#define RED 0x02 /* AWB ¨C Red channel gain setting */
#define GREEN 0x03 /* AWB ¨C Green channel gain setting */
#define BAVG 0x05 /* U/B Average Level */
#define GAVG 0x06 /* Y/Gb Average Level */
#define RAVG 0x07 /* V/R Average Level */
#define AECH 0x08 /* Exposure Value ¨C AEC MSBs */
#define COM2 0x09 /* Common Control 2 */
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
#define REG_PID 0x0A /* Product ID Number MSB */
#define REG_VER 0x0B /* Product ID Number LSB */
#define COM3 0x0C /* Common Control 3 */
#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */
#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */
#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */
#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */
#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */
#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */
#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */
#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */
#define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0))
#define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6))
#define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7))
#define COM4 0x0D /* Common Control 4 */
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
#define COM5 0x0E /* Common Control 5 */
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
#define COM6 0x0F /* Common Control 6 */
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
#define CLKRC 0x11 /* Internal Clock */
#define COM7 0x12 /* Common Control 7 */
#define COM7_RESET 0x80 /* SCCB Register Reset */
#define COM7_RES_VGA 0x00 /* Resolution VGA */
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
#define COM7_FMT_YUV 0x00 /* Output format YUV */
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
#define COM7_FMT_RGB 0x02 /* Output format RGB */
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0))
#define COM7_SET_RGB(r, x) ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
#define COM8 0x13 /* Common Control 8 */
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
#define COM8_AGC_EN 0x04 /* AGC Enable */
#define COM8_AWB_EN 0x02 /* AWB Enable */
#define COM8_AEC_EN 0x01 /* AEC Enable */
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
#define COM9 0x14 /* Common Control 9 */
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
#define COM10 0x15 /* Common Control 10 */
#define COM10_NEGATIVE 0x80 /* Output negative data */
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
#define COM10_HREF_REV 0x08 /* HREF reverse */
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
#define REG16 0x16 /* Register 16 */
#define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
#define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
#define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
#define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
#define REG_MIDH 0x1C /* Manufacturer ID Byte ¨C High */
#define REG_MIDL 0x1D /* Manufacturer ID Byte ¨C Low */
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */
#define COM11 0x20 /* Common Control 11 */
#define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */
#define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */
#define BDBASE 0x22 /* Banding Filter Minimum AEC Value */
#define DBSTEP 0x23 /* Banding Filter Maximum Step */
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
#define REG28 0x28 /* Selection on the number of dummy rows, N */
#define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
#define YAVE 0x2F /* Y/G Channel Average Value */
#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */
#define HREF 0x32 /* Image Start and Size Control */
#define DM_LNL 0x33 /* Dummy Row Low 8 Bits */
#define DM_LNH 0x34 /* Dummy Row High 8 Bits */
#define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */
#define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */
#define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */
#define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */
#define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */
#define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */
#define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */
#define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */
#define COM12 0x3D /* DC offset compensation for analog process */
#define COM13 0x3E /* Common Control 13 */
#define COM13_BLC_EN 0x80 /* BLC enable */
#define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */
#define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */
#define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */
#define COM14 0x3F /* Common Control 14 */
#define COM15 0x40 /* Common Control 15 */
#define COM16 0x41 /* Common Control 16 */
#define TGT_B 0x42 /* BLC Blue Channel Target Value */
#define TGT_R 0x43 /* BLC Red Channel Target Value */
#define TGT_GB 0x44 /* BLC Gb Channel Target Value */
#define TGT_GR 0x45 /* BLC Gr Channel Target Value */
#define LC_CTR 0x46 /* Lens Correction Control */
#define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
#define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers
LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
#define LC_CTR_EN 0x01 /* Lens correction enable */
#define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
#define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
#define LC_COEF 0x49 /* Lens Correction Coefficient */
#define LC_RADI 0x4A /* Lens Correction Radius */
#define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */
#define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */
#define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */
#define AREF0 0x4E /* Sensor Reference Control */
#define AREF1 0x4F /* Sensor Reference Current Control */
#define AREF2 0x50 /* Analog Reference Control */
#define AREF3 0x51 /* ADC Reference Control */
#define AREF4 0x52 /* ADC Reference Control */
#define AREF5 0x53 /* ADC Reference Control */
#define AREF6 0x54 /* Analog Reference Control */
#define AREF7 0x55 /* Analog Reference Control */
#define UFIX 0x60 /* U Channel Fixed Value Output */
#define VFIX 0x61 /* V Channel Fixed Value Output */
#define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */
#define AWB_CTRL0 0x63 /* AWB Control Byte 0 */
#define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */
#define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */
#define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */
#define DSP_CTRL1 0x64 /* DSP Control Byte 1 */
#define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */
#define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */
#define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */
#define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */
#define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */
#define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */
#define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */
#define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */
#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */
#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */
#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */
#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */
#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */
#define DSP_CTRL3 0x66 /* DSP Control Byte 3 */
#define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */
#define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */
#define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */
#define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */
#define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */
#define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */
#define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5))
#define DSP_CTRL4 0x67 /* DSP Control Byte 4 */
#define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */
#define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */
#define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */
#define AWB_BIAS 0x68 /* AWB BLC Level Clip */
#define AWB_CTRL1 0x69 /* AWB Control 1 */
#define AWB_CTRL2 0x6A /* AWB Control 2 */
#define AWB_CTRL3 0x6B /* AWB Control 3 */
#define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */
#define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */
#define AWB_CTRL4 0x6C /* AWB Control 4 */
#define AWB_CTRL5 0x6D /* AWB Control 5 */
#define AWB_CTRL6 0x6E /* AWB Control 6 */
#define AWB_CTRL7 0x6F /* AWB Control 7 */
#define AWB_CTRL8 0x70 /* AWB Control 8 */
#define AWB_CTRL9 0x71 /* AWB Control 9 */
#define AWB_CTRL10 0x72 /* AWB Control 10 */
#define AWB_CTRL11 0x73 /* AWB Control 11 */
#define AWB_CTRL12 0x74 /* AWB Control 12 */
#define AWB_CTRL13 0x75 /* AWB Control 13 */
#define AWB_CTRL14 0x76 /* AWB Control 14 */
#define AWB_CTRL15 0x77 /* AWB Control 15 */
#define AWB_CTRL16 0x78 /* AWB Control 16 */
#define AWB_CTRL17 0x79 /* AWB Control 17 */
#define AWB_CTRL18 0x7A /* AWB Control 18 */
#define AWB_CTRL19 0x7B /* AWB Control 19 */
#define AWB_CTRL20 0x7C /* AWB Control 20 */
#define AWB_CTRL21 0x7D /* AWB Control 21 */
#define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
#define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
#define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
#define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
#define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
#define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
#define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
#define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
#define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
#define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
#define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
#define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
#define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
#define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
#define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
#define SLOP 0x8D /* Gamma Curve Highest Segment Slope */
#define DNSTH 0x8E /* De-noise Threshold */
#define EDGE0 0x8F /* Edge Enhancement Strength Control */
#define EDGE1 0x90 /* Edge Enhancement Threshold Control */
#define DNSOFF 0x91 /* Auto De-noise Threshold Control */
#define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */
#define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */
#define MTX1 0x94 /* Matrix Coefficient 1 */
#define MTX2 0x95 /* Matrix Coefficient 2 */
#define MTX3 0x96 /* Matrix Coefficient 3 */
#define MTX4 0x97 /* Matrix Coefficient 4 */
#define MTX5 0x98 /* Matrix Coefficient 5 */
#define MTX6 0x99 /* Matrix Coefficient 6 */
#define MTX_CTRL 0x9A /* Matrix Control */
#define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */
#define BRIGHTNESS 0x9B /* Brightness Control */
#define CONTRAST 0x9C /* Contrast Gain */
#define UVADJ0 0x9E /* Auto UV Adjust Control 0 */
#define UVADJ1 0x9F /* Auto UV Adjust Control 1 */
#define SCAL0 0xA0 /* DCW Ratio Control */
#define SCAL1 0xA1 /* Horizontal Zoom Out Control */
#define SCAL2 0xA2 /* Vertical Zoom Out Control */
#define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */
#define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */
#define SDE 0xA6 /* Special Digital Effect Control */
#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */
#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */
#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */
#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */
#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */
#define SDE_SATURATION_EN 0x02 /* Saturation enable */
#define SDE_HUE_EN 0x01 /* Hue enable */
#define USAT 0xA7 /* U Component Saturation Gain */
#define VSAT 0xA8 /* V Component Saturation Gain */
#define HUECOS 0xA9 /* Cosine value ¡Á 0x80 */
#define HUESIN 0xAA /* Sine value ¡Á 0x80 */
#define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */
#define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */
#define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */
#define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */
#define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */
#define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */
#define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
#define SET_REG(reg, x) (##reg_DEFAULT|x)
#endif //__REG_REGS_H__
#endif

View File

@@ -0,0 +1,20 @@
#ifndef __DRV_BF3901_H__
#define __DRV_BF3901_H__
#include <stdint.h>
#include "sensor.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CAM_FRAME_WIDTH 96
#define CAM_FRAME_HEIGHT 240
int bf3901_detect(int slv_addr, sensor_id_t *id);
int bf3901_init(sensor_t *sensor);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,31 @@
#pragma once
#include "sensor.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int gc0308_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int gc0308_init(sensor_t *sensor);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,25 @@
/*
* GC0308 register definitions.
*/
#ifndef __GC0308_REG_REGS_H__
#define __GC0308_REG_REGS_H__
#define RESET_RELATED 0xfe // Bit[7]: Software reset
// Bit[6:5]: NA
// Bit[4]: CISCTL_restart_n
// Bit[3:1]: NA
// Bit[0]: page select
// 0:page0
// 1:page1
// page0:
/**
* @brief register value
*/
#endif // __GC0308_REG_REGS_H__

View File

@@ -0,0 +1,258 @@
#ifndef _GC0308_SETTINGS_H_
#define _GC0308_SETTINGS_H_
#include <stdint.h>
#define REG_DLY 0xff
static const uint8_t gc0308_sensor_default_regs[][2] = {
{0xfe, 0x00},
{0xec, 0x20},
{0x05, 0x00},
{0x06, 0x00},
{0x07, 0x00},
{0x08, 0x00},
{0x09, 0x01},
{0x0a, 0xe8},
{0x0b, 0x02},
{0x0c, 0x88},
{0x0d, 0x02},
{0x0e, 0x02},
{0x10, 0x26},
{0x11, 0x0d},
{0x12, 0x2a},
{0x13, 0x00},
{0x14, 0x11},
{0x15, 0x0a},
{0x16, 0x05},
{0x17, 0x01},
{0x18, 0x44},
{0x19, 0x44},
{0x1a, 0x2a},
{0x1b, 0x00},
{0x1c, 0x49},
{0x1d, 0x9a},
{0x1e, 0x61},
{0x1f, 0x00}, //pad drv <=24MHz, use 0x00 is ok
{0x20, 0x7f},
{0x21, 0xfa},
{0x22, 0x57},
{0x24, 0xa2}, //YCbYCr
{0x25, 0x0f},
{0x26, 0x03}, // 0x01
{0x28, 0x00},
{0x2d, 0x0a},
{0x2f, 0x01},
{0x30, 0xf7},
{0x31, 0x50},
{0x32, 0x00},
{0x33, 0x28},
{0x34, 0x2a},
{0x35, 0x28},
{0x39, 0x04},
{0x3a, 0x20},
{0x3b, 0x20},
{0x3c, 0x00},
{0x3d, 0x00},
{0x3e, 0x00},
{0x3f, 0x00},
{0x50, 0x14}, // 0x14
{0x52, 0x41},
{0x53, 0x80},
{0x54, 0x80},
{0x55, 0x80},
{0x56, 0x80},
{0x8b, 0x20},
{0x8c, 0x20},
{0x8d, 0x20},
{0x8e, 0x14},
{0x8f, 0x10},
{0x90, 0x14},
{0x91, 0x3c},
{0x92, 0x50},
//{0x8b,0x10},
//{0x8c,0x10},
//{0x8d,0x10},
//{0x8e,0x10},
//{0x8f,0x10},
//{0x90,0x10},
//{0x91,0x3c},
//{0x92,0x50},
{0x5d, 0x12},
{0x5e, 0x1a},
{0x5f, 0x24},
{0x60, 0x07},
{0x61, 0x15},
{0x62, 0x08}, // 0x08
{0x64, 0x03}, // 0x03
{0x66, 0xe8},
{0x67, 0x86},
{0x68, 0x82},
{0x69, 0x18},
{0x6a, 0x0f},
{0x6b, 0x00},
{0x6c, 0x5f},
{0x6d, 0x8f},
{0x6e, 0x55},
{0x6f, 0x38},
{0x70, 0x15},
{0x71, 0x33},
{0x72, 0xdc},
{0x73, 0x00},
{0x74, 0x02},
{0x75, 0x3f},
{0x76, 0x02},
{0x77, 0x38}, // 0x47
{0x78, 0x88},
{0x79, 0x81},
{0x7a, 0x81},
{0x7b, 0x22},
{0x7c, 0xff},
{0x93, 0x48}, //color matrix default
{0x94, 0x02},
{0x95, 0x07},
{0x96, 0xe0},
{0x97, 0x40},
{0x98, 0xf0},
{0xb1, 0x40},
{0xb2, 0x40},
{0xb3, 0x40}, //0x40
{0xb6, 0xe0},
{0xbd, 0x38},
{0xbe, 0x36},
{0xd0, 0xCB},
{0xd1, 0x10},
{0xd2, 0x90},
{0xd3, 0x48},
{0xd5, 0xF2},
{0xd6, 0x16},
{0xdb, 0x92},
{0xdc, 0xA5},
{0xdf, 0x23},
{0xd9, 0x00},
{0xda, 0x00},
{0xe0, 0x09},
{0xed, 0x04},
{0xee, 0xa0},
{0xef, 0x40},
{0x80, 0x03},
{0x9F, 0x10},
{0xA0, 0x20},
{0xA1, 0x38},
{0xA2, 0x4e},
{0xA3, 0x63},
{0xA4, 0x76},
{0xA5, 0x87},
{0xA6, 0xa2},
{0xA7, 0xb8},
{0xA8, 0xca},
{0xA9, 0xd8},
{0xAA, 0xe3},
{0xAB, 0xeb},
{0xAC, 0xf0},
{0xAD, 0xF8},
{0xAE, 0xFd},
{0xAF, 0xFF},
{0xc0, 0x00},
{0xc1, 0x10},
{0xc2, 0x1c},
{0xc3, 0x30},
{0xc4, 0x43},
{0xc5, 0x54},
{0xc6, 0x65},
{0xc7, 0x75},
{0xc8, 0x93},
{0xc9, 0xB0},
{0xca, 0xCB},
{0xcb, 0xE6},
{0xcc, 0xFF},
{0xf0, 0x02},
{0xf1, 0x01},
{0xf2, 0x02},
{0xf3, 0x30},
{0xf7, 0x04},
{0xf8, 0x02},
{0xf9, 0x9f},
{0xfa, 0x78},
{0xfe, 0x01},
{0x00, 0xf5},
{0x02, 0x20},
{0x04, 0x10},
{0x05, 0x08},
{0x06, 0x20},
{0x08, 0x0a},
{0x0a, 0xa0},
{0x0b, 0x60},
{0x0c, 0x08},
{0x0e, 0x44},
{0x0f, 0x32},
{0x10, 0x41},
{0x11, 0x37},
{0x12, 0x22},
{0x13, 0x19},
{0x14, 0x44},
{0x15, 0x44},
{0x16, 0xc2},
{0x17, 0xA8},
{0x18, 0x18},
{0x19, 0x50},
{0x1a, 0xd8},
{0x1b, 0xf5},
{0x70, 0x40},
{0x71, 0x58},
{0x72, 0x30},
{0x73, 0x48},
{0x74, 0x20},
{0x75, 0x60},
{0x77, 0x20},
{0x78, 0x32},
{0x30, 0x03},
{0x31, 0x40},
{0x32, 0x10},
{0x33, 0xe0},
{0x34, 0xe0},
{0x35, 0x00},
{0x36, 0x80},
{0x37, 0x00},
{0x38, 0x04},
{0x39, 0x09},
{0x3a, 0x12},
{0x3b, 0x1C},
{0x3c, 0x28},
{0x3d, 0x31},
{0x3e, 0x44},
{0x3f, 0x57},
{0x40, 0x6C},
{0x41, 0x81},
{0x42, 0x94},
{0x43, 0xA7},
{0x44, 0xB8},
{0x45, 0xD6},
{0x46, 0xEE},
{0x47, 0x0d},
{0x62, 0xf7},
{0x63, 0x68},
{0x64, 0xd3},
{0x65, 0xd3},
{0x66, 0x60},
{0xfe, 0x00},
{0x01, 0x32}, //frame setting
{0x02, 0x0c},
{0x0f, 0x01},
{0xe2, 0x00},
{0xe3, 0x78},
{0xe4, 0x00},
{0xe5, 0xfe},
{0xe6, 0x01},
{0xe7, 0xe0},
{0xe8, 0x01},
{0xe9, 0xe0},
{0xea, 0x01},
{0xeb, 0xe0},
{0xfe, 0x00},
};
#endif

View File

@@ -0,0 +1,39 @@
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GC0310_H
#define __GC0310_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int gc0310_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int gc0310_init(sensor_t *sensor);
#ifdef __cplusplus
}
#endif
#endif /* __GC0310_H */

View File

@@ -0,0 +1,34 @@
/*
* GC032A register definitions.
*/
#ifndef __GC0310_REG_REGS_H__
#define __GC0310_REG_REGS_H__
#define RESET_RELATED 0XFE // Bit[7]: Software reset
// Bit[6:5]: NA
// Bit[4]: CISCTL_restart_n
// Bit[3:2]: NA
// Bit[1:0]: page select
// 00:page0
// 01:page1
//----page0-----------------------------
#define P0_ROW_START_HIGH 0x51
#define P0_ROW_START_LOW 0x52
#define P0_COLUMN_START_HIGH 0x53
#define P0_COLUMN_START_LOW 0x54
#define P0_WINDOW_HEIGHT_HIGH 0x55
#define P0_WINDOW_HEIGHT_LOW 0x56
#define P0_WINDOW_WIDTH_HIGH 0x57
#define P0_WINDOW_WIDTH_LOW 0x58
#define P0_MIRROR_FLIP 0X17
#define P0_DEBUG_MODE2 0X4C
#define REG_CHIP_ID_H 0XF0
#define REG_CHIP_ID_L 0XF1
#endif //__GC032A_REG_REGS_H__

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/*
*
* GC032A driver.
*
*/
#ifndef __GC0328_H__
#define __GC0328_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int gc0328_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int gc0328_init(sensor_t *sensor);
#endif // __GC032A_H__

View File

@@ -0,0 +1,33 @@
/*
* GC032A register definitions.
*/
#ifndef __GC0328_REG_REGS_H__
#define __GC0328_REG_REGS_H__
#define RESET_RELATED 0XFE // Bit[7]: Software reset
// Bit[6:5]: NA
// Bit[4]: CISCTL_restart_n
// Bit[3:2]: NA
// Bit[1:0]: page select
// 00:page0
// 01:page1
//----page0-----------------------------
#define P0_ROW_START_HIGH 0x51
#define P0_ROW_START_LOW 0x52
#define P0_COLUMN_START_HIGH 0x53
#define P0_COLUMN_START_LOW 0x54
#define P0_WINDOW_HEIGHT_HIGH 0x55
#define P0_WINDOW_HEIGHT_LOW 0x56
#define P0_WINDOW_WIDTH_HIGH 0x57
#define P0_WINDOW_WIDTH_LOW 0x58
#define P0_MIRROR_FLIP 0X17
#define P0_DEBUG_MODE2 0X4C
#define REG_CHIP_ID 0XF0
#endif //__GC032A_REG_REGS_H__

View File

@@ -0,0 +1,319 @@
#ifndef _GC0328_SETTINGS_H_
#define _GC0328_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "gc0328_regs.h"
#define REG_DLY 0xff
/* 15MHz CLK & 15fps */
static const uint8_t gc0328_default_regs[][2] = {
{0xfe,0x80},
{0xfe,0x80},
{0xfc,0x16},
{0xfc,0x16},
{0xfc,0x16},
{0xfc,0x16},
{0xfe,0x00},
{0x4f,0x00},
{0x42,0x00},
{0x03,0x00},
{0x04,0xc0},
{0x77,0x62},
{0x78,0x40},
{0x79,0x4d},
{0x05,0x01},
{0x06,0x32},
{0x07,0x00},
{0x08,0x0c},
{0xfe,0x01},
{0x29,0x00},
{0x2a,0x78},
{0x2b,0x01},
{0x2c,0xe0},
{0x2d,0x01},
{0x2e,0xe0},
{0x2f,0x01},
{0x30,0xe0},
{0x31,0x01},
{0x32,0xe0},
{0xfe,0x00},
{0xfe,0x01},
{0x4f,0x00},
{0x4c,0x01},
{0xfe,0x00},
//////////////////////////////
///////////AWB///////////
////////////////////////////////
{0xfe,0x01,},
{0x51,0x80,},
{0x52,0x12,},
{0x53,0x80,},
{0x54,0x60,},
{0x55,0x01,},
{0x56,0x06,},
{0x5b,0x02,},
{0x61,0xdc,},
{0x62,0xdc,},
{0x7c,0x71,},
{0x7d,0x00,},
{0x76,0x00,},
{0x79,0x20,},
{0x7b,0x00,},
{0x70,0xFF,},
{0x71,0x00,},
{0x72,0x10,},
{0x73,0x40,},
{0x74,0x40,},
////AWB//
{0x50,0x00,},
{0xfe,0x01,},
{0x4f,0x00,},
{0x4c,0x01,},
{0x4f,0x00,},
{0x4f,0x00,},
{0x4f,0x00,},
{0x4d,0x36,},
{0x4e,0x02,},
{0x4e,0x02,},
{0x4d,0x44,},
{0x4e,0x02,},
{0x4e,0x02,},
{0x4e,0x02,},
{0x4e,0x02,},
{0x4d,0x53,},
{0x4e,0x08,},
{0x4e,0x08,},
{0x4e,0x02,},
{0x4d,0x63,},
{0x4e,0x08,},
{0x4e,0x08,},
{0x4d,0x73,},
{0x4e,0x20,},
{0x4d,0x83,},
{0x4e,0x20,},
{0x4f,0x01,},
{0x50,0x88,},
{0xfe,0x00,},
////////////////////////////////////////////////
//////////// BLK //////////////////////
////////////////////////////////////////////////
{0x27,0x00,},
{0x2a,0x40,},
{0x2b,0x40,},
{0x2c,0x40,},
{0x2d,0x40,},
//////////////////////////////////////////////
////////// page 0 ////////////////////////
//////////////////////////////////////////////
{0xfe,0x00,},
{0x09,0x00,},
{0x0a,0x00,},
{0x0b,0x00,},
{0x0c,0x00,},
{0x0d,0x01,},
{0x0e,0xe8,},
{0x0f,0x02,},
{0x10,0x88,},
{0x16,0x00,},
{0x17,0x14,},
{0x18,0x0e,},
{0x19,0x06,},
{0x1b,0x48,},
{0x1f,0xC8,},
{0x20,0x01},
{0x21,0x78},
{0x22,0xb0},
{0x23,0x04},
{0x24,0x3f}, // SYNC/data/pclk driver : 4/6/10/12mA
{0x26,0x00},
{0x50,0x01},
{0x70,0x85,},
////////////////////////////////////////////////
//////////// block enable /////////////
////////////////////////////////////////////////
{0x40,0x7f,},
{0x41,0x26,},
{0x42,0xff},
{0x45,0x00,},
{0x44,0x06,}, // 0x01:CrYCbY 0x06:RGB565
{0x46,0x02,},
{0x4b,0x01,},
//{0x4c,0x01,}, // debug mode2: color bar
{0x50,0x01,},
/////DN & EEINTP/////
{0x7e,0x0a,},
{0x7f,0x03,},
{0x80,0x27,},
{0x81,0x15,},
{0x82,0x90,},
{0x83,0x02,},
{0x84,0x23,},
{0x90,0x2c,},
{0x92,0x02,},
{0x94,0x02,},
{0x95,0x35,},
///////YCP
{0xd1,0x32,},
{0xd2,0x32,},
{0xdd,0x18,},
{0xde,0x32,},
{0xe4,0x88,},
{0xe5,0x40,},
{0xd7,0x0e,},
/////////////////////////////
////////////////}, GAMMA //////},
/////////////////////////////
//},rgb gamma},
{0xfe,0x00,},
{0xbf,0x10,},
{0xc0,0x1c,},
{0xc1,0x33,},
{0xc2,0x48,},
{0xc3,0x5a,},
{0xc4,0x6b,},
{0xc5,0x7b,},
{0xc6,0x95,},
{0xc7,0xab,},
{0xc8,0xbf,},
{0xc9,0xcd,},
{0xca,0xd9,},
{0xcb,0xe3,},
{0xcc,0xeb,},
{0xcd,0xf7,},
{0xce,0xfd,},
{0xcf,0xff,},
/////////////Y gamma},//////////
{0xfe,0x00,},
{0x63,0x00,},
{0x64,0x05,},
{0x65,0x0c,},
{0x66,0x1a,},
{0x67,0x29,},
{0x68,0x39,},
{0x69,0x4b,},
{0x6a,0x5e,},
{0x6b,0x82,},
{0x6c,0xa4,},
{0x6d,0xc5,},
{0x6e,0xe5,},
{0x6f,0xFF,},
//////ASDE },
{0xfe,0x01,},
{0x18,0x02,},
{0xfe,0x00,},
{0x98,0x00,},
{0x9b,0x20,},
{0x9c,0x80,},
{0xa4,0x10,},
{0xa8,0xB0,},
{0xaa,0x40,},
{0xa2,0x23,},
{0xad,0x01,},
//////////////////////////////////////////////
////////// AEC}, ////////////////////////},
//////////////////////////////////////////////
{0xfe,0x01},
{0x9c,0x02,},
{0x08,0xa0,},
{0x09,0xe8,},
{0x10,0x00,},
{0x11,0x11,},
{0x12,0x10,},
{0x13,0x80,},
{0x15,0xfc,},
{0x18,0x03,},
{0x21,0xc0,},
{0x22,0x60,},
{0x23,0x30,},
{0x25,0x00,},
{0x24,0x14,},
//////////////////////////////////////
////////////LSC},//////////////////////},
//////////////////////////////////////
{0xfe,0x01},
{0xc0,0x10,},
{0xc1,0x0c,},
{0xc2,0x0a,},
{0xc6,0x0e,},
{0xc7,0x0b,},
{0xc8,0x0a,},
{0xba,0x26,},
{0xbb,0x1c,},
{0xbc,0x1d,},
{0xb4,0x23,},
{0xb5,0x1c,},
{0xb6,0x1a,},
{0xc3,0x00,},
{0xc4,0x00,},
{0xc5,0x00,},
{0xc9,0x00,},
{0xca,0x00,},
{0xcb,0x00,},
{0xbd,0x00,},
{0xbe,0x00,},
{0xbf,0x00,},
{0xb7,0x07,},
{0xb8,0x05,},
{0xb9,0x05,},
{0xa8,0x07,},
{0xa9,0x06,},
{0xaa,0x00,},
{0xab,0x04,},
{0xac,0x00,},
{0xad,0x02,},
{0xae,0x0d,},
{0xaf,0x05,},
{0xb0,0x00,},
{0xb1,0x07,},
{0xb2,0x03,},
{0xb3,0x00,},
{0xa4,0x00,},
{0xa5,0x00,},
{0xa6,0x00,},
{0xa7,0x00,},
{0xa1,0x3c,},
{0xa2,0x50,},
{0xfe,0x00,},
///////////////CCT ///////////},
{0xB1,0x04,},
{0xB2,0xfd,},
{0xB3,0xfc,},
{0xB4,0xf0,},
{0xB5,0x05,},
{0xB6,0xf0,},
{0xfe,0x00,},
{0x27,0xf7,},
{0x28,0x7F,},
{0x29,0x20,},
{0x33,0x20,},
{0x34,0x20,},
{0x35,0x20,},
{0x36,0x20,},
{0x32,0x08,},
{0x47,0x00,},
{0x48,0x00,},
{0xfe,0x01,},
{0x79,0x00,},
{0x7d,0x00,},
{0x50,0x88,},
{0x5b,0x0c,},
{0x76,0x8f,},
{0x80,0x70,},
{0x81,0x70,},
{0x82,0xb0,},
{0x70,0xff,},
{0x71,0x00,},
{0x72,0x28,},
{0x73,0x0b,},
{0x74,0x0b,},
{0xfe,0x00,},
{0x70,0x45,},
{0x4f,0x01,},
{0xf1,0x07,},
{0xf2,0x01,},
};
#endif

View File

@@ -0,0 +1,31 @@
/*
*
* GC032A driver.
*
*/
#ifndef __GC032A_H__
#define __GC032A_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int gc032a_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int gc032a_init(sensor_t *sensor);
#endif // __GC032A_H__

View File

@@ -0,0 +1,82 @@
/*
* GC032A register definitions.
*/
#ifndef __GC032A_REG_REGS_H__
#define __GC032A_REG_REGS_H__
#define SENSOR_ID_HIGH 0XF0
#define SENSOR_ID_LOW 0XF1
#define PAD_VB_HIZ_MODE 0XF2
#define SYNC_OUTPUT 0XF3
#define I2C_CONFIG 0XF4
#define PLL_MODE1 0XF7
#define PLL_MODE2 0XF8
#define CM_MODE 0XF9
#define ISP_DIV_MODE 0XFA
#define I2C_DEVICE_ID 0XFB
#define ANALOG_PWC 0XFC
#define ISP_DIV_MODE2 0XFD
#define RESET_RELATED 0XFE // Bit[7]: Software reset
// Bit[6]: cm reset
// Bit[5]: spi reset
// Bit[4]: CISCTL_restart_n
// Bit[3]: PLL_rst
// Bit[2:0]: page select
// 000:page0
// 001:page1
// 010:page2
// 011:page3
//----page0-----------------------------
#define P0_EXPOSURE_HIGH 0X03
#define P0_EXPOSURE_LOW 0X04
#define P0_HB_HIGH 0X05
#define P0_HB_LOW 0X06
#define P0_VB_HIGH 0X07
#define P0_VB_LOW 0X08
#define P0_ROW_START_HIGH 0X09
#define P0_ROW_START_LOW 0X0A
#define P0_COLUMN_START_HIGH 0X0B
#define P0_COLUMN_START_LOW 0X0C
#define P0_WINDOW_HEIGHT_HIGH 0X0D
#define P0_WINDOW_HEIGHT_LOW 0X0E
#define P0_WINDOW_WIDTH_HIGH 0X0F
#define P0_WINDOW_WIDTH_LOW 0X10
#define P0_SH_DELAY 0X11
#define P0_VS_ST 0X12
#define P0_VS_ET 0X13
#define P0_CISCTL_MODE1 0X17
#define P0_BLOCK_ENABLE_1 0X40
#define P0_AAAA_ENABLE 0X42
#define P0_SPECIAL_EFFECT 0X43
#define P0_SYNC_MODE 0X46
#define P0_GAIN_CODE 0X48
#define P0_DEBUG_MODE2 0X4C
#define P0_WIN_MODE 0X50
#define P0_OUT_WIN_Y1_HIGH 0X51
#define P0_OUT_WIN_Y1_LOW 0X52
#define P0_OUT_WIN_X1_HIGH 0X53
#define P0_OUT_WIN_X1_LOW 0X54
#define P0_OUT_WIN_HEIGHT_HIGH 0X55
#define P0_OUT_WIN_HEIGHT_LOW 0X56
#define P0_OUT_WIN_WIDTH_HIGH 0X57
#define P0_OUT_WIN_WIDTH_LOW 0X58
#define P0_GLOBAL_SATURATION 0XD0
#define P0_SATURATION_CB 0XD1
#define P0_SATURATION_CR 0XD2
#define P0_LUMA_CONTRAST 0XD3
#define P0_CONTRAST_CENTER 0XD4
#define P0_LUMA_OFFSET 0XD5
#define P0_FIXED_CB 0XDA
#define P0_FIXED_CR 0XDB
//----page3-----------------------------
#define P3_IMAGE_WIDTH_LOW 0X5B
#define P3_IMAGE_WIDTH_HIGH 0X5C
#define P3_IMAGE_HEIGHT_LOW 0X5D
#define P3_IMAGE_HEIGHT_HIGH 0X5E
#endif //__GC032A_REG_REGS_H__

View File

@@ -0,0 +1,737 @@
#ifndef _GC032A_SETTINGS_H_
#define _GC032A_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "gc032a_regs.h"
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000
/* 25M CLK & VGA & YUV(UYVY) & 15fps */
/* 25M CLK & VGA & YUV(UYVY) & 15fps */
static const uint16_t gc032a_init_reg_tb[][2] = {
/*System*/
{0xf3, 0xff},
{0xf5, 0x06},
{0xf7, 0x01},
{0xf8, 0x03},
{0xf9, 0xce},
{0xfa, 0x00},
{0xfc, 0x02},
{0xfe, 0x02},
{0x81, 0x03},
{0xfe, 0x00},
{0x77, 0x64},
{0x78, 0x40},
{0x79, 0x60},
/*ANALOG & CISCTL*/
{0xfe, 0x00},
{0x03, 0x01},
{0x04, 0xce},
{0x05, 0x01},
{0x06, 0xad},
{0x07, 0x00},
{0x08, 0x10},
{0x0a, 0x00},
{0x0c, 0x00},
{0x0d, 0x01},
{0x0e, 0xe8}, // height 488
{0x0f, 0x02},
{0x10, 0x88}, // width 648
{0x17, 0x54},
{0x19, 0x08},
{0x1a, 0x0a},
{0x1f, 0x40},
{0x20, 0x30},
{0x2e, 0x80},
{0x2f, 0x2b},
{0x30, 0x1a},
{0xfe, 0x02},
{0x03, 0x02},
{0x05, 0xd7},
{0x06, 0x60},
{0x08, 0x80},
{0x12, 0x89},
/*blk*/
{0xfe, 0x00},
{0x18, 0x02},
{0xfe, 0x02},
{0x40, 0x22},
{0x45, 0x00},
{0x46, 0x00},
{0x49, 0x20},
{0x4b, 0x3c},
{0x50, 0x20},
{0x42, 0x10},
/*isp*/
{0xfe, 0x01},
{0x0a, 0xc5},
{0x45, 0x00},
{0xfe, 0x00},
{0x40, 0xff},
{0x41, 0x25},
{0x42, 0xcf},
{0x43, 0x10},
{0x44, 0x83},
{0x46, 0x23},
{0x49, 0x23}, //P0:0x49[5]-> 1:big endian 0: little endian
{0x52, 0x02},
{0x54, 0x00},
{0xfe, 0x02},
{0x22, 0xf6},
/*Shading*/
{0xfe, 0x01},
{0xc1, 0x38},
{0xc2, 0x4c},
{0xc3, 0x00},
{0xc4, 0x32},
{0xc5, 0x24},
{0xc6, 0x16},
{0xc7, 0x08},
{0xc8, 0x08},
{0xc9, 0x00},
{0xca, 0x20},
{0xdc, 0x8a},
{0xdd, 0xa0},
{0xde, 0xa6},
{0xdf, 0x75},
/*AWB*/
{0xfe, 0x01},
{0x7c, 0x09},
{0x65, 0x06},
{0x7c, 0x08},
{0x56, 0xf4},
{0x66, 0x0f},
{0x67, 0x84},
{0x6b, 0x80},
{0x6d, 0x12},
{0x6e, 0xb0},
{0x86, 0x00},
{0x87, 0x00},
{0x88, 0x00},
{0x89, 0x00},
{0x8a, 0x00},
{0x8b, 0x00},
{0x8c, 0x00},
{0x8d, 0x00},
{0x8e, 0x00},
{0x8f, 0x00},
{0x90, 0x00},
{0x91, 0x00},
{0x92, 0xf4},
{0x93, 0xd5},
{0x94, 0x50},
{0x95, 0x0f},
{0x96, 0xf4},
{0x97, 0x2d},
{0x98, 0x0f},
{0x99, 0xa6},
{0x9a, 0x2d},
{0x9b, 0x0f},
{0x9c, 0x59},
{0x9d, 0x2d},
{0x9e, 0xaa},
{0x9f, 0x67},
{0xa0, 0x59},
{0xa1, 0x00},
{0xa2, 0x00},
{0xa3, 0x0a},
{0xa4, 0x00},
{0xa5, 0x00},
{0xa6, 0xd4},
{0xa7, 0x9f},
{0xa8, 0x55},
{0xa9, 0xd4},
{0xaa, 0x9f},
{0xab, 0xac},
{0xac, 0x9f},
{0xad, 0x55},
{0xae, 0xd4},
{0xaf, 0xac},
{0xb0, 0xd4},
{0xb1, 0xa3},
{0xb2, 0x55},
{0xb3, 0xd4},
{0xb4, 0xac},
{0xb5, 0x00},
{0xb6, 0x00},
{0xb7, 0x05},
{0xb8, 0xd6},
{0xb9, 0x8c},
/*CC*/
{0xfe, 0x01},
{0xd0, 0x40},
{0xd1, 0xf8},
{0xd2, 0x00},
{0xd3, 0xfa},
{0xd4, 0x45},
{0xd5, 0x02},
{0xd6, 0x30},
{0xd7, 0xfa},
{0xd8, 0x08},
{0xd9, 0x08},
{0xda, 0x58},
{0xdb, 0x02},
{0xfe, 0x00},
/*Gamma*/
{0xfe, 0x00},
{0xba, 0x00},
{0xbb, 0x04},
{0xbc, 0x0a},
{0xbd, 0x0e},
{0xbe, 0x22},
{0xbf, 0x30},
{0xc0, 0x3d},
{0xc1, 0x4a},
{0xc2, 0x5d},
{0xc3, 0x6b},
{0xc4, 0x7a},
{0xc5, 0x85},
{0xc6, 0x90},
{0xc7, 0xa5},
{0xc8, 0xb5},
{0xc9, 0xc2},
{0xca, 0xcc},
{0xcb, 0xd5},
{0xcc, 0xde},
{0xcd, 0xea},
{0xce, 0xf5},
{0xcf, 0xff},
/*Auto Gamma*/
{0xfe, 0x00},
{0x5a, 0x08},
{0x5b, 0x0f},
{0x5c, 0x15},
{0x5d, 0x1c},
{0x5e, 0x28},
{0x5f, 0x36},
{0x60, 0x45},
{0x61, 0x51},
{0x62, 0x6a},
{0x63, 0x7d},
{0x64, 0x8d},
{0x65, 0x98},
{0x66, 0xa2},
{0x67, 0xb5},
{0x68, 0xc3},
{0x69, 0xcd},
{0x6a, 0xd4},
{0x6b, 0xdc},
{0x6c, 0xe3},
{0x6d, 0xf0},
{0x6e, 0xf9},
{0x6f, 0xff},
/*Gain*/
{0xfe, 0x00},
{0x70, 0x50},
/*AEC*/
{0xfe, 0x00},
{0x4f, 0x01},
{0xfe, 0x01},
{0x0d, 0x00},
{0x12, 0xa0},
{0x13, 0x3a},
{0x44, 0x04},
{0x1f, 0x30},
{0x20, 0x40},
{0x26, 0x9a},
{0x3e, 0x20},
{0x3f, 0x2d},
{0x40, 0x40},
{0x41, 0x5b},
{0x42, 0x82},
{0x43, 0xb7},
{0x04, 0x0a},
{0x02, 0x79},
{0x03, 0xc0},
/*measure window*/
{0xfe, 0x01},
{0xcc, 0x08},
{0xcd, 0x08},
{0xce, 0xa4},
{0xcf, 0xec},
/*DNDD*/
{0xfe, 0x00},
{0x81, 0xb8},
{0x82, 0x12},
{0x83, 0x0a},
{0x84, 0x01},
{0x86, 0x50},
{0x87, 0x18},
{0x88, 0x10},
{0x89, 0x70},
{0x8a, 0x20},
{0x8b, 0x10},
{0x8c, 0x08},
{0x8d, 0x0a},
/*Intpee*/
{0xfe, 0x00},
{0x8f, 0xaa},
{0x90, 0x9c},
{0x91, 0x52},
{0x92, 0x03},
{0x93, 0x03},
{0x94, 0x08},
{0x95, 0x44},
{0x97, 0x00},
{0x98, 0x00},
/*ASDE*/
{0xfe, 0x00},
{0xa1, 0x30},
{0xa2, 0x41},
{0xa4, 0x30},
{0xa5, 0x20},
{0xaa, 0x30},
{0xac, 0x32},
/*YCP*/
{0xfe, 0x00},
{0xd1, 0x3c},
{0xd2, 0x3c},
{0xd3, 0x38},
{0xd6, 0xf4},
{0xd7, 0x1d},
{0xdd, 0x73},
{0xde, 0x84},
/*Banding*/
{0xfe, 0x00},
{0x05, 0x01},
{0x06, 0xad},
{0x07, 0x00},
{0x08, 0x10},
{0xfe, 0x01},
{0x25, 0x00},
{0x26, 0x9a},
{0x27, 0x01},
{0x28, 0xce},
{0x29, 0x02},
{0x2a, 0x68},
{0x2b, 0x02},
{0x2c, 0x68},
{0x2d, 0x07},
{0x2e, 0xd2},
{0x2f, 0x0b},
{0x30, 0x6e},
{0x31, 0x0e},
{0x32, 0x70},
{0x33, 0x12},
{0x34, 0x0c},
{0x3c, 0x30},
/*Analog&Cisctl*/
{0xfe, 0x00},
{0x05, 0x01},
{0x06, 0xa0},
{0x07, 0x00},
{0x08, 0x20},
{0x0a, 0x78},
{0x0c, 0xa0},
{0x0d, 0x00}, //window_height [8]
{0x0e, 0xf8}, //window_height [7:0] 248
{0x0f, 0x01}, //window_width [9:8]
{0x10, 0x48}, //window_width [7:0] 328
{0x55, 0x00},
{0x56, 0xf0}, // 240
{0x57, 0x01},
{0x58, 0x40}, // 320
/*SPI*/
{0xfe, 0x03},
{0x5b, 0x40},
{0x5c, 0x01},
{0x5d, 0xf0},
{0x5e, 0x00},
/*AEC*/
{0xfe, 0x01},
{0x25, 0x00}, //step
{0x26, 0x63},
{0x27, 0x01},
{0x28, 0x29},
{0x29, 0x01},
{0x2a, 0x29},
{0x2b, 0x01},
{0x2c, 0x29},
{0x2d, 0x01},
{0x2e, 0x29},
{0x2f, 0x01},
{0x30, 0x29},
{0x31, 0x01},
{0x32, 0x29},
{0x33, 0x01},
{0x34, 0x29},
{0x3c, 0x00},
/*measure window*/
{0xfe, 0x01},
{0xcc, 0x04},
{0xcd, 0x04},
{0xce, 0x72},
{0xcf, 0x52},
{REGLIST_TAIL, 0x00},
};
static const uint16_t gc032a_init_reg320x240_tb[][2] = {
/*System*/
{0xf3, 0xff},
{0xf5, 0x06},
{0xf7, 0x01},
{0xf8, 0x03},
{0xf9, 0xce},
{0xfa, 0x00},
{0xfc, 0x02},
{0xfe, 0x02},
{0x81, 0x03},
{0xfe, 0x00},
{0x77, 0x64},
{0x78, 0x40},
{0x79, 0x60},
/*Analog&Cisctl*/
{0xfe, 0x00},
{0x03, 0x01},
{0x04, 0xce},
{0x05, 0x01},
{0x06, 0xad},
{0x07, 0x00},
{0x08, 0x10},
{0x0a, 0x00},
{0x0c, 0x00},
{0x0d, 0x01},
{0x0e, 0xe8},
{0x0f, 0x02},
{0x10, 0x88},
{0x17, 0x54},
{0x19, 0x08},
{0x1a, 0x0a},
{0x1f, 0x40},
{0x20, 0x30},
{0x2e, 0x80},
{0x2f, 0x2b},
{0x30, 0x1a},
{0xfe, 0x02},
{0x03, 0x02},
{0x05, 0xd7},
{0x06, 0x60},
{0x08, 0x80},
{0x12, 0x89},
/*blk*/
{0xfe, 0x00},
{0x18, 0x02},
{0xfe, 0x02},
{0x40, 0x22},
{0x45, 0x00},
{0x46, 0x00},
{0x49, 0x20},
{0x4b, 0x3c},
{0x50, 0x20},
{0x42, 0x10},
/*isp*/
{0xfe, 0x01},
{0x0a, 0xc5},
{0x45, 0x00},
{0xfe, 0x00},
{0x40, 0xff},
{0x41, 0x25},
{0x42, 0xcf},
{0x43, 0x10},
{0x44, 0x80}, // yuv 80 81 82 83
{0x46, 0x22},
{0x49, 0x03},
{0x52, 0x02},
{0x54, 0x00},
{0xfe, 0x02},
{0x22, 0xf6},
/*Shading*/
{0xfe, 0x01},
{0xc1, 0x38},
{0xc2, 0x4c},
{0xc3, 0x00},
{0xc4, 0x32},
{0xc5, 0x24},
{0xc6, 0x16},
{0xc7, 0x08},
{0xc8, 0x08},
{0xc9, 0x00},
{0xca, 0x20},
{0xdc, 0x8a},
{0xdd, 0xa0},
{0xde, 0xa6},
{0xdf, 0x75},
/*AWB*/ /*20170110*/
{0xfe, 0x01},
{0x7c, 0x09},
{0x65, 0x06},
{0x7c, 0x08},
{0x56, 0xf4},
{0x66, 0x0f},
{0x67, 0x84},
{0x6b, 0x80},
{0x6d, 0x12},
{0x6e, 0xb0},
{0xfe, 0x01},
{0x90, 0x00},
{0x91, 0x00},
{0x92, 0xf4},
{0x93, 0xd5},
{0x95, 0x0f},
{0x96, 0xf4},
{0x97, 0x2d},
{0x98, 0x0f},
{0x9a, 0x2d},
{0x9b, 0x0f},
{0x9c, 0x59},
{0x9d, 0x2d},
{0x9f, 0x67},
{0xa0, 0x59},
{0xa1, 0x00},
{0xa2, 0x00},
{0x86, 0x00},
{0x87, 0x00},
{0x88, 0x00},
{0x89, 0x00},
{0xa4, 0x00},
{0xa5, 0x00},
{0xa6, 0xd4},
{0xa7, 0x9f},
{0xa9, 0xd4},
{0xaa, 0x9f},
{0xab, 0xac},
{0xac, 0x9f},
{0xae, 0xd4},
{0xaf, 0xac},
{0xb0, 0xd4},
{0xb1, 0xa3},
{0xb3, 0xd4},
{0xb4, 0xac},
{0xb5, 0x00},
{0xb6, 0x00},
{0x8b, 0x00},
{0x8c, 0x00},
{0x8d, 0x00},
{0x8e, 0x00},
{0x94, 0x50},
{0x99, 0xa6},
{0x9e, 0xaa},
{0xa3, 0x0a},
{0x8a, 0x00},
{0xa8, 0x50},
{0xad, 0x55},
{0xb2, 0x55},
{0xb7, 0x05},
{0x8f, 0x00},
{0xb8, 0xb3},
{0xb9, 0xb6},
/*CC*/
{0xfe, 0x01},
{0xd0, 0x40},
{0xd1, 0xf8},
{0xd2, 0x00},
{0xd3, 0xfa},
{0xd4, 0x45},
{0xd5, 0x02},
{0xd6, 0x30},
{0xd7, 0xfa},
{0xd8, 0x08},
{0xd9, 0x08},
{0xda, 0x58},
{0xdb, 0x02},
{0xfe, 0x00},
/*Gamma*/
{0xfe, 0x00},
{0xba, 0x00},
{0xbb, 0x04},
{0xbc, 0x0a},
{0xbd, 0x0e},
{0xbe, 0x22},
{0xbf, 0x30},
{0xc0, 0x3d},
{0xc1, 0x4a},
{0xc2, 0x5d},
{0xc3, 0x6b},
{0xc4, 0x7a},
{0xc5, 0x85},
{0xc6, 0x90},
{0xc7, 0xa5},
{0xc8, 0xb5},
{0xc9, 0xc2},
{0xca, 0xcc},
{0xcb, 0xd5},
{0xcc, 0xde},
{0xcd, 0xea},
{0xce, 0xf5},
{0xcf, 0xff},
/*Auto Gamma*/
{0xfe, 0x00},
{0x5a, 0x08},
{0x5b, 0x0f},
{0x5c, 0x15},
{0x5d, 0x1c},
{0x5e, 0x28},
{0x5f, 0x36},
{0x60, 0x45},
{0x61, 0x51},
{0x62, 0x6a},
{0x63, 0x7d},
{0x64, 0x8d},
{0x65, 0x98},
{0x66, 0xa2},
{0x67, 0xb5},
{0x68, 0xc3},
{0x69, 0xcd},
{0x6a, 0xd4},
{0x6b, 0xdc},
{0x6c, 0xe3},
{0x6d, 0xf0},
{0x6e, 0xf9},
{0x6f, 0xff},
/*Gain*/
{0xfe, 0x00},
{0x70, 0x50},
/*AEC*/
{0xfe, 0x00},
{0x4f, 0x01},
{0xfe, 0x01},
{0x0d, 0x00}, // 08 add 20170110
{0x12, 0xa0},
{0x13, 0x3a},
{0x44, 0x04},
{0x1f, 0x30},
{0x20, 0x40},
{0x26, 0x9a},
{0x3e, 0x20},
{0x3f, 0x2d},
{0x40, 0x40},
{0x41, 0x5b},
{0x42, 0x82},
{0x43, 0xb7},
{0x04, 0x0a},
{0x02, 0x79},
{0x03, 0xc0},
/*measure window*/
{0xfe, 0x01},
{0xcc, 0x08},
{0xcd, 0x08},
{0xce, 0xa4},
{0xcf, 0xec},
/*DNDD*/
{0xfe, 0x00},
{0x81, 0xb8},
{0x82, 0x12},
{0x83, 0x0a},
{0x84, 0x01},
{0x86, 0x50},
{0x87, 0x18},
{0x88, 0x10},
{0x89, 0x70},
{0x8a, 0x20},
{0x8b, 0x10},
{0x8c, 0x08},
{0x8d, 0x0a},
/*Intpee*/
{0xfe, 0x00},
{0x8f, 0xaa},
{0x90, 0x9c},
{0x91, 0x52},
{0x92, 0x03},
{0x93, 0x03},
{0x94, 0x08},
{0x95, 0x44},
{0x97, 0x00},
{0x98, 0x00},
/*ASDE*/
{0xfe, 0x00},
{0xa1, 0x30},
{0xa2, 0x41},
{0xa4, 0x30},
{0xa5, 0x20},
{0xaa, 0x30},
{0xac, 0x32},
/*YCP*/
{0xfe, 0x00},
{0xd1, 0x3c},
{0xd2, 0x3c},
{0xd3, 0x38},
{0xd6, 0xf4},
{0xd7, 0x1d},
{0xdd, 0x73},
{0xde, 0x84},
/*Banding*/
{0xfe, 0x00},
{0x05, 0x01},
{0x06, 0xad},
{0x07, 0x00},
{0x08, 0x10},
{0xfe, 0x01},
{0x25, 0x00},
{0x26, 0x9a},
{0x27, 0x04},
{0x28, 0x00},
{0x29, 0x04},
{0x2a, 0x00},
{0x2b, 0x04},
{0x2c, 0x00},
{0x2d, 0x04},
{0x2e, 0x00},
{0x2f, 0x04},
{0x30, 0x00},
{0x31, 0x03},
{0x32, 0x02},
{0x33, 0x12},
{0x34, 0x0c},
{0x3c, 0x30},
{0xfe, 0x00},
{0x4c, 0x00}, //test mode
{0xfe, 0x00},
{0x50, 0x01},
{0x55, 0x00},
{0x56, 0xf0},
{0x57, 0x01},
{0x58, 0x40},
{REGLIST_TAIL, 0x00},
};
#endif

View File

@@ -0,0 +1,27 @@
#ifndef __GC2145_H__
#define __GC2145_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int gc2145_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int gc2145_init(sensor_t *sensor);
#endif // __GC2145_H__

View File

@@ -0,0 +1,85 @@
/*
* GC2145 register definitions.
*/
#ifndef __GC2145_REG_REGS_H__
#define __GC2145_REG_REGS_H__
#define CHIP_ID_HIGH 0XF0
#define CHIP_ID_LOW 0XF1
#define PLL_MODE1 0XF7
#define PLL_MODE2 0XF8
#define CM_MODE 0XF9
#define CLK_DIV_MODE 0XFA
#define RESET_RELATED 0xfe // Bit[7]: Software reset
// Bit[6]: cm reset
// Bit[5]: mipi reset
// Bit[4]: CISCTL_restart_n
// Bit[3]: NA
// Bit[2:0]: page select
// 000:page0
// 001:page1
// 010:page2
// 011:page3
//-page0----------------
#define P0_EXPOSURE_HIGH 0X03
#define P0_EXPOSURE_LOW 0X04
#define P0_HB_HIGH 0X05
#define P0_HB_LOW 0X06
#define P0_VB_HIGH 0X07
#define P0_VB_LOW 0X08
#define P0_ROW_START_HIGH 0X09
#define P0_ROW_START_LOW 0X0A
#define P0_COL_START_HIGH 0X0B
#define P0_COL_START_LOW 0X0C
#define P0_WIN_HEIGHT_HIGH 0X0D
#define P0_WIN_HEIGHT_LOW 0X0E
#define P0_WIN_WIDTH_HIGH 0X0F
#define P0_WIN_WIDTH_LOW 0X10
#define P0_ANALOG_MODE1 0X17
#define P0_ANALOG_MODE2 0X18
#define P0_SPECIAL_EFFECT 0X83
#define P0_OUTPUT_FORMAT 0x84 // Format select
// Bit[7]:YUV420 row switch
// Bit[6]:YUV420 col switch
// Bit[7]:YUV420_legacy
// Bit[4:0]:output data mode
// 5h00 Cb Y Cr Y
// 5h01 Cr Y Cb Y
// 5h02 Y Cb Y Cr
// 5h03 Y Cr Y Cb
// 5h04 LSC bypass, C/Y
// 5h05 LSC bypass, Y/C
// 5h06 RGB 565
// 5h0f bypass 10bits
// 5h17 switch odd/even column /row to controls output Bayer pattern
// 00 RGBG
// 01 RGGB
// 10 BGGR
// 11 GBRG
// 5'h18 DNDD out mode
// 5'h19 LSC out mode
// 5;h1b EEINTP out mode
#define P0_FRAME_START 0X85
#define P0_SYNC_MODE 0X86
#define P0_MODULE_GATING 0X88
#define P0_BYPASS_MODE 0X89
#define P0_DEBUG_MODE2 0X8C
#define P0_DEBUG_MODE3 0X8D
#define P0_CROP_ENABLE 0X90
#define P0_OUT_WIN_Y1_HIGH 0X91
#define P0_OUT_WIN_Y1_LOW 0X92
#define P0_OUT_WIN_X1_HIGH 0X93
#define P0_OUT_WIN_X1_LOW 0X94
#define P0_OUT_WIN_HEIGHT_HIGH 0X95
#define P0_OUT_WIN_HEIGHT_LOW 0X96
#define P0_OUT_WIN_WIDTH_HIGH 0X97
#define P0_OUT_WIN_WIDTH_LOW 0X98
#define P0_SUBSAMPLE 0X99
#define P0_SUBSAMPLE_MODE 0X9A
#endif // __GC2145_REG_REGS_H__

View File

@@ -0,0 +1,719 @@
#include <stdint.h>
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000 /* Array end token */
static const uint16_t gc2145_default_init_regs[][2] = {
{0xfe, 0xf0},
{0xfe, 0xf0},
{0xfe, 0xf0},
{0xfc, 0x06},
{0xf6, 0x00},
{0xf7, 0x1d}, //37 //17 //37 //1d//05
{0xf8, 0x83}, //87 //83 //82
{0xfa, 0x00},
{0xf9, 0xfe}, //ff
{0xfd, 0x00},
{0xc2, 0x00},
{0xf2, 0x0f},
//////////////////////////////////////////////////////
//////////////////// Analog & Cisctl ////////////////
//////////////////////////////////////////////////////
{0xfe, 0x00},
{0x03, 0x04}, //exp time
{0x04, 0x62}, //exp time
{0x05, 0x01}, //00 //hb[11:8]
{0x06, 0x3b}, //0b //hb
{0x09, 0x00}, //row start
{0x0a, 0x00}, //
{0x0b, 0x00}, //col start
{0x0c, 0x00},
{0x0d, 0x04}, //height
{0x0e, 0xc0},
{0x0f, 0x06}, //width
{0x10, 0x52},
{0x12, 0x2e}, //sh_delay 太短 YUV出图异常
{0x17, 0x14}, //CISCTL Mode1 [1:0]mirror flip
{0x18, 0x22}, //sdark mode
{0x19, 0x0f}, // AD pipe number
{0x1a, 0x01}, //AD manual switch mode
{0x1b, 0x4b}, //48 restg Width,SH width
{0x1c, 0x07}, //06 帧率快后,横条纹 //12 //TX Width,Space Width
{0x1d, 0x10}, //double reset
{0x1e, 0x88}, //90//98 //fix 竖线//Analog Mode1,TX high,Coln_r
{0x1f, 0x78}, //78 //38 //18 //Analog Mode2,txlow
{0x20, 0x03}, //07 //Analog Mode3,comv,ad_clk mode
{0x21, 0x40}, //10//20//40 //fix 灯管横条纹
{0x22, 0xa0}, //d0//f0 //a2 //Vref vpix FPN严重
{0x24, 0x1e},
{0x25, 0x01}, //col sel
{0x26, 0x10}, //Analog PGA gain1
{0x2d, 0x60}, //40//40 //txl drv mode
{0x30, 0x01}, //Analog Mode4
{0x31, 0x90}, //b0//70 // Analog Mode7 [7:5]rsgh_r灯管横条纹[4:3]isp_g
{0x33, 0x06}, //03//02//01 //EQ_hstart_width
{0x34, 0x01},
//
///////////////////////////////////////////////////
//////////////////// ISP reg //////////////////////
//////////////////////////////////////////////////////
{0x80, 0xff}, //outdoor gamma_en, GAMMA_en, CC_en, EE_en, INTP_en, DN_en, DD_en,LSC_en
{0x81, 0x24}, //26//24 //BLK dither mode, ll_y_en ,skin_en, edge SA, new_skin_mode, autogray_en,ll_gamma_en,BFF test image
{0x82, 0xfa}, //FA //auto_SA, auto_EE, auto_DN, auto_DD, auto_LSC, ABS_en, AWB_en, NA
{0x83, 0x00}, //special_effect
{0x84, 0x02}, //output format
{0x86, 0x03}, //c2 //46 //c2 //sync mode
{0x88, 0x03}, //[1]ctl_auto_gating [0]out_auto_gating
{0x89, 0x03}, //bypass disable
{0x85, 0x30}, //60//frame start cut
{0x8a, 0x00}, //ISP_quiet_mode,close aaa pclk,BLK gate mode,exception,close first pipe clock,close dndd clock,close intp clock,DIV_gatedclk_en
{0x8b, 0x00}, //[7:6]BFF_gate_mode,[5]BLK switch gain,[4]protect exp,[3:2]pipe gate mode,[1]not split sram,[0]dark current update
{0xb0, 0x55}, //60 //global gain
{0xc3, 0x00}, //[7:4]auto_exp_gamma_th1[11:8],[3:0]auto_exp_gamma_th2[11:8]
{0xc4, 0x80}, //auto_exp_gamma_th1[7:0] into
{0xc5, 0x90}, //auto_exp_gamma_th2[7:0] out //outdoor gamma
{0xc6, 0x38}, //auto_gamma_th1
{0xc7, 0x40}, //auto_gamma_th2
{0xec, 0x06}, //measure window
{0xed, 0x04},
{0xee, 0x60}, //16 col
{0xef, 0x90}, //8 row
{0xb6, 0x01}, //[0]aec en
{0x90, 0x01}, //crop
{0x91, 0x00},
{0x92, 0x00},
{0x93, 0x00},
{0x94, 0x00}, //08
{0x95, 0x04},
{0x96, 0xb0},
{0x97, 0x06},
{0x98, 0x40},
///////////////////////////////////////////////
/////////// BLK ////////////////////////
///////////////////////////////////////////////
{0x18, 0x02},
{0x40, 0x42}, //2b //27
{0x41, 0x00}, //80 //dark row sel
{0x43, 0x54}, //[7:4]BLK start not smooth [3:0]output start frame
{0x5e, 0x00}, //00//10 //18
{0x5f, 0x00}, //00//10 //18
{0x60, 0x00}, //00//10 //18
{0x61, 0x00}, //00///10 //18
{0x62, 0x00}, //00//10 //18
{0x63, 0x00}, //00//10 //18
{0x64, 0x00}, //00/10 //18
{0x65, 0x00}, //00//10 //18
{0x66, 0x20}, //1e
{0x67, 0x20}, //1e
{0x68, 0x20}, //1e
{0x69, 0x20}, //1e
{0x76, 0x00}, //0f
{0x6a, 0x00}, //06
{0x6b, 0x00}, //06
{0x6c, 0x3e}, //06
{0x6d, 0x3e}, //06
{0x6e, 0x3f}, //06
{0x6f, 0x3f}, //06
{0x70, 0x00}, //06
{0x71, 0x00}, //06 //manual offset
{0x76, 0x00}, //1f//add offset
{0x72, 0xf0}, //[7:4]BLK DD th [3:0]BLK various th
{0x7e, 0x3c}, //ndark
{0x7f, 0x00},
{0xfe, 0x02},
{0x48, 0x15},
{0x49, 0x00}, //04//04 //ASDE OFFSET SLOPE
{0x4b, 0x0b}, //ASDE y OFFSET SLOPE
{0xfe, 0x00},
///////////////////////////////////////////////
/////////// AEC ////////////////////////
///////////////////////////////////////////////
{0xfe, 0x01},
{0x01, 0x04}, //AEC X1
{0x02, 0xc0}, //AEC X2
{0x03, 0x04}, //AEC Y1
{0x04, 0x90}, //AEC Y2
{0x05, 0x30}, //20 //AEC center X1
{0x06, 0x90}, //40 //AEC center X2
{0x07, 0x20}, //30 //AEC center Y1
{0x08, 0x70}, //60 //AEC center Y2
{0x09, 0x00}, //AEC show mode
{0x0a, 0xc2}, //[7]col gain enable
{0x0b, 0x11}, //AEC every N
{0x0c, 0x10}, //AEC_mode3 center weight
{0x13, 0x40}, //2a //AEC Y target
{0x17, 0x00}, //AEC ignore mode
{0x1c, 0x11}, //
{0x1e, 0x61}, //
{0x1f, 0x30}, //40//50 //max pre gain
{0x20, 0x40}, //60//40 //max post gain
{0x22, 0x80}, //AEC outdoor THD
{0x23, 0x20}, //target_Y_low_limit
{0xfe, 0x02},
{0x0f, 0x04}, //05
{0xfe, 0x01},
{0x12, 0x35}, //35 //[5:4]group_size [3]slope_disable [2]outdoor_enable [0]histogram_enable
{0x15, 0x50}, //target_Y_high_limit
{0x10, 0x31}, //num_thd_high
{0x3e, 0x28}, //num_thd_low
{0x3f, 0xe0}, //luma_thd
{0x40, 0x20}, //luma_slope
{0x41, 0x0f}, //color_diff
{0xfe, 0x02},
{0x0f, 0x05}, //max_col_level
///////////////////////////
////// INTPEE /////////////
///////////////////////////
{0xfe, 0x02}, //page2
{0x90, 0x6c}, //ac //eeintp mode1
{0x91, 0x03}, //02 ////eeintp mode2
{0x92, 0xc8}, //44 //low criteria for direction
{0x94, 0x66},
{0x95, 0xb5},
{0x97, 0x64}, //78 ////edge effect
{0xa2, 0x11}, //fix direction
{0xfe, 0x00},
/////////////////////////////
//////// DNDD///////////////
/////////////////////////////
{0xfe, 0x02},
{0x80, 0xc1}, //c1 //[7]share mode [6]skin mode [5]is 5x5 mode [1:0]noise value select 0:2 1:2.5 2:3 3:4
{0x81, 0x08}, //
{0x82, 0x08}, //signal a 0.6
{0x83, 0x08}, //04 //signal b 2.5
{0x84, 0x0a}, //10 //05 dark_DD_TH
{0x86, 0xf0}, //a0 Y_value_dd_th2
{0x87, 0x50}, //90 Y_value_dd_th3
{0x88, 0x15}, //60 Y_value_dd_th4
{0x89, 0x50}, //80 // asde th2
{0x8a, 0x30}, //60 // asde th3
{0x8b, 0x10}, //30 // asde th4
/////////////////////////////////////////////////
///////////// ASDE ////////////////////////
/////////////////////////////////////////////////
{0xfe, 0x01}, //page 1
{0x21, 0x14}, //luma_value_div_sel(分频与0xef呈2倍关系增大10xef的值减小1倍)
//ff ef luma_value read_only
{0xfe, 0x02}, //page2
{0xa3, 0x40}, //ASDE_low_luma_value_LSC_th_H
{0xa4, 0x20}, //ASDE_low_luma_value_LSC_th_L
{0xa5, 0x40}, //80 //ASDE_LSC_gain_dec_slope_H
{0xa6, 0x80}, // 80 //ASDE_LSC_gain_dec_slope_L
//ff a7 ASDE_LSC_gain_dec //read only
{0xab, 0x40}, //50 //ASDE_low_luma_value_OT_th
{0xae, 0x0c}, //[3]EE1_effect_inc_or_dec_high,[2]EE2_effect_inc_or_dec_high,
//[1]EE1_effect_inc_or_dec_low,[0]EE2_effect_inc_or_dec_low, 1:inc 0:dec
{0xb3, 0x34}, //44 //ASDE_EE1_effect_slope_low,ASDE_EE2_effect_slope_low
{0xb4, 0x44}, //12 //ASDE_EE1_effect_slope_high,ASDE_EE2_effect_slope_high
{0xb6, 0x38}, //40//40 //ASDE_auto_saturation_dec_slope
{0xb7, 0x02}, //04 //ASDE_sub_saturation_slope
{0xb9, 0x30}, //[7:0]ASDE_auto_saturation_low_limit
{0x3c, 0x08}, //[3:0]auto gray_dec_slope
{0x3d, 0x30}, //[7:0]auto gray_dec_th
{0x4b, 0x0d}, //y offset slope
{0x4c, 0x20}, //y offset limit
{0xfe, 0x00},
//
///////////////////gamma1////////////////////
////Gamma
{0xfe, 0x02},
{0x10, 0x10},
{0x11, 0x15},
{0x12, 0x1a},
{0x13, 0x1f},
{0x14, 0x2c},
{0x15, 0x39},
{0x16, 0x45},
{0x17, 0x54},
{0x18, 0x69},
{0x19, 0x7d},
{0x1a, 0x8f},
{0x1b, 0x9d},
{0x1c, 0xa9},
{0x1d, 0xbd},
{0x1e, 0xcd},
{0x1f, 0xd9},
{0x20, 0xe3},
{0x21, 0xea},
{0x22, 0xef},
{0x23, 0xf5},
{0x24, 0xf9},
{0x25, 0xff},
/////auto gamma/////
{0xfe, 0x02},
{0x26, 0x0f},
{0x27, 0x14},
{0x28, 0x19},
{0x29, 0x1e},
{0x2a, 0x27},
{0x2b, 0x33},
{0x2c, 0x3b},
{0x2d, 0x45},
{0x2e, 0x59},
{0x2f, 0x69},
{0x30, 0x7c},
{0x31, 0x89},
{0x32, 0x98},
{0x33, 0xae},
{0x34, 0xc0},
{0x35, 0xcf},
{0x36, 0xda},
{0x37, 0xe2},
{0x38, 0xe9},
{0x39, 0xf3},
{0x3a, 0xf9},
{0x3b, 0xff},
///////////////////////////////////////////////
/////////// YCP ///////////////////////
///////////////////////////////////////////////
{0xfe, 0x02},
{0xd1, 0x30}, //32 //
{0xd2, 0x30}, //32 //
{0xd3, 0x45},
{0xdd, 0x14}, //edge sa
{0xde, 0x86}, //asde auto gray
{0xed, 0x01}, //
{0xee, 0x28},
{0xef, 0x30},
{0xd8, 0xd8}, //autogray protecy
////////////////////////////
//////// LSC 0.8///////////////
////////////////////////////
{0xfe, 0x01},
{0xa1, 0x80}, // center_row
{0xa2, 0x80}, // center_col
{0xa4, 0x00}, // sign of b1
{0xa5, 0x00}, // sign of b1
{0xa6, 0x70}, // sign of b4
{0xa7, 0x00}, // sign of b4
{0xa8, 0x77}, // sign of b22
{0xa9, 0x77}, // sign of b22
{0xaa, 0x1f}, // Q1_b1 of R
{0xab, 0x0d}, // Q1_b1 of G
{0xac, 0x19}, // Q1_b1 of B
{0xad, 0x24}, // Q2_b1 of R
{0xae, 0x0e}, // Q2_b1 of G
{0xaf, 0x1d}, // Q2_b1 of B
{0xb0, 0x12}, // Q3_b1 of R
{0xb1, 0x0c}, // Q3_b1 of G
{0xb2, 0x06}, // Q3_b1 of B
{0xb3, 0x13}, // Q4_b1 of R
{0xb4, 0x10}, // Q4_b1 of G
{0xb5, 0x0c}, // Q4_b1 of B
{0xb6, 0x6a}, // right_b2 of R
{0xb7, 0x46}, // right_b2 of G
{0xb8, 0x40}, // right_b2 of B
{0xb9, 0x0b}, // right_b4 of R
{0xba, 0x04}, // right_b4 of G
{0xbb, 0x00}, // right_b4 of B
{0xbc, 0x53}, // left_b2 of R
{0xbd, 0x37}, // left_b2 of G
{0xbe, 0x2d}, // left_b2 of B
{0xbf, 0x0a}, // left_b4 of R
{0xc0, 0x0a}, // left_b4 of G
{0xc1, 0x14}, // left_b4 of B
{0xc2, 0x34}, // up_b2 of R
{0xc3, 0x22}, // up_b2 of G
{0xc4, 0x18}, // up_b2 of B
{0xc5, 0x23}, // up_b4 of R
{0xc6, 0x0f}, // up_b4 of G
{0xc7, 0x3c}, // up_b4 of B
{0xc8, 0x20}, // down_b2 of R
{0xc9, 0x1f}, // down_b2 of G
{0xca, 0x17}, // down_b2 of B
{0xcb, 0x2d}, // down_b4 of R
{0xcc, 0x12}, // down_b4 of G
{0xcd, 0x20}, // down_b4 of B
{0xd0, 0x61}, // right_up_b22 of R
{0xd1, 0x2f}, // right_up_b22 of G
{0xd2, 0x39}, // right_up_b22 of B
{0xd3, 0x45}, // right_down_b22 of R
{0xd4, 0x2c}, // right_down_b22 of G
{0xd5, 0x21}, // right_down_b22 of B
{0xd6, 0x64}, // left_up_b22 of R
{0xd7, 0x2d}, // left_up_b22 of G
{0xd8, 0x30}, // left_up_b22 of B
{0xd9, 0x42}, // left_down_b22 of R
{0xda, 0x27}, // left_down_b22 of G
{0xdb, 0x13}, // left_down_b22 of B
{0xfe, 0x00},
/////////////////////////////////////////////////
///////////// AWB ////////////////////////
/////////////////////////////////////////////////
{0xfe, 0x01},
{0x4f, 0x00},
{0x4f, 0x00},
{0x4b, 0x01},
{0x4f, 0x00},
{0x4c, 0x01},
{0x4d, 0x6f},
{0x4e, 0x02},
{0x4c, 0x01},
{0x4d, 0x70},
{0x4e, 0x02},
{0x4c, 0x01},
{0x4d, 0x8f},
{0x4e, 0x02},
{0x4c, 0x01},
{0x4d, 0x90},
{0x4e, 0x02}, //light
{0x4c, 0x01},
{0x4d, 0xed},
{0x4e, 0x33}, //light
{0x4c, 0x01},
{0x4d, 0xcd},
{0x4e, 0x33}, //light
{0x4c, 0x01},
{0x4d, 0xec},
{0x4e, 0x03}, //light
{0x4c, 0x01},
{0x4d, 0x6c},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0x6d},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0x6e},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0x8c},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0x8d},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0x8e},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xab},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xac},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xad},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xae},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xcb},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xcc},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xce},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xeb},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xec},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xee},
{0x4e, 0x03},
{0x4c, 0x02},
{0x4d, 0x0c},
{0x4e, 0x03},
{0x4c, 0x02},
{0x4d, 0x0d},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xea},
{0x4e, 0x03},
{0x4c, 0x01},
{0x4d, 0xaf},
{0x4e, 0x03}, //dark
{0x4c, 0x01},
{0x4d, 0xcf},
{0x4e, 0x03}, //dark
{0x4c, 0x01},
{0x4d, 0xca},
{0x4e, 0x04}, //light
{0x4c, 0x02},
{0x4d, 0x0b},
{0x4e, 0x05}, //light
{0x4c, 0x02},
{0x4d, 0xc8},
{0x4e, 0x06}, //light 100lux
{0x4c, 0x02},
{0x4d, 0xa8},
{0x4e, 0x06}, //light
{0x4c, 0x02},
{0x4d, 0xa9},
{0x4e, 0x06}, //light
{0x4c, 0x02},
{0x4d, 0x89},
{0x4e, 0x06}, //400lux
{0x4c, 0x02},
{0x4d, 0x69},
{0x4e, 0x06}, //f12
{0x4c, 0x02},
{0x4d, 0x6a},
{0x4e, 0x06}, //f12
{0x4c, 0x02},
{0x4d, 0xc7},
{0x4e, 0x07},
{0x4c, 0x02},
{0x4d, 0xe7},
{0x4e, 0x07}, //100lux
{0x4c, 0x03},
{0x4d, 0x07},
{0x4e, 0x07}, //light
{0x4c, 0x02},
{0x4d, 0xe8},
{0x4e, 0x07},
{0x4c, 0x02},
{0x4d, 0xe9},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x08},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x09},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x27},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x28},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x29},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x47},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x48},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x49},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x67},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x68},
{0x4e, 0x07},
{0x4c, 0x03},
{0x4d, 0x69},
{0x4e, 0x07},
{0x4f, 0x01},
{0xfe, 0x01},
{0x50, 0x80}, //AWB_PRE_mode
{0x51, 0xa8}, //AWB_pre_THD_min[7:0]
{0x52, 0x57}, //AWB_pre_THD_min[15:8] Dominiate luma 0.25=639c 0.22=57a8
{0x53, 0x38}, //AWB_pre_THD_min_MIX[7:0]
{0x54, 0xc7}, //AWB_pre_THD_min_MIX[15:8] Mix luma 0.5
{0x56, 0x0e}, //AWB_tone mode
{0x58, 0x08}, //AWB_C_num_sel,AWB_D_num_sel
{0x5b, 0x00}, //AWB_mix_mode
{0x5c, 0x74}, //green_num0[7:0]
{0x5d, 0x8b}, //green_num0[15:8] 0.35
{0x61, 0xd3}, //R2G_stand0
{0x62, 0xb5}, //B2G_stand0
{0x63, 0x00}, //88//a4 //AWB gray mode [7]enable
{0x65, 0x04}, //AWB margin
{0x67, 0xb2}, //R2G_stand3[7:0] FF/CWF
{0x68, 0xac}, //B2G_stand3[7:0]
{0x69, 0x00}, //R2G_stand4[9:8] B2G_stand4[9:8] R2G_stand3[9:8] B2G_stand3[9:8]
{0x6a, 0xb2}, //R2G_stand4[7:0] TL84/TL84&CWF
{0x6b, 0xac}, //B2G_stand4[7:0]
{0x6c, 0xb2}, //R2G_stand5[7:0] A
{0x6d, 0xac}, //B2G_stand5[7:0]
{0x6e, 0x40}, //AWB_skin_weight R2G_stand5[9:8] B2G_stand5[9:8]
{0x6f, 0x18}, //AWB_indoor_THD (0x21=17 caculate)
{0x73, 0x00}, //AWB_indoor_mode
{0x70, 0x10}, //AWB low luma TH
{0x71, 0xe8}, //AWB outdoor TH
{0x72, 0xc0}, //outdoor mode
{0x74, 0x01}, //[2:0]AWB skip mode 2x2,4x4,4x8,8x8
{0x75, 0x01}, //[1:0]AWB_every_N
{0x7f, 0x08}, //[3]gray world frame start
{0x76, 0x70}, //R limit
{0x77, 0x58}, //G limit
{0x78, 0xa0}, //d8 //B limit
{0xfe, 0x00},
//
//////////////////////////////////////////
/////////// CC ////////////////////////
//////////////////////////////////////////
{0xfe, 0x02},
{0xc0, 0x01}, //[5:4] CC mode [0]CCT enable
{0xC1, 0x50}, //D50/D65
{0xc2, 0xF9},
{0xc3, 0x00}, //0
{0xc4, 0xe8}, //e0
{0xc5, 0x48},
{0xc6, 0xf0},
{0xC7, 0x50},
{0xc8, 0xf2},
{0xc9, 0x00},
{0xcA, 0xE0},
{0xcB, 0x45},
{0xcC, 0xec},
{0xCd, 0x45},
{0xce, 0xf0},
{0xcf, 0x00},
{0xe3, 0xf0},
{0xe4, 0x45},
{0xe5, 0xe8},
{0xfe, 0x00},
{0xf2, 0x0f},
//////////////frame rate 50Hz
{0xfe, 0x00},
{0xf7, 0x1d},
{0xf8, 0x84},
{0xfa, 0x00},
{0x05, 0x01}, //hb
{0x06, 0x3b},
{0x07, 0x01}, //Vb
{0x08, 0x0b},
{0xfe, 0x01},
{0x25, 0x01},
{0x26, 0x32}, //step
{0x27, 0x03}, //8.15fps
{0x28, 0x96},
{0x29, 0x03}, //8.15fps
{0x2a, 0x96},
{0x2b, 0x03}, //8.15fps
{0x2c, 0x96},
{0x2d, 0x04}, //8.15fps
{0x2e, 0x62},
{0x3c, 0x00},
{0xfe, 0x00},
/////////dark sun//////
{0xfe, 0x00},
{0x18, 0x22},
{0xfe, 0x02},
{0x40, 0xbf},
{0x46, 0xcf},
{0xfe, 0x00},
{0xfe, 0x00},
{0xf7, 0x1d},
{0xf8, 0x84},
{0xfa, 0x10},
{0x05, 0x01}, //hb
{0x06, 0x18},
{0x07, 0x00}, //Vb
{0x08, 0x2e},
{0xfe, 0x01},
{0x25, 0x00},
{0x26, 0xa2}, //step
{0x27, 0x01},
{0x28, 0xe6},
{0x29, 0x01},
{0x2a, 0xe6},
{0x2b, 0x01},
{0x2c, 0xe6},
{0x2d, 0x04}, // AEC_exp_level4[12:8]
{0x2e, 0x62}, // AEC_exp_level4[7:0]
{0x3c, 0x00},
{0xfe, 0x00},
{0x09, 0x01}, //row start
{0x0a, 0xd0}, //
{0x0b, 0x02}, //col start
{0x0c, 0x70},
{0x0d, 0x01}, //height
{0x0e, 0x00},
{0x0f, 0x01}, //width
{0x10, 0x50},
{0x90, 0x01}, //crop
{0x91, 0x00},
{0x92, 0x00},
{0x93, 0x00},
{0x94, 0x00},
{0x95, 0x00},
{0x96, 0xf0},
{0x97, 0x01},
{0x98, 0x40},
{REGLIST_TAIL, 0x00},
};

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* NT99141 driver.
*
*/
#ifndef __NT99141_H__
#define __NT99141_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int nt99141_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int nt99141_init(sensor_t *sensor);
#endif // __NT99141_H__

View File

@@ -0,0 +1,211 @@
/*
* NT99141 register definitions.
*/
#ifndef __NT99141_REG_REGS_H__
#define __NT99141_REG_REGS_H__
/* system control registers */
#define SYSTEM_CTROL0 0x3021 // Bit[7]: Software reset
// Bit[6]: Software power down
// Bit[5]: Reserved
// Bit[4]: SRB clock SYNC enable
// Bit[3]: Isolation suspend select
// Bit[2:0]: Not used
/* output format control registers */
#define FORMAT_CTRL 0x501F // Format select
// Bit[2:0]:
// 000: YUV422
// 001: RGB
// 010: Dither
// 011: RAW after DPC
// 101: RAW after CIP
/* format control registers */
#define FORMAT_CTRL00 0x4300
/* frame control registers */
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// Bit[3:0]: Frame ON number
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// BIT[3:0]: Frame OFF number
/* ISP top control registers */
#define PRE_ISP_TEST_SETTING_1 0x3025 // Bit[7]: Test enable
// 0: Test disable
// 1: Color bar enable
// Bit[6]: Rolling
// Bit[5]: Transparent
// Bit[4]: Square black and white
// Bit[3:2]: Color bar style
// 00: Standard 8 color bar
// 01: Gradual change at vertical mode 1
// 10: Gradual change at horizontal
// 11: Gradual change at vertical mode 2
// Bit[1:0]: Test select
// 00: Color bar
// 01: Random data
// 10: Square data
// 11: Black image
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
/* AEC/AGC control functions */
#define AEC_PK_MANUAL 0x3201 // AEC Manual Mode Control
// Bit[7:6]: Reserved
// Bit[5]: Gain delay option
// Valid when 0x3503[4]=1b0
// 0: Delay one frame latch
// 1: One frame latch
// Bit[4:2]: Reserved
// Bit[1]: AGC manual
// 0: Auto enable
// 1: Manual enable
// Bit[0]: AEC manual
// 0: Auto enable
// 1: Manual enable
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
/* mirror and flip registers */
#define TIMING_TC_REG20 0x3022 // Timing Control Register
// Bit[2:1]: Vertical flip enable
// 00: Normal
// 11: Vertical flip
// Bit[0]: Vertical binning enable
#define TIMING_TC_REG21 0x3022 // Timing Control Register
// Bit[5]: Compression Enable
// Bit[2:1]: Horizontal mirror enable
// 00: Normal
// 11: Horizontal mirror
// Bit[0]: Horizontal binning enable
#define CLOCK_POL_CONTROL 0x3024// Bit[5]: PCLK polarity 0: active low
// 1: active high
// Bit[3]: Gate PCLK under VSYNC
// Bit[2]: Gate PCLK under HREF
// Bit[1]: HREF polarity
// 0: active low
// 1: active high
// Bit[0] VSYNC polarity
// 0: active low
// 1: active high
#define DRIVE_CAPABILITY 0x306a // Bit[7:6]:
// 00: 1x
// 01: 2x
// 10: 3x
// 11: 4x
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
// Size after scaling
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
#define X_OFFSET_L 0x3811 //Bit[7:0]:
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
//Bit[3:0]: Horizontal even subsample increment
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
//Bit[3:0]: Vertical even subsample increment
// Size before scaling
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
#define ISP_CONTROL_01 0x3021 // Bit[5]: Scale enable
// 0: Disable
// 1: Enable
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
// Bit[2:0]: VDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
// 0: Auto
// 1: Manual by PCLK_RATIO
#define VFIFO_X_SIZE_H 0x4602
#define VFIFO_X_SIZE_L 0x4603
#define VFIFO_Y_SIZE_H 0x4604
#define VFIFO_Y_SIZE_L 0x4605
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
// Bit[3:0]: PLLS system divider
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
// 00: 1
// 01: 1.5
// 10: 2
// 11: 3
// Bit[2]: PLLS root-divider - 1
// Bit[1:0]: PLLS seld5
// 00: 1
// 01: 1
// 10: 2
// 11: 2.5
#define COMPRESSION_CTRL00 0x4400 //
#define COMPRESSION_CTRL01 0x4401 //
#define COMPRESSION_CTRL02 0x4402 //
#define COMPRESSION_CTRL03 0x4403 //
#define COMPRESSION_CTRL04 0x4404 //
#define COMPRESSION_CTRL05 0x4405 //
#define COMPRESSION_CTRL06 0x4406 //
#define COMPRESSION_CTRL07 0x3401 // Bit[5:0]: QS
#define COMPRESSION_ISI_CTRL 0x4408 //
#define COMPRESSION_CTRL09 0x4409 //
#define COMPRESSION_CTRL0a 0x440a //
#define COMPRESSION_CTRL0b 0x440b //
#define COMPRESSION_CTRL0c 0x440c //
#define COMPRESSION_CTRL0d 0x440d //
#define COMPRESSION_CTRL0E 0x440e //
/**
* @brief register value
*/
#define TEST_COLOR_BAR 0x02 /* Enable Color Bar roling Test */
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
#define TIMING_TC_REG20_VFLIP 0x01 /* Vertical flip enable */
#define TIMING_TC_REG21_HMIRROR 0x02 /* Horizontal mirror enable */
#endif // __NT99141_REG_REGS_H__

View File

@@ -0,0 +1,824 @@
#ifndef _NT99141_SETTINGS_H_
#define _NT99141_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "nt99141_regs.h"
static const ratio_settings_t ratio_table[] = {
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
{ 1280, 720, 0, 4, 1283, 723, 0, 4, 1660, 963 },
};
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000
static const uint16_t sensor_default_regs[][2] = {
//initial
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x3109, 0x04},
{0x3040, 0x04},
{0x3041, 0x02},
{0x3042, 0xFF},
{0x3043, 0x08},
{0x3052, 0xE0},
{0x305F, 0x33},
{0x3100, 0x07},
{0x3106, 0x03},
{0x3105, 0x01},
{0x3108, 0x05},
{0x3110, 0x22},
{0x3111, 0x57},
{0x3112, 0x22},
{0x3113, 0x55},
{0x3114, 0x05},
{0x3135, 0x00},
{0x32F0, 0x01},
{0x3290, 0x01},
{0x3291, 0x80},
{0x3296, 0x01},
{0x3297, 0x73},
{0x3250, 0x80},
{0x3251, 0x03},
{0x3252, 0xFF},
{0x3253, 0x00},
{0x3254, 0x03},
{0x3255, 0xFF},
{0x3256, 0x00},
{0x3257, 0x50},
{0x3270, 0x00},
{0x3271, 0x0C},
{0x3272, 0x18},
{0x3273, 0x32},
{0x3274, 0x44},
{0x3275, 0x54},
{0x3276, 0x70},
{0x3277, 0x88},
{0x3278, 0x9D},
{0x3279, 0xB0},
{0x327A, 0xCF},
{0x327B, 0xE2},
{0x327C, 0xEF},
{0x327D, 0xF7},
{0x327E, 0xFF},
{0x3302, 0x00},
{0x3303, 0x40},
{0x3304, 0x00},
{0x3305, 0x96},
{0x3306, 0x00},
{0x3307, 0x29},
{0x3308, 0x07},
{0x3309, 0xBA},
{0x330A, 0x06},
{0x330B, 0xF5},
{0x330C, 0x01},
{0x330D, 0x51},
{0x330E, 0x01},
{0x330F, 0x30},
{0x3310, 0x07},
{0x3311, 0x16},
{0x3312, 0x07},
{0x3313, 0xBA},
{0x3326, 0x02},
{0x32F6, 0x0F},
{0x32F9, 0x42},
{0x32FA, 0x24},
{0x3325, 0x4A},
{0x3330, 0x00},
{0x3331, 0x0A},
{0x3332, 0xFF},
{0x3338, 0x30},
{0x3339, 0x84},
{0x333A, 0x48},
{0x333F, 0x07},
{0x3360, 0x10},
{0x3361, 0x18},
{0x3362, 0x1f},
{0x3363, 0x37},
{0x3364, 0x80},
{0x3365, 0x80},
{0x3366, 0x68},
{0x3367, 0x60},
{0x3368, 0x30},
{0x3369, 0x28},
{0x336A, 0x20},
{0x336B, 0x10},
{0x336C, 0x00},
{0x336D, 0x20},
{0x336E, 0x1C},
{0x336F, 0x18},
{0x3370, 0x10},
{0x3371, 0x38},
{0x3372, 0x3C},
{0x3373, 0x3F},
{0x3374, 0x3F},
{0x338A, 0x34},
{0x338B, 0x7F},
{0x338C, 0x10},
{0x338D, 0x23},
{0x338E, 0x7F},
{0x338F, 0x14},
{0x3375, 0x08},
{0x3376, 0x0C},
{0x3377, 0x18},
{0x3378, 0x20},
{0x3012, 0x02},
{0x3013, 0xD0},
{0x3025, 0x02}, //colorbar
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_jpeg[][2] = {
{0x32F0, 0x70}, // YUV422
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_raw[][2] = {
{0x32F0, 0x50}, // RAW
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_grayscale[][2] = {
{0x32F1, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_yuv422[][2] = {
{0x32F0, 0x00}, // YUV422
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_rgb565[][2] = {
{0x32F0, 0x01}, // RGB
{REGLIST_TAIL, 0x00}, // tail
};
static const uint8_t sensor_saturation_levels[9][1] = {
{0x60},//-4
{0x68},//-3
{0x70},//-2
{0x78},//-1
{0x80},//0
{0x88},//+1
{0x90},//+2
{0x98},//+3
{0xA0},//+4
};
static const uint8_t sensor_special_effects[7][4] = {
{0x00, 0x80, 0x80, 0x01},//Normal
{0x03, 0x80, 0x80, 0x01},//Negative
{0x01, 0x80, 0x80, 0x01},//Grayscale
{0x05, 0x2A, 0xF0, 0x01},//Red Tint
{0x05, 0x60, 0x20, 0x01},//Green Tint
{0x05, 0xF0, 0x80, 0x01},//Blue Tint
{0x02, 0x80, 0x80, 0x01},//Sepia
};
// AE LEVEL
static const uint16_t sensor_ae_level[][2] = {
// 1. [AE_Target : 0x24]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x29 },
{0x32B9, 0x1F },
{0x32BC, 0x24 },
{0x32BD, 0x27 },
{0x32BE, 0x21 },
//------------------------------------------------------------------------
// 2. [AE_Target : 0x28]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x2D },
{0x32B9, 0x23 },
{0x32BC, 0x28 },
{0x32BD, 0x2B },
{0x32BE, 0x25 },
//------------------------------------------------------------------------
// 3. [AE_Target : 0x2C]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x32 },
{0x32B9, 0x26 },
{0x32BC, 0x2C },
{0x32BD, 0x2F },
{0x32BE, 0x29 },
//------------------------------------------------------------------------
// 4, [AE_Target : 0x30]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x36 },
{0x32B9, 0x2A },
{0x32BC, 0x30 },
{0x32BD, 0x33 },
{0x32BE, 0x2D },
//------------------------------------------------------------------------
// 5. [AE_Target : 0x34]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x3B },
{0x32B9, 0x2D },
{0x32BC, 0x34 },
{0x32BD, 0x38 },
{0x32BE, 0x30 },
//------------------------------------------------------------------------
// 6. [AE_Target : 0x38]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x3F },
{0x32B9, 0x31 },
{0x32BC, 0x38 },
{0x32BD, 0x3C },
{0x32BE, 0x34 },
//------------------------------------------------------------------------
// 7. [AE_Target : 0x3D]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x44 },
{0x32B9, 0x34 },
{0x32BC, 0x3C },
{0x32BD, 0x40 },
{0x32BE, 0x38 },
//------------------------------------------------------------------------
// 8. [AE_Target : 0x40]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x48 },
{0x32B9, 0x38 },
{0x32BC, 0x40 },
{0x32BD, 0x44 },
{0x32BE, 0x3C },
//------------------------------------------------------------------------
// 9. [AE_Target : 0x44]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x32B8, 0x4D },
{0x32B9, 0x3B },
{0x32BC, 0x44 },
{0x32BD, 0x49 },
{0x32BE, 0x3F },
};
static const uint16_t sensor_framesize_HD[][2] = {
//[JPEG_1280x720_8.18_8.18_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x3C},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x5E},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x24},
{0x3002, 0x00},
{0x3003, 0x04},
{0x3004, 0x00},
{0x3005, 0x04},
{0x3006, 0x05},
{0x3007, 0x03},
{0x3008, 0x02},
{0x3009, 0xD3},
{0x300A, 0x06},
{0x300B, 0x7C},
{0x300C, 0x02},
{0x300D, 0xE0},
{0x300E, 0x05},
{0x300F, 0x00},
{0x3010, 0x02},
{0x3011, 0xD0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x3F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_VGA[][2] = {
//[JPEG_640x480_10.14_10.14_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x4B},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x62},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x32E0, 0x02},
{0x32E1, 0x80},
{0x32E2, 0x01},
{0x32E3, 0xE0},
{0x32E4, 0x00},
{0x32E5, 0x80},
{0x32E6, 0x00},
{0x32E7, 0x80},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x24},
{0x3002, 0x00},
{0x3003, 0xA4},
{0x3004, 0x00},
{0x3005, 0x04},
{0x3006, 0x04},
{0x3007, 0x63},
{0x3008, 0x02},
{0x3009, 0xD3},
{0x300A, 0x05},
{0x300B, 0x3C},
{0x300C, 0x02},
{0x300D, 0xE0},
{0x300E, 0x03},
{0x300F, 0xC0},
{0x3010, 0x02},
{0x3011, 0xD0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x7F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_QVGA[][2] = {
//[JPEG_320x240_10.14_10.14_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x4B},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x62},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x32E0, 0x01},
{0x32E1, 0x40},
{0x32E2, 0x00},
{0x32E3, 0xF0},
{0x32E4, 0x02},
{0x32E5, 0x02},
{0x32E6, 0x02},
{0x32E7, 0x03},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x24},
{0x3002, 0x00},
{0x3003, 0xA4},
{0x3004, 0x00},
{0x3005, 0x04},
{0x3006, 0x04},
{0x3007, 0x63},
{0x3008, 0x02},
{0x3009, 0xD3},
{0x300A, 0x05},
{0x300B, 0x3C},
{0x300C, 0x02},
{0x300D, 0xE0},
{0x300E, 0x03},
{0x300F, 0xC0},
{0x3010, 0x02},
{0x3011, 0xD0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x7F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_VGA_xyskip[][2] = {
// [JPEG_640x360_20.00_25.01_Fps_XY_Skip]
// Set_Device_Format = FORMAT_16_8
// SET_Device_Addr = 0x54
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60 },
{0x320A, 0xB2 },
{0x32C0, 0x64 },
{0x32C1, 0x64 },
{0x32C2, 0x64 },
{0x32C3, 0x00 },
{0x32C4, 0x20 },
{0x32C5, 0x20 },
{0x32C6, 0x20 },
{0x32C7, 0x00 },
{0x32C8, 0x62 },
{0x32C9, 0x64 },
{0x32CA, 0x84 },
{0x32CB, 0x84 },
{0x32CC, 0x84 },
{0x32CD, 0x84 },
{0x32DB, 0x68 },
{0x32F0, 0x70 },
{0x3400, 0x08 },
{0x3400, 0x00 },
{0x3401, 0x4E },
{0x3404, 0x00 },
{0x3405, 0x00 },
{0x3410, 0x00 },
{0x3200, 0x3E },
{0x3201, 0x0F },
{0x3028, 0x0F },
{0x3029, 0x00 },
{0x302A, 0x08 },
{0x3022, 0x24 },
{0x3023, 0x6C },
{0x3002, 0x00 },
{0x3003, 0x04 },
{0x3004, 0x00 },
{0x3005, 0x04 },
{0x3006, 0x05 },
{0x3007, 0x03 },
{0x3008, 0x02 },
{0x3009, 0xD3 },
{0x300A, 0x03 },
{0x300B, 0xFC },
{0x300C, 0x01 },
{0x300D, 0x88 },
{0x300E, 0x02 },
{0x300F, 0x80 },
{0x3010, 0x01 },
{0x3011, 0x68 },
{0x32B8, 0x3F },
{0x32B9, 0x31 },
{0x32BB, 0x87 },
{0x32BC, 0x38 },
{0x32BD, 0x3C },
{0x32BE, 0x34 },
{0x3201, 0x3F },
{0x3025, 0x00 }, //normal
{0x3021, 0x06 },
{0x3400, 0x01 },
{0x3060, 0x01 },
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_VGA_xskip[][2] = {
//[JPEG_640x480_Xskip_13.32_13.32_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x62},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x68},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x32E0, 0x02},
{0x32E1, 0x80},
{0x32E2, 0x01},
{0x32E3, 0xE0},
{0x32E4, 0x00},
{0x32E5, 0x00},
{0x32E6, 0x00},
{0x32E7, 0x80},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x2C},
{0x3002, 0x00},
{0x3003, 0x04},
{0x3004, 0x00},
{0x3005, 0x04},
{0x3006, 0x05},
{0x3007, 0x03},
{0x3008, 0x02},
{0x3009, 0xD3},
{0x300A, 0x03},
{0x300B, 0xFC},
{0x300C, 0x02},
{0x300D, 0xE0},
{0x300E, 0x02},
{0x300F, 0x80},
{0x3010, 0x02},
{0x3011, 0xD0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x7F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_QVGA_xskip[][2] = {
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
//[JPEG_320x240_Xskip_13.32_13.32_Fps]
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x62},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x68},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x32E0, 0x01},
{0x32E1, 0x40},
{0x32E2, 0x00},
{0x32E3, 0xF0},
{0x32E4, 0x01},
{0x32E5, 0x01},
{0x32E6, 0x02},
{0x32E7, 0x03},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x2C},
{0x3002, 0x00},
{0x3003, 0x04},
{0x3004, 0x00},
{0x3005, 0x04},
{0x3006, 0x05},
{0x3007, 0x03},
{0x3008, 0x02},
{0x3009, 0xD3},
{0x300A, 0x03},
{0x300B, 0xFC},
{0x300C, 0x02},
{0x300D, 0xE0},
{0x300E, 0x02},
{0x300F, 0x80},
{0x3010, 0x02},
{0x3011, 0xD0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x7F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_VGA_crop[][2] = {
//[JPEG_640x480_Crop_19.77_19.77_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x62},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x68},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x24},
{0x3002, 0x01},
{0x3003, 0x44},
{0x3004, 0x00},
{0x3005, 0x7C},
{0x3006, 0x03},
{0x3007, 0xC3},
{0x3008, 0x02},
{0x3009, 0x5B},
{0x300A, 0x03},
{0x300B, 0xFC},
{0x300C, 0x01},
{0x300D, 0xF0},
{0x300E, 0x02},
{0x300F, 0x80},
{0x3010, 0x01},
{0x3011, 0xE0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x3F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_framesize_QVGA_crop[][2] = {
//[JPEG_320x240_Crop_19.77_19.77_Fps]
{0x3021, 0x00},
{REG_DLY, 100}, // delay 100ms
{0x32BF, 0x60},
{0x32C0, 0x5A},
{0x32C1, 0x5A},
{0x32C2, 0x5A},
{0x32C3, 0x00},
{0x32C4, 0x20},
{0x32C5, 0x20},
{0x32C6, 0x20},
{0x32C7, 0x00},
{0x32C8, 0x62},
{0x32C9, 0x5A},
{0x32CA, 0x7A},
{0x32CB, 0x7A},
{0x32CC, 0x7A},
{0x32CD, 0x7A},
{0x32DB, 0x68},
{0x32F0, 0x70},
{0x3400, 0x08},
{0x3400, 0x00},
{0x3401, 0x4E},
{0x3404, 0x00},
{0x3405, 0x00},
{0x3410, 0x00},
{0x32E0, 0x01},
{0x32E1, 0x40},
{0x32E2, 0x00},
{0x32E3, 0xF0},
{0x32E4, 0x01},
{0x32E5, 0x01},
{0x32E6, 0x01},
{0x32E7, 0x02},
{0x3200, 0x3E},
{0x3201, 0x0F},
{0x3028, 0x0F},
{0x3029, 0x00},
{0x302A, 0x08},
{0x3022, 0x24},
{0x3023, 0x24},
{0x3002, 0x01},
{0x3003, 0x44},
{0x3004, 0x00},
{0x3005, 0x7C},
{0x3006, 0x03},
{0x3007, 0xC3},
{0x3008, 0x02},
{0x3009, 0x5B},
{0x300A, 0x03},
{0x300B, 0xFC},
{0x300C, 0x01},
{0x300D, 0xF0},
{0x300E, 0x02},
{0x300F, 0x80},
{0x3010, 0x01},
{0x3011, 0xE0},
{0x32B8, 0x3F},
{0x32B9, 0x31},
{0x32BB, 0x87},
{0x32BC, 0x38},
{0x32BD, 0x3C},
{0x32BE, 0x34},
{0x3201, 0x7F},
{0x3021, 0x06},
{0x3025, 0x00}, //normal
{0x3400, 0x01},
{0x3060, 0x01},
{REGLIST_TAIL, 0x00}, // tail
};
#endif

View File

@@ -0,0 +1,32 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV2640 driver.
*
*/
#ifndef __OV2640_H__
#define __OV2640_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov2640_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov2640_init(sensor_t *sensor);
#endif // __OV2640_H__

View File

@@ -0,0 +1,216 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV2640 register definitions.
*/
#ifndef __REG_REGS_H__
#define __REG_REGS_H__
/* DSP register bank FF=0x00*/
#define R_BYPASS 0x05
#define QS 0x44
#define CTRLI 0x50
#define HSIZE 0x51
#define VSIZE 0x52
#define XOFFL 0x53
#define YOFFL 0x54
#define VHYX 0x55
#define DPRP 0x56
#define TEST 0x57
#define ZMOW 0x5A
#define ZMOH 0x5B
#define ZMHH 0x5C
#define BPADDR 0x7C
#define BPDATA 0x7D
#define CTRL2 0x86
#define CTRL3 0x87
#define SIZEL 0x8C
#define HSIZE8 0xC0
#define VSIZE8 0xC1
#define CTRL0 0xC2
#define CTRL1 0xC3
#define R_DVP_SP 0xD3
#define IMAGE_MODE 0xDA
#define RESET 0xE0
#define MS_SP 0xF0
#define SS_ID 0xF7
#define SS_CTRL 0xF7
#define MC_BIST 0xF9
#define MC_AL 0xFA
#define MC_AH 0xFB
#define MC_D 0xFC
#define P_CMD 0xFD
#define P_STATUS 0xFE
#define BANK_SEL 0xFF
#define CTRLI_LP_DP 0x80
#define CTRLI_ROUND 0x40
#define CTRL0_AEC_EN 0x80
#define CTRL0_AEC_SEL 0x40
#define CTRL0_STAT_SEL 0x20
#define CTRL0_VFIRST 0x10
#define CTRL0_YUV422 0x08
#define CTRL0_YUV_EN 0x04
#define CTRL0_RGB_EN 0x02
#define CTRL0_RAW_EN 0x01
#define CTRL2_DCW_EN 0x20
#define CTRL2_SDE_EN 0x10
#define CTRL2_UV_ADJ_EN 0x08
#define CTRL2_UV_AVG_EN 0x04
#define CTRL2_CMX_EN 0x01
#define CTRL3_BPC_EN 0x80
#define CTRL3_WPC_EN 0x40
#define R_DVP_SP_AUTO_MODE 0x80
#define R_BYPASS_DSP_EN 0x00
#define R_BYPASS_DSP_BYPAS 0x01
#define IMAGE_MODE_Y8_DVP_EN 0x40
#define IMAGE_MODE_JPEG_EN 0x10
#define IMAGE_MODE_YUV422 0x00
#define IMAGE_MODE_RAW10 0x04
#define IMAGE_MODE_RGB565 0x08
#define IMAGE_MODE_HREF_VSYNC 0x02
#define IMAGE_MODE_LBYTE_FIRST 0x01
#define RESET_MICROC 0x40
#define RESET_SCCB 0x20
#define RESET_JPEG 0x10
#define RESET_DVP 0x04
#define RESET_IPU 0x02
#define RESET_CIF 0x01
#define MC_BIST_RESET 0x80
#define MC_BIST_BOOT_ROM_SEL 0x40
#define MC_BIST_12KB_SEL 0x20
#define MC_BIST_12KB_MASK 0x30
#define MC_BIST_512KB_SEL 0x08
#define MC_BIST_512KB_MASK 0x0C
#define MC_BIST_BUSY_BIT_R 0x02
#define MC_BIST_MC_RES_ONE_SH_W 0x02
#define MC_BIST_LAUNCH 0x01
typedef enum {
BANK_DSP, BANK_SENSOR, BANK_MAX
} ov2640_bank_t;
/* Sensor register bank FF=0x01*/
#define GAIN 0x00
#define COM1 0x03
#define REG04 0x04
#define REG08 0x08
#define COM2 0x09
#define REG_PID 0x0A
#define REG_VER 0x0B
#define COM3 0x0C
#define COM4 0x0D
#define AEC 0x10
#define CLKRC 0x11
#define COM7 0x12
#define COM8 0x13
#define COM9 0x14 /* AGC gain ceiling */
#define COM10 0x15
#define HSTART 0x17
#define HSTOP 0x18
#define VSTART 0x19
#define VSTOP 0x1A
#define REG_MIDH 0x1C
#define REG_MIDL 0x1D
#define AEW 0x24
#define AEB 0x25
#define VV 0x26
#define REG2A 0x2A
#define FRARL 0x2B
#define ADDVSL 0x2D
#define ADDVSH 0x2E
#define YAVG 0x2F
#define HSDY 0x30
#define HEDY 0x31
#define REG32 0x32
#define ARCOM2 0x34
#define REG45 0x45
#define FLL 0x46
#define FLH 0x47
#define COM19 0x48
#define ZOOMS 0x49
#define COM22 0x4B
#define COM25 0x4E
#define BD50 0x4F
#define BD60 0x50
#define REG5D 0x5D
#define REG5E 0x5E
#define REG5F 0x5F
#define REG60 0x60
#define HISTO_LOW 0x61
#define HISTO_HIGH 0x62
#define REG04_DEFAULT 0x28
#define REG04_HFLIP_IMG 0x80
#define REG04_VFLIP_IMG 0x40
#define REG04_VREF_EN 0x10
#define REG04_HREF_EN 0x08
#define REG04_SET(x) (REG04_DEFAULT|x)
#define COM2_STDBY 0x10
#define COM2_OUT_DRIVE_1x 0x00
#define COM2_OUT_DRIVE_2x 0x01
#define COM2_OUT_DRIVE_3x 0x02
#define COM2_OUT_DRIVE_4x 0x03
#define COM3_DEFAULT 0x38
#define COM3_BAND_50Hz 0x04
#define COM3_BAND_60Hz 0x00
#define COM3_BAND_AUTO 0x02
#define COM3_BAND_SET(x) (COM3_DEFAULT|x)
#define COM7_SRST 0x80
#define COM7_RES_UXGA 0x00 /* UXGA */
#define COM7_RES_SVGA 0x40 /* SVGA */
#define COM7_RES_CIF 0x20 /* CIF */
#define COM7_ZOOM_EN 0x04 /* Enable Zoom */
#define COM7_COLOR_BAR 0x02 /* Enable Color Bar Test */
#define COM8_DEFAULT 0xC0
#define COM8_BNDF_EN 0x20 /* Enable Banding filter */
#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */
#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */
#define COM8_SET(x) (COM8_DEFAULT|x)
#define COM9_DEFAULT 0x08
#define COM9_AGC_GAIN_2x 0x00 /* AGC: 2x */
#define COM9_AGC_GAIN_4x 0x01 /* AGC: 4x */
#define COM9_AGC_GAIN_8x 0x02 /* AGC: 8x */
#define COM9_AGC_GAIN_16x 0x03 /* AGC: 16x */
#define COM9_AGC_GAIN_32x 0x04 /* AGC: 32x */
#define COM9_AGC_GAIN_64x 0x05 /* AGC: 64x */
#define COM9_AGC_GAIN_128x 0x06 /* AGC: 128x */
#define COM9_AGC_SET(x) (COM9_DEFAULT|(x<<5))
#define COM10_HREF_EN 0x80 /* HSYNC changes to HREF */
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
#define COM10_PCLK_FREE 0x20 /* PCLK output option: free running PCLK */
#define COM10_PCLK_EDGE 0x10 /* Data is updated at the rising edge of PCLK */
#define COM10_HREF_NEG 0x08 /* HREF negative */
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
#define COM10_HSYNC_NEG 0x01 /* HSYNC negative */
#define CTRL1_AWB 0x08 /* Enable AWB */
#define VV_AGC_TH_SET(h,l) ((h<<4)|(l&0x0F))
#define REG32_UXGA 0x36
#define REG32_SVGA 0x09
#define REG32_CIF 0x89
#define CLKRC_2X 0x80
#define CLKRC_2X_UXGA (0x01 | CLKRC_2X)
#define CLKRC_2X_SVGA CLKRC_2X
#define CLKRC_2X_CIF CLKRC_2X
#endif //__REG_REGS_H__

View File

@@ -0,0 +1,484 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _OV2640_SETTINGS_H_
#define _OV2640_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "ov2640_regs.h"
typedef enum {
OV2640_MODE_UXGA, OV2640_MODE_SVGA, OV2640_MODE_CIF, OV2640_MODE_MAX
} ov2640_sensor_mode_t;
typedef struct {
union {
struct {
uint8_t pclk_div:7;
uint8_t pclk_auto:1;
};
uint8_t pclk;
};
union {
struct {
uint8_t clk_div:6;
uint8_t reserved:1;
uint8_t clk_2x:1;
};
uint8_t clk;
};
} ov2640_clk_t;
typedef struct {
uint16_t offset_x;
uint16_t offset_y;
uint16_t max_x;
uint16_t max_y;
} ov2640_ratio_settings_t;
static const ov2640_ratio_settings_t ratio_table[] = {
// ox, oy, mx, my
{ 0, 0, 1600, 1200 }, //4x3
{ 8, 72, 1584, 1056 }, //3x2
{ 0, 100, 1600, 1000 }, //16x10
{ 0, 120, 1600, 960 }, //5x3
{ 0, 150, 1600, 900 }, //16x9
{ 2, 258, 1596, 684 }, //21x9
{ 50, 0, 1500, 1200 }, //5x4
{ 200, 0, 1200, 1200 }, //1x1
{ 462, 0, 676, 1200 } //9x16
};
// 30fps@24MHz
const uint8_t ov2640_settings_cif[][2] = {
{BANK_SEL, BANK_DSP},
{0x2c, 0xff},
{0x2e, 0xdf},
{BANK_SEL, BANK_SENSOR},
{0x3c, 0x32},
{CLKRC, 0x01},
{COM2, COM2_OUT_DRIVE_3x},
{REG04, REG04_DEFAULT},
{COM8, COM8_DEFAULT | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN},
{COM9, COM9_AGC_SET(COM9_AGC_GAIN_8x)},
{0x2c, 0x0c},
{0x33, 0x78},
{0x3a, 0x33},
{0x3b, 0xfB},
{0x3e, 0x00},
{0x43, 0x11},
{0x16, 0x10},
{0x39, 0x92},
{0x35, 0xda},
{0x22, 0x1a},
{0x37, 0xc3},
{0x23, 0x00},
{ARCOM2, 0xc0},
{0x06, 0x88},
{0x07, 0xc0},
{COM4, 0x87},
{0x0e, 0x41},
{0x4c, 0x00},
{0x4a, 0x81},
{0x21, 0x99},
{AEW, 0x40},
{AEB, 0x38},
{VV, VV_AGC_TH_SET(8,2)},
{0x5c, 0x00},
{0x63, 0x00},
{HISTO_LOW, 0x70},
{HISTO_HIGH, 0x80},
{0x7c, 0x05},
{0x20, 0x80},
{0x28, 0x30},
{0x6c, 0x00},
{0x6d, 0x80},
{0x6e, 0x00},
{0x70, 0x02},
{0x71, 0x94},
{0x73, 0xc1},
{0x3d, 0x34},
{0x5a, 0x57},
{BD50, 0xbb},
{BD60, 0x9c},
{COM7, COM7_RES_CIF},
{HSTART, 0x11},
{HSTOP, 0x43},
{VSTART, 0x00},
{VSTOP, 0x25},
{REG32, 0x89},
{0x37, 0xc0},
{BD50, 0xca},
{BD60, 0xa8},
{0x6d, 0x00},
{0x3d, 0x38},
{BANK_SEL, BANK_DSP},
{0xe5, 0x7f},
{MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL},
{0x41, 0x24},
{RESET, RESET_JPEG | RESET_DVP},
{0x76, 0xff},
{0x33, 0xa0},
{0x42, 0x20},
{0x43, 0x18},
{0x4c, 0x00},
{CTRL3, CTRL3_WPC_EN | 0x10 },
{0x88, 0x3f},
{0xd7, 0x03},
{0xd9, 0x10},
{R_DVP_SP, R_DVP_SP_AUTO_MODE | 0x02},
{0xc8, 0x08},
{0xc9, 0x80},
{BPADDR, 0x00},
{BPDATA, 0x00},
{BPADDR, 0x03},
{BPDATA, 0x48},
{BPDATA, 0x48},
{BPADDR, 0x08},
{BPDATA, 0x20},
{BPDATA, 0x10},
{BPDATA, 0x0e},
{0x90, 0x00},
{0x91, 0x0e},
{0x91, 0x1a},
{0x91, 0x31},
{0x91, 0x5a},
{0x91, 0x69},
{0x91, 0x75},
{0x91, 0x7e},
{0x91, 0x88},
{0x91, 0x8f},
{0x91, 0x96},
{0x91, 0xa3},
{0x91, 0xaf},
{0x91, 0xc4},
{0x91, 0xd7},
{0x91, 0xe8},
{0x91, 0x20},
{0x92, 0x00},
{0x93, 0x06},
{0x93, 0xe3},
{0x93, 0x05},
{0x93, 0x05},
{0x93, 0x00},
{0x93, 0x04},
{0x93, 0x00},
{0x93, 0x00},
{0x93, 0x00},
{0x93, 0x00},
{0x93, 0x00},
{0x93, 0x00},
{0x93, 0x00},
{0x96, 0x00},
{0x97, 0x08},
{0x97, 0x19},
{0x97, 0x02},
{0x97, 0x0c},
{0x97, 0x24},
{0x97, 0x30},
{0x97, 0x28},
{0x97, 0x26},
{0x97, 0x02},
{0x97, 0x98},
{0x97, 0x80},
{0x97, 0x00},
{0x97, 0x00},
{0xa4, 0x00},
{0xa8, 0x00},
{0xc5, 0x11},
{0xc6, 0x51},
{0xbf, 0x80},
{0xc7, 0x10},
{0xb6, 0x66},
{0xb8, 0xA5},
{0xb7, 0x64},
{0xb9, 0x7C},
{0xb3, 0xaf},
{0xb4, 0x97},
{0xb5, 0xFF},
{0xb0, 0xC5},
{0xb1, 0x94},
{0xb2, 0x0f},
{0xc4, 0x5c},
{CTRL1, 0xfd},
{0x7f, 0x00},
{0xe5, 0x1f},
{0xe1, 0x67},
{0xdd, 0x7f},
{IMAGE_MODE, 0x00},
{RESET, 0x00},
{R_BYPASS, R_BYPASS_DSP_EN},
{0, 0}
};
const uint8_t ov2640_settings_to_cif[][2] = {
{BANK_SEL, BANK_SENSOR},
{COM7, COM7_RES_CIF},
//Set the sensor output window
{COM1, 0x0A},
{REG32, REG32_CIF},
{HSTART, 0x11},
{HSTOP, 0x43},
{VSTART, 0x00},
{VSTOP, 0x25},
//{CLKRC, 0x00},
{BD50, 0xca},
{BD60, 0xa8},
{0x5a, 0x23},
{0x6d, 0x00},
{0x3d, 0x38},
{0x39, 0x92},
{0x35, 0xda},
{0x22, 0x1a},
{0x37, 0xc3},
{0x23, 0x00},
{ARCOM2, 0xc0},
{0x06, 0x88},
{0x07, 0xc0},
{COM4, 0x87},
{0x0e, 0x41},
{0x4c, 0x00},
{BANK_SEL, BANK_DSP},
{RESET, RESET_DVP},
//Set the sensor resolution (UXGA, SVGA, CIF)
{HSIZE8, 0x32},
{VSIZE8, 0x25},
{SIZEL, 0x00},
//Set the image window size >= output size
{HSIZE, 0x64},
{VSIZE, 0x4a},
{XOFFL, 0x00},
{YOFFL, 0x00},
{VHYX, 0x00},
{TEST, 0x00},
{CTRL2, CTRL2_DCW_EN | 0x1D},
{CTRLI, CTRLI_LP_DP | 0x00},
//{R_DVP_SP, 0x08},
{0, 0}
};
const uint8_t ov2640_settings_to_svga[][2] = {
{BANK_SEL, BANK_SENSOR},
{COM7, COM7_RES_SVGA},
//Set the sensor output window
{COM1, 0x0A},
{REG32, REG32_SVGA},
{HSTART, 0x11},
{HSTOP, 0x43},
{VSTART, 0x00},
{VSTOP, 0x4b},
//{CLKRC, 0x00},
{0x37, 0xc0},
{BD50, 0xca},
{BD60, 0xa8},
{0x5a, 0x23},
{0x6d, 0x00},
{0x3d, 0x38},
{0x39, 0x92},
{0x35, 0xda},
{0x22, 0x1a},
{0x37, 0xc3},
{0x23, 0x00},
{ARCOM2, 0xc0},
{0x06, 0x88},
{0x07, 0xc0},
{COM4, 0x87},
{0x0e, 0x41},
{0x42, 0x03},
{0x4c, 0x00},
{BANK_SEL, BANK_DSP},
{RESET, RESET_DVP},
//Set the sensor resolution (UXGA, SVGA, CIF)
{HSIZE8, 0x64},
{VSIZE8, 0x4B},
{SIZEL, 0x00},
//Set the image window size >= output size
{HSIZE, 0xC8},
{VSIZE, 0x96},
{XOFFL, 0x00},
{YOFFL, 0x00},
{VHYX, 0x00},
{TEST, 0x00},
{CTRL2, CTRL2_DCW_EN | 0x1D},
{CTRLI, CTRLI_LP_DP | 0x00},
//{R_DVP_SP, 0x08},
{0, 0}
};
const uint8_t ov2640_settings_to_uxga[][2] = {
{BANK_SEL, BANK_SENSOR},
{COM7, COM7_RES_UXGA},
//Set the sensor output window
{COM1, 0x0F},
{REG32, REG32_UXGA},
{HSTART, 0x11},
{HSTOP, 0x75},
{VSTART, 0x01},
{VSTOP, 0x97},
//{CLKRC, 0x00},
{0x3d, 0x34},
{BD50, 0xbb},
{BD60, 0x9c},
{0x5a, 0x57},
{0x6d, 0x80},
{0x39, 0x82},
{0x23, 0x00},
{0x07, 0xc0},
{0x4c, 0x00},
{0x35, 0x88},
{0x22, 0x0a},
{0x37, 0x40},
{ARCOM2, 0xa0},
{0x06, 0x02},
{COM4, 0xb7},
{0x0e, 0x01},
{0x42, 0x83},
{BANK_SEL, BANK_DSP},
{RESET, RESET_DVP},
//Set the sensor resolution (UXGA, SVGA, CIF)
{HSIZE8, 0xc8},
{VSIZE8, 0x96},
{SIZEL, 0x00},
//Set the image window size >= output size
{HSIZE, 0x90},
{VSIZE, 0x2c},
{XOFFL, 0x00},
{YOFFL, 0x00},
{VHYX, 0x88},
{TEST, 0x00},
{CTRL2, CTRL2_DCW_EN | 0x1d},
{CTRLI, 0x00},
//{R_DVP_SP, 0x06},
{0, 0}
};
const uint8_t ov2640_settings_jpeg3[][2] = {
{BANK_SEL, BANK_DSP},
{RESET, RESET_JPEG | RESET_DVP},
{IMAGE_MODE, IMAGE_MODE_JPEG_EN | IMAGE_MODE_HREF_VSYNC},
{0xD7, 0x03},
{0xE1, 0x77},
{0xE5, 0x1F},
{0xD9, 0x10},
{0xDF, 0x80},
{0x33, 0x80},
{0x3C, 0x10},
{0xEB, 0x30},
{0xDD, 0x7F},
{RESET, 0x00},
{0, 0}
};
static const uint8_t ov2640_settings_yuv422[][2] = {
{BANK_SEL, BANK_DSP},
{RESET, RESET_DVP},
{IMAGE_MODE, IMAGE_MODE_YUV422},
{0xD7, 0x01},
{0xE1, 0x67},
{RESET, 0x00},
{0, 0},
};
static const uint8_t ov2640_settings_rgb565[][2] = {
{BANK_SEL, BANK_DSP},
{RESET, RESET_DVP},
{IMAGE_MODE, IMAGE_MODE_RGB565},
{0xD7, 0x03},
{0xE1, 0x77},
{RESET, 0x00},
{0, 0},
};
#define NUM_BRIGHTNESS_LEVELS (5)
static const uint8_t brightness_regs[NUM_BRIGHTNESS_LEVELS + 1][5] = {
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
{0x00, 0x04, 0x09, 0x00, 0x00 }, /* -2 */
{0x00, 0x04, 0x09, 0x10, 0x00 }, /* -1 */
{0x00, 0x04, 0x09, 0x20, 0x00 }, /* 0 */
{0x00, 0x04, 0x09, 0x30, 0x00 }, /* +1 */
{0x00, 0x04, 0x09, 0x40, 0x00 }, /* +2 */
};
#define NUM_CONTRAST_LEVELS (5)
static const uint8_t contrast_regs[NUM_CONTRAST_LEVELS + 1][7] = {
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA, BPDATA, BPDATA },
{0x00, 0x04, 0x07, 0x20, 0x18, 0x34, 0x06 }, /* -2 */
{0x00, 0x04, 0x07, 0x20, 0x1c, 0x2a, 0x06 }, /* -1 */
{0x00, 0x04, 0x07, 0x20, 0x20, 0x20, 0x06 }, /* 0 */
{0x00, 0x04, 0x07, 0x20, 0x24, 0x16, 0x06 }, /* +1 */
{0x00, 0x04, 0x07, 0x20, 0x28, 0x0c, 0x06 }, /* +2 */
};
#define NUM_SATURATION_LEVELS (5)
static const uint8_t saturation_regs[NUM_SATURATION_LEVELS + 1][5] = {
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
{0x00, 0x02, 0x03, 0x28, 0x28 }, /* -2 */
{0x00, 0x02, 0x03, 0x38, 0x38 }, /* -1 */
{0x00, 0x02, 0x03, 0x48, 0x48 }, /* 0 */
{0x00, 0x02, 0x03, 0x58, 0x58 }, /* +1 */
{0x00, 0x02, 0x03, 0x68, 0x68 }, /* +2 */
};
#define NUM_SPECIAL_EFFECTS (7)
static const uint8_t special_effects_regs[NUM_SPECIAL_EFFECTS + 1][5] = {
{BPADDR, BPDATA, BPADDR, BPDATA, BPDATA },
{0x00, 0X00, 0x05, 0X80, 0X80 }, /* no effect */
{0x00, 0X40, 0x05, 0X80, 0X80 }, /* negative */
{0x00, 0X18, 0x05, 0X80, 0X80 }, /* black and white */
{0x00, 0X18, 0x05, 0X40, 0XC0 }, /* reddish */
{0x00, 0X18, 0x05, 0X40, 0X40 }, /* greenish */
{0x00, 0X18, 0x05, 0XA0, 0X40 }, /* blue */
{0x00, 0X18, 0x05, 0X40, 0XA6 }, /* retro */
};
#define NUM_WB_MODES (4)
static const uint8_t wb_modes_regs[NUM_WB_MODES + 1][3] = {
{0XCC, 0XCD, 0XCE },
{0x5E, 0X41, 0x54 }, /* sunny */
{0x65, 0X41, 0x4F }, /* cloudy */
{0x52, 0X41, 0x66 }, /* office */
{0x42, 0X3F, 0x71 }, /* home */
};
#define NUM_AE_LEVELS (5)
static const uint8_t ae_levels_regs[NUM_AE_LEVELS + 1][3] = {
{ AEW, AEB, VV },
{0x20, 0X18, 0x60 },
{0x34, 0X1C, 0x00 },
{0x3E, 0X38, 0x81 },
{0x48, 0X40, 0x81 },
{0x58, 0X50, 0x92 },
};
const uint8_t agc_gain_tbl[31] = {
0x00, 0x10, 0x18, 0x30, 0x34, 0x38, 0x3C, 0x70, 0x72, 0x74, 0x76, 0x78, 0x7A, 0x7C, 0x7E, 0xF0,
0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
#endif /* _OV2640_SETTINGS_H_ */

View File

@@ -0,0 +1,34 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV3660 driver.
*
*/
#ifndef __OV3660_H__
#define __OV3660_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov3660_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov3660_init(sensor_t *sensor);
#endif // __OV3660_H__

View File

@@ -0,0 +1,211 @@
/*
* OV3660 register definitions.
*/
#ifndef __OV3660_REG_REGS_H__
#define __OV3660_REG_REGS_H__
/* system control registers */
#define SYSTEM_CTROL0 0x3008 // Bit[7]: Software reset
// Bit[6]: Software power down
// Bit[5]: Reserved
// Bit[4]: SRB clock SYNC enable
// Bit[3]: Isolation suspend select
// Bit[2:0]: Not used
/* output format control registers */
#define FORMAT_CTRL 0x501F // Format select
// Bit[2:0]:
// 000: YUV422
// 001: RGB
// 010: Dither
// 011: RAW after DPC
// 101: RAW after CIP
/* format control registers */
#define FORMAT_CTRL00 0x4300
/* frame control registers */
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// Bit[3:0]: Frame ON number
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// BIT[3:0]: Frame OFF number
/* ISP top control registers */
#define PRE_ISP_TEST_SETTING_1 0x503D // Bit[7]: Test enable
// 0: Test disable
// 1: Color bar enable
// Bit[6]: Rolling
// Bit[5]: Transparent
// Bit[4]: Square black and white
// Bit[3:2]: Color bar style
// 00: Standard 8 color bar
// 01: Gradual change at vertical mode 1
// 10: Gradual change at horizontal
// 11: Gradual change at vertical mode 2
// Bit[1:0]: Test select
// 00: Color bar
// 01: Random data
// 10: Square data
// 11: Black image
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
/* AEC/AGC control functions */
#define AEC_PK_MANUAL 0x3503 // AEC Manual Mode Control
// Bit[7:6]: Reserved
// Bit[5]: Gain delay option
// Valid when 0x3503[4]=1b0
// 0: Delay one frame latch
// 1: One frame latch
// Bit[4:2]: Reserved
// Bit[1]: AGC manual
// 0: Auto enable
// 1: Manual enable
// Bit[0]: AEC manual
// 0: Auto enable
// 1: Manual enable
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
/* mirror and flip registers */
#define TIMING_TC_REG20 0x3820 // Timing Control Register
// Bit[2:1]: Vertical flip enable
// 00: Normal
// 11: Vertical flip
// Bit[0]: Vertical binning enable
#define TIMING_TC_REG21 0x3821 // Timing Control Register
// Bit[5]: Compression Enable
// Bit[2:1]: Horizontal mirror enable
// 00: Normal
// 11: Horizontal mirror
// Bit[0]: Horizontal binning enable
#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
// 1: active high
// Bit[3]: Gate PCLK under VSYNC
// Bit[2]: Gate PCLK under HREF
// Bit[1]: HREF polarity
// 0: active low
// 1: active high
// Bit[0] VSYNC polarity
// 0: active low
// 1: active high
#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
// 00: 1x
// 01: 2x
// 10: 3x
// 11: 4x
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
// Size after scaling
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
#define X_OFFSET_L 0x3811 //Bit[7:0]:
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
//Bit[3:0]: Horizontal even subsample increment
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
//Bit[3:0]: Vertical even subsample increment
// Size before scaling
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
#define ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable
// 0: Disable
// 1: Enable
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
// Bit[2:0]: VDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
// 0: Auto
// 1: Manual by PCLK_RATIO
#define VFIFO_X_SIZE_H 0x4602
#define VFIFO_X_SIZE_L 0x4603
#define VFIFO_Y_SIZE_H 0x4604
#define VFIFO_Y_SIZE_L 0x4605
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
// Bit[3:0]: PLLS system divider
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
// 00: 1
// 01: 1.5
// 10: 2
// 11: 3
// Bit[2]: PLLS root-divider - 1
// Bit[1:0]: PLLS seld5
// 00: 1
// 01: 1
// 10: 2
// 11: 2.5
#define COMPRESSION_CTRL00 0x4400 //
#define COMPRESSION_CTRL01 0x4401 //
#define COMPRESSION_CTRL02 0x4402 //
#define COMPRESSION_CTRL03 0x4403 //
#define COMPRESSION_CTRL04 0x4404 //
#define COMPRESSION_CTRL05 0x4405 //
#define COMPRESSION_CTRL06 0x4406 //
#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
#define COMPRESSION_ISI_CTRL 0x4408 //
#define COMPRESSION_CTRL09 0x4409 //
#define COMPRESSION_CTRL0a 0x440a //
#define COMPRESSION_CTRL0b 0x440b //
#define COMPRESSION_CTRL0c 0x440c //
#define COMPRESSION_CTRL0d 0x440d //
#define COMPRESSION_CTRL0E 0x440e //
/**
* @brief register value
*/
#define TEST_COLOR_BAR 0xC0 /* Enable Color Bar roling Test */
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
#define TIMING_TC_REG20_VFLIP 0x06 /* Vertical flip enable */
#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
#endif // __OV3660_REG_REGS_H__

View File

@@ -0,0 +1,317 @@
#ifndef _OV3660_SETTINGS_H_
#define _OV3660_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "ov3660_regs.h"
static const ratio_settings_t ratio_table[] = {
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
{ 2048, 1536, 0, 0, 2079, 1547, 16, 6, 2300, 1564 }, //4x3
{ 1920, 1280, 64, 128, 2015, 1419, 16, 6, 2172, 1436 }, //3x2
{ 2048, 1280, 0, 128, 2079, 1419, 16, 6, 2300, 1436 }, //16x10
{ 1920, 1152, 64, 192, 2015, 1355, 16, 6, 2172, 1372 }, //5x3
{ 1920, 1080, 64, 242, 2015, 1333, 16, 6, 2172, 1322 }, //16x9
{ 2048, 880, 0, 328, 2079, 1219, 16, 6, 2300, 1236 }, //21x9
{ 1920, 1536, 64, 0, 2015, 1547, 16, 6, 2172, 1564 }, //5x4
{ 1536, 1536, 256, 0, 1823, 1547, 16, 6, 2044, 1564 }, //1x1
{ 864, 1536, 592, 0, 1487, 1547, 16, 6, 2044, 1564 } //9x16
};
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000
static const uint16_t sensor_default_regs[][2] = {
{SYSTEM_CTROL0, 0x82}, // software reset
{REG_DLY, 10}, // delay 10ms
{0x3103, 0x13},
{SYSTEM_CTROL0, 0x42},
{0x3017, 0xff},
{0x3018, 0xff},
{DRIVE_CAPABILITY, 0xc3},
{CLOCK_POL_CONTROL, 0x21},
{0x3611, 0x01},
{0x3612, 0x2d},
{0x3032, 0x00},
{0x3614, 0x80},
{0x3618, 0x00},
{0x3619, 0x75},
{0x3622, 0x80},
{0x3623, 0x00},
{0x3624, 0x03},
{0x3630, 0x52},
{0x3632, 0x07},
{0x3633, 0xd2},
{0x3704, 0x80},
{0x3708, 0x66},
{0x3709, 0x12},
{0x370b, 0x12},
{0x3717, 0x00},
{0x371b, 0x60},
{0x371c, 0x00},
{0x3901, 0x13},
{0x3600, 0x08},
{0x3620, 0x43},
{0x3702, 0x20},
{0x3739, 0x48},
{0x3730, 0x20},
{0x370c, 0x0c},
{0x3a18, 0x00},
{0x3a19, 0xf8},
{0x3000, 0x10},
{0x3004, 0xef},
{0x6700, 0x05},
{0x6701, 0x19},
{0x6702, 0xfd},
{0x6703, 0xd1},
{0x6704, 0xff},
{0x6705, 0xff},
{0x3c01, 0x80},
{0x3c00, 0x04},
{0x3a08, 0x00}, {0x3a09, 0x62}, //50Hz Band Width Step (10bit)
{0x3a0e, 0x08}, //50Hz Max Bands in One Frame (6 bit)
{0x3a0a, 0x00}, {0x3a0b, 0x52}, //60Hz Band Width Step (10bit)
{0x3a0d, 0x09}, //60Hz Max Bands in One Frame (6 bit)
{0x3a00, 0x3a},//night mode off
{0x3a14, 0x09},
{0x3a15, 0x30},
{0x3a02, 0x09},
{0x3a03, 0x30},
{COMPRESSION_CTRL0E, 0x08},
{0x4520, 0x0b},
{0x460b, 0x37},
{0x4713, 0x02},
{0x471c, 0xd0},
{0x5086, 0x00},
{0x5002, 0x00},
{0x501f, 0x00},
{SYSTEM_CTROL0, 0x02},
{0x5180, 0xff},
{0x5181, 0xf2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x16},
{0x5187, 0x16},
{0x5188, 0x16},
{0x5189, 0x68},
{0x518a, 0x60},
{0x518b, 0xe0},
{0x518c, 0xb2},
{0x518d, 0x42},
{0x518e, 0x35},
{0x518f, 0x56},
{0x5190, 0x56},
{0x5191, 0xf8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xf0},
{0x5195, 0xf0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519a, 0x04},
{0x519b, 0x00},
{0x519c, 0x06},
{0x519d, 0x82},
{0x519e, 0x38},
{0x5381, 0x1d},
{0x5382, 0x60},
{0x5383, 0x03},
{0x5384, 0x0c},
{0x5385, 0x78},
{0x5386, 0x84},
{0x5387, 0x7d},
{0x5388, 0x6b},
{0x5389, 0x12},
{0x538a, 0x01},
{0x538b, 0x98},
{0x5480, 0x01},
// {0x5481, 0x05},
// {0x5482, 0x09},
// {0x5483, 0x10},
// {0x5484, 0x3a},
// {0x5485, 0x4c},
// {0x5486, 0x5a},
// {0x5487, 0x68},
// {0x5488, 0x74},
// {0x5489, 0x80},
// {0x548a, 0x8e},
// {0x548b, 0xa4},
// {0x548c, 0xb4},
// {0x548d, 0xc8},
// {0x548e, 0xde},
// {0x548f, 0xf0},
// {0x5490, 0x15},
{0x5000, 0xa7},
{0x5800, 0x0C},
{0x5801, 0x09},
{0x5802, 0x0C},
{0x5803, 0x0C},
{0x5804, 0x0D},
{0x5805, 0x17},
{0x5806, 0x06},
{0x5807, 0x05},
{0x5808, 0x04},
{0x5809, 0x06},
{0x580a, 0x09},
{0x580b, 0x0E},
{0x580c, 0x05},
{0x580d, 0x01},
{0x580e, 0x01},
{0x580f, 0x01},
{0x5810, 0x05},
{0x5811, 0x0D},
{0x5812, 0x05},
{0x5813, 0x01},
{0x5814, 0x01},
{0x5815, 0x01},
{0x5816, 0x05},
{0x5817, 0x0D},
{0x5818, 0x08},
{0x5819, 0x06},
{0x581a, 0x05},
{0x581b, 0x07},
{0x581c, 0x0B},
{0x581d, 0x0D},
{0x581e, 0x12},
{0x581f, 0x0D},
{0x5820, 0x0E},
{0x5821, 0x10},
{0x5822, 0x10},
{0x5823, 0x1E},
{0x5824, 0x53},
{0x5825, 0x15},
{0x5826, 0x05},
{0x5827, 0x14},
{0x5828, 0x54},
{0x5829, 0x25},
{0x582a, 0x33},
{0x582b, 0x33},
{0x582c, 0x34},
{0x582d, 0x16},
{0x582e, 0x24},
{0x582f, 0x41},
{0x5830, 0x50},
{0x5831, 0x42},
{0x5832, 0x15},
{0x5833, 0x25},
{0x5834, 0x34},
{0x5835, 0x33},
{0x5836, 0x24},
{0x5837, 0x26},
{0x5838, 0x54},
{0x5839, 0x25},
{0x583a, 0x15},
{0x583b, 0x25},
{0x583c, 0x53},
{0x583d, 0xCF},
{0x3a0f, 0x30},
{0x3a10, 0x28},
{0x3a1b, 0x30},
{0x3a1e, 0x28},
{0x3a11, 0x60},
{0x3a1f, 0x14},
{0x5302, 0x28},
{0x5303, 0x20},
{0x5306, 0x1c}, //de-noise offset 1
{0x5307, 0x28}, //de-noise offset 2
{0x4002, 0xc5},
{0x4003, 0x81},
{0x4005, 0x12},
{0x5688, 0x11},
{0x5689, 0x11},
{0x568a, 0x11},
{0x568b, 0x11},
{0x568c, 0x11},
{0x568d, 0x11},
{0x568e, 0x11},
{0x568f, 0x11},
{0x5580, 0x06},
{0x5588, 0x00},
{0x5583, 0x40},
{0x5584, 0x2c},
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_jpeg[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x30}, // YUYV
{0x3002, 0x00},//0x1c to 0x00 !!!
{0x3006, 0xff},//0xc3 to 0xff !!!
{0x471c, 0x50},//0xd0 to 0x50 !!!
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_raw[][2] = {
{FORMAT_CTRL00, 0x00}, // RAW
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_grayscale[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x10}, // Y8
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_yuv422[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x30}, // YUYV
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_rgb565[][2] = {
{FORMAT_CTRL, 0x01}, // RGB
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
{REGLIST_TAIL, 0x00}
};
static const uint8_t sensor_saturation_levels[9][11] = {
{0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
{0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
{0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
{0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
{0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
{0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
{0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
{0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
};
static const uint8_t sensor_special_effects[7][4] = {
{0x06, 0x40, 0x2c, 0x08},//Normal
{0x46, 0x40, 0x28, 0x08},//Negative
{0x1e, 0x80, 0x80, 0x08},//Grayscale
{0x1e, 0x80, 0xc0, 0x08},//Red Tint
{0x1e, 0x60, 0x60, 0x08},//Green Tint
{0x1e, 0xa0, 0x40, 0x08},//Blue Tint
{0x1e, 0x40, 0xa0, 0x08},//Sepia
};
#endif

View File

@@ -0,0 +1,27 @@
#ifndef __OV5640_H__
#define __OV5640_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov5640_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov5640_init(sensor_t *sensor);
#endif // __OV5640_H__

View File

@@ -0,0 +1,270 @@
#ifndef _OV5640_JPEG_SETTINGS_H_
#define _OV5640_JPEG_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
uint16_t reg;
uint8_t dat;
} ov5640_reg_cfg_t;
static const ov5640_reg_cfg_t ov5640_jpeg_init_cfg[] = {
{0x3008, 0x42},
{0x3103, 0x03},
{0x3017, 0xFF},
{0x3018, 0xFF},
{0x3034, 0x1A},
{0x3037, 0x13},
{0x3108, 0x01},
{0x3630, 0x36},
{0x3631, 0x0E},
{0x3632, 0xE2},
{0x3633, 0x12},
{0x3621, 0xE0},
{0x3704, 0xA0},
{0x3703, 0x5A},
{0x3715, 0x78},
{0x3717, 0x01},
{0x370B, 0x60},
{0x3705, 0x1A},
{0x3905, 0x02},
{0x3906, 0x10},
{0x3901, 0x0A},
{0x3731, 0x12},
{0x3600, 0x08},
{0x3601, 0x33},
{0x302D, 0x60},
{0x3620, 0x52},
{0x371B, 0x20},
{0x471C, 0x50},
{0x3A13, 0x43},
{0x3A18, 0x00},
{0x3A19, 0xF8},
{0x3635, 0x13},
{0x3636, 0x03},
{0x3634, 0x40},
{0x3622, 0x01},
{0x3C01, 0x34},
{0x3C04, 0x28},
{0x3C05, 0x98},
{0x3C06, 0x00},
{0x3C07, 0x08},
{0x3C08, 0x00},
{0x3C09, 0x1C},
{0x3C0A, 0x9C},
{0x3C0B, 0x40},
{0x3810, 0x00},
{0x3811, 0x10},
{0x3812, 0x00},
{0x3708, 0x64},
{0x4001, 0x02},
{0x4005, 0x1A},
{0x3000, 0x00},
{0x3004, 0xFF},
{0x300E, 0x58},
{0x302E, 0x00},
{0x4300, 0x30},
{0x501F, 0x00},
{0x440E, 0x00},
{0x5000, 0xA7},
{0x3A0F, 0x30},
{0x3A10, 0x28},
{0x3A1B, 0x30},
{0x3A1E, 0x26},
{0x3A11, 0x60},
{0x3A1F, 0x14},
{0x5800, 0x23},
{0x5801, 0x14},
{0x5802, 0x0F},
{0x5803, 0x0F},
{0x5804, 0x12},
{0x5805, 0x26},
{0x5806, 0x0C},
{0x5807, 0x08},
{0x5808, 0x05},
{0x5809, 0x05},
{0x580A, 0x08},
{0x580B, 0x0D},
{0x580C, 0x08},
{0x580D, 0x03},
{0x580E, 0x00},
{0x580F, 0x00},
{0x5810, 0x03},
{0x5811, 0x09},
{0x5812, 0x07},
{0x5813, 0x03},
{0x5814, 0x00},
{0x5815, 0x01},
{0x5816, 0x03},
{0x5817, 0x08},
{0x5818, 0x0D},
{0x5819, 0x08},
{0x581A, 0x05},
{0x581B, 0x06},
{0x581C, 0x08},
{0x581D, 0x0E},
{0x581E, 0x29},
{0x581F, 0x17},
{0x5820, 0x11},
{0x5821, 0x11},
{0x5822, 0x15},
{0x5823, 0x28},
{0x5824, 0x46},
{0x5825, 0x26},
{0x5826, 0x08},
{0x5827, 0x26},
{0x5828, 0x64},
{0x5829, 0x26},
{0x582A, 0x24},
{0x582B, 0x22},
{0x582C, 0x24},
{0x582D, 0x24},
{0x582E, 0x06},
{0x582F, 0x22},
{0x5830, 0x40},
{0x5831, 0x42},
{0x5832, 0x24},
{0x5833, 0x26},
{0x5834, 0x24},
{0x5835, 0x22},
{0x5836, 0x22},
{0x5837, 0x26},
{0x5838, 0x44},
{0x5839, 0x24},
{0x583A, 0x26},
{0x583B, 0x28},
{0x583C, 0x42},
{0x583D, 0xCE},
{0x5180, 0xFF},
{0x5181, 0xF2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x09},
{0x5187, 0x09},
{0x5188, 0x09},
{0x5189, 0x75},
{0x518A, 0x54},
{0x518B, 0xE0},
{0x518C, 0xB2},
{0x518D, 0x42},
{0x518E, 0x3D},
{0x518F, 0x56},
{0x5190, 0x46},
{0x5191, 0xF8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xF0},
{0x5195, 0xF0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519A, 0x04},
{0x519B, 0x00},
{0x519C, 0x06},
{0x519D, 0x82},
{0x519E, 0x38},
{0x5480, 0x01},
{0x5481, 0x08},
{0x5482, 0x14},
{0x5483, 0x28},
{0x5484, 0x51},
{0x5485, 0x65},
{0x5486, 0x71},
{0x5487, 0x7D},
{0x5488, 0x87},
{0x5489, 0x91},
{0x548A, 0x9A},
{0x548B, 0xAA},
{0x548C, 0xB8},
{0x548D, 0xCD},
{0x548E, 0xDD},
{0x548F, 0xEA},
{0x5490, 0x1D},
{0x5381, 0x1E},
{0x5382, 0x5B},
{0x5383, 0x08},
{0x5384, 0x0A},
{0x5385, 0x7E},
{0x5386, 0x88},
{0x5387, 0x7C},
{0x5388, 0x6C},
{0x5389, 0x10},
{0x538A, 0x01},
{0x538B, 0x98},
{0x5580, 0x06},
{0x5583, 0x40},
{0x5584, 0x10},
{0x5589, 0x10},
{0x558A, 0x00},
{0x558B, 0xF8},
{0x501D, 0x40},
{0x5300, 0x08},
{0x5301, 0x30},
{0x5302, 0x10},
{0x5303, 0x00},
{0x5304, 0x08},
{0x5305, 0x30},
{0x5306, 0x08},
{0x5307, 0x16},
{0x5309, 0x08},
{0x530A, 0x30},
{0x530B, 0x04},
{0x530C, 0x06},
{0x5025, 0x00},
{0x3008, 0x02},
{0x4740, 0x21},
};
static const ov5640_reg_cfg_t ov5640_jpeg_cfg[] = {
{0x4300, 0x30},
{0x501F, 0x00},
//{0x3035, 0x21}, // 0x21 //0x11:60fps 0x21:30fps 0x41:15fps 0xa1:7.5fps
{0x3035, 0xa1},
{0x3036, 0x69},
{0x3C07, 0x07},
{0x3820, 0x46},
{0x3821, 0x20},
{0x3814, 0x11},
{0x3815, 0x11},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x0A},
{0x3805, 0x3F},
{0x3806, 0x07},
{0x3807, 0x9F},
{0x3808, 0x02},
{0x3809, 0x80},
{0x380A, 0x01},
{0x380B, 0xE0},
{0x380C, 0x0B},
{0x380D, 0x1C},
{0x380E, 0x07},
{0x380F, 0xB0},
{0x3813, 0x04},
{0x3618, 0x04},
{0x3612, 0x2B},
{0x3709, 0x12},
{0x370C, 0x00},
{0x4004, 0x06},
{0x3002, 0x00},
{0x3006, 0xFF},
{0x4713, 0x03},
{0x4407, 0x01},
{0x460B, 0x35},
{0x460C, 0x22},
{0x4837, 0x16},
{0x3824, 0x02},
{0x5001, 0xA3},
{0x3503, 0x00},
};
#endif

View File

@@ -0,0 +1,213 @@
/*
* OV5640 register definitions.
*/
#ifndef __OV5640_REG_REGS_H__
#define __OV5640_REG_REGS_H__
/* system control registers */
#define SYSTEM_CTROL0 0x3008 // Bit[7]: Software reset
// Bit[6]: Software power down
// Bit[5]: Reserved
// Bit[4]: SRB clock SYNC enable
// Bit[3]: Isolation suspend select
// Bit[2:0]: Not used
#define DRIVE_CAPABILITY 0x302c // Bit[7:6]:
// 00: 1x
// 01: 2x
// 10: 3x
// 11: 4x
#define SC_PLLS_CTRL0 0x303a // Bit[7]: PLLS bypass
#define SC_PLLS_CTRL1 0x303b // Bit[4:0]: PLLS multiplier
#define SC_PLLS_CTRL2 0x303c // Bit[6:4]: PLLS charge pump control
// Bit[3:0]: PLLS system divider
#define SC_PLLS_CTRL3 0x303d // Bit[5:4]: PLLS pre-divider
// 00: 1
// 01: 1.5
// 10: 2
// 11: 3
// Bit[2]: PLLS root-divider - 1
// Bit[1:0]: PLLS seld5
// 00: 1
// 01: 1
// 10: 2
// 11: 2.5
/* AEC/AGC control functions */
#define AEC_PK_MANUAL 0x3503 // AEC Manual Mode Control
// Bit[7:6]: Reserved
// Bit[5]: Gain delay option
// Valid when 0x3503[4]=1b0
// 0: Delay one frame latch
// 1: One frame latch
// Bit[4:2]: Reserved
// Bit[1]: AGC manual
// 0: Auto enable
// 1: Manual enable
// Bit[0]: AEC manual
// 0: Auto enable
// 1: Manual enable
//gain = {0x350A[1:0], 0x350B[7:0]} / 16
#define X_ADDR_ST_H 0x3800 //Bit[3:0]: X address start[11:8]
#define X_ADDR_ST_L 0x3801 //Bit[7:0]: X address start[7:0]
#define Y_ADDR_ST_H 0x3802 //Bit[2:0]: Y address start[10:8]
#define Y_ADDR_ST_L 0x3803 //Bit[7:0]: Y address start[7:0]
#define X_ADDR_END_H 0x3804 //Bit[3:0]: X address end[11:8]
#define X_ADDR_END_L 0x3805 //Bit[7:0]:
#define Y_ADDR_END_H 0x3806 //Bit[2:0]: Y address end[10:8]
#define Y_ADDR_END_L 0x3807 //Bit[7:0]:
// Size after scaling
#define X_OUTPUT_SIZE_H 0x3808 //Bit[3:0]: DVP output horizontal width[11:8]
#define X_OUTPUT_SIZE_L 0x3809 //Bit[7:0]:
#define Y_OUTPUT_SIZE_H 0x380a //Bit[2:0]: DVP output vertical height[10:8]
#define Y_OUTPUT_SIZE_L 0x380b //Bit[7:0]:
#define X_TOTAL_SIZE_H 0x380c //Bit[3:0]: Total horizontal size[11:8]
#define X_TOTAL_SIZE_L 0x380d //Bit[7:0]:
#define Y_TOTAL_SIZE_H 0x380e //Bit[7:0]: Total vertical size[15:8]
#define Y_TOTAL_SIZE_L 0x380f //Bit[7:0]:
#define X_OFFSET_H 0x3810 //Bit[3:0]: ISP horizontal offset[11:8]
#define X_OFFSET_L 0x3811 //Bit[7:0]:
#define Y_OFFSET_H 0x3812 //Bit[2:0]: ISP vertical offset[10:8]
#define Y_OFFSET_L 0x3813 //Bit[7:0]:
#define X_INCREMENT 0x3814 //Bit[7:4]: Horizontal odd subsample increment
//Bit[3:0]: Horizontal even subsample increment
#define Y_INCREMENT 0x3815 //Bit[7:4]: Vertical odd subsample increment
//Bit[3:0]: Vertical even subsample increment
// Size before scaling
//#define X_INPUT_SIZE (X_ADDR_END - X_ADDR_ST + 1 - (2 * X_OFFSET))
//#define Y_INPUT_SIZE (Y_ADDR_END - Y_ADDR_ST + 1 - (2 * Y_OFFSET))
/* mirror and flip registers */
#define TIMING_TC_REG20 0x3820 // Timing Control Register
// Bit[2:1]: Vertical flip enable
// 00: Normal
// 11: Vertical flip
// Bit[0]: Vertical binning enable
#define TIMING_TC_REG21 0x3821 // Timing Control Register
// Bit[5]: Compression Enable
// Bit[2:1]: Horizontal mirror enable
// 00: Normal
// 11: Horizontal mirror
// Bit[0]: Horizontal binning enable
#define PCLK_RATIO 0x3824 // Bit[4:0]: PCLK ratio manual
/* frame control registers */
#define FRAME_CTRL01 0x4201 // Control Passed Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// Bit[3:0]: Frame ON number
#define FRAME_CTRL02 0x4202 // Control Masked Frame Number When both ON and OFF number set to 0x00,frame control is in bypass mode
// Bit[7:4]: Not used
// BIT[3:0]: Frame OFF number
/* format control registers */
#define FORMAT_CTRL00 0x4300
#define CLOCK_POL_CONTROL 0x4740// Bit[5]: PCLK polarity 0: active low
// 1: active high
// Bit[3]: Gate PCLK under VSYNC
// Bit[2]: Gate PCLK under HREF
// Bit[1]: HREF polarity
// 0: active low
// 1: active high
// Bit[0] VSYNC polarity
// 0: active low
// 1: active high
#define ISP_CONTROL_01 0x5001 // Bit[5]: Scale enable
// 0: Disable
// 1: Enable
/* output format control registers */
#define FORMAT_CTRL 0x501F // Format select
// Bit[2:0]:
// 000: YUV422
// 001: RGB
// 010: Dither
// 011: RAW after DPC
// 101: RAW after CIP
/* ISP top control registers */
#define PRE_ISP_TEST_SETTING_1 0x503D // Bit[7]: Test enable
// 0: Test disable
// 1: Color bar enable
// Bit[6]: Rolling
// Bit[5]: Transparent
// Bit[4]: Square black and white
// Bit[3:2]: Color bar style
// 00: Standard 8 color bar
// 01: Gradual change at vertical mode 1
// 10: Gradual change at horizontal
// 11: Gradual change at vertical mode 2
// Bit[1:0]: Test select
// 00: Color bar
// 01: Random data
// 10: Square data
// 11: Black image
//exposure = {0x3500[3:0], 0x3501[7:0], 0x3502[7:0]} / 16 × tROW
#define SCALE_CTRL_1 0x5601 // Bit[6:4]: HDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
// Bit[2:0]: VDIV RW
// DCW scale times
// 000: DCW 1 time
// 001: DCW 2 times
// 010: DCW 4 times
// 100: DCW 8 times
// 101: DCW 16 times
// Others: DCW 16 times
#define SCALE_CTRL_2 0x5602 // X_SCALE High Bits
#define SCALE_CTRL_3 0x5603 // X_SCALE Low Bits
#define SCALE_CTRL_4 0x5604 // Y_SCALE High Bits
#define SCALE_CTRL_5 0x5605 // Y_SCALE Low Bits
#define SCALE_CTRL_6 0x5606 // Bit[3:0]: V Offset
#define VFIFO_CTRL0C 0x460C // Bit[1]: PCLK manual enable
// 0: Auto
// 1: Manual by PCLK_RATIO
#define VFIFO_X_SIZE_H 0x4602
#define VFIFO_X_SIZE_L 0x4603
#define VFIFO_Y_SIZE_H 0x4604
#define VFIFO_Y_SIZE_L 0x4605
#define COMPRESSION_CTRL00 0x4400 //
#define COMPRESSION_CTRL01 0x4401 //
#define COMPRESSION_CTRL02 0x4402 //
#define COMPRESSION_CTRL03 0x4403 //
#define COMPRESSION_CTRL04 0x4404 //
#define COMPRESSION_CTRL05 0x4405 //
#define COMPRESSION_CTRL06 0x4406 //
#define COMPRESSION_CTRL07 0x4407 // Bit[5:0]: QS
#define COMPRESSION_ISI_CTRL 0x4408 //
#define COMPRESSION_CTRL09 0x4409 //
#define COMPRESSION_CTRL0a 0x440a //
#define COMPRESSION_CTRL0b 0x440b //
#define COMPRESSION_CTRL0c 0x440c //
#define COMPRESSION_CTRL0d 0x440d //
#define COMPRESSION_CTRL0E 0x440e //
/**
* @brief register value
*/
#define TEST_COLOR_BAR 0xC0 /* Enable Color Bar roling Test */
#define AEC_PK_MANUAL_AGC_MANUALEN 0x02 /* Enable AGC Manual enable */
#define AEC_PK_MANUAL_AEC_MANUALEN 0x01 /* Enable AEC Manual enable */
#define TIMING_TC_REG20_VFLIP 0x06 /* Vertical flip enable */
#define TIMING_TC_REG21_HMIRROR 0x06 /* Horizontal mirror enable */
#endif // __OV3660_REG_REGS_H__

View File

@@ -0,0 +1,748 @@
#ifndef _OV5640_SETTINGS_H_
#define _OV5640_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "ov5640_regs.h"
static const ratio_settings_t ratio_table[] = {
// mw, mh, sx, sy, ex, ey, ox, oy, tx, ty
{ 2560, 1920, 0, 0, 2623, 1951, 32, 16, 2844, 1968 }, //4x3
{ 2560, 1704, 0, 110, 2623, 1843, 32, 16, 2844, 1752 }, //3x2
{ 2560, 1600, 0, 160, 2623, 1791, 32, 16, 2844, 1648 }, //16x10
{ 2560, 1536, 0, 192, 2623, 1759, 32, 16, 2844, 1584 }, //5x3
{ 2560, 1440, 0, 240, 2623, 1711, 32, 16, 2844, 1488 }, //16x9
{ 2560, 1080, 0, 420, 2623, 1531, 32, 16, 2844, 1128 }, //21x9
{ 2400, 1920, 80, 0, 2543, 1951, 32, 16, 2684, 1968 }, //5x4
{ 1920, 1920, 320, 0, 2543, 1951, 32, 16, 2684, 1968 }, //1x1
{ 1088, 1920, 736, 0, 1887, 1951, 32, 16, 1884, 1968 } //9x16
};
#define REG_DLY 0xffff
#define REGLIST_TAIL 0x0000
static const uint16_t sensor_default_regs_backup[][2] = {
{SYSTEM_CTROL0, 0x82}, // software reset
{REG_DLY, 10}, // delay 10ms
{SYSTEM_CTROL0, 0x42}, // power down
//enable pll
{0x3103, 0x13},
//io direction
{0x3017, 0xff},
{0x3018, 0xff},
{DRIVE_CAPABILITY, 0xc3},
{CLOCK_POL_CONTROL, 0x21},
{0x4713, 0x02},//jpg mode select
{ISP_CONTROL_01, 0x83}, // turn color matrix, awb and SDE
//sys reset
{0x3000, 0x20}, // reset MCU
{REG_DLY, 10}, // delay 10ms
{0x3002, 0x1c},
//clock enable
{0x3004, 0xff},
{0x3006, 0xc3},
//isp control
{0x5000, 0xa7},
{ISP_CONTROL_01, 0xa3},//+scaling?
{0x5003, 0x08},//special_effect
//unknown
{0x370c, 0x02},//!!IMPORTANT
{0x3634, 0x40},//!!IMPORTANT
//AEC/AGC
{0x3a02, 0x03},
{0x3a03, 0xd8},
{0x3a08, 0x01},
{0x3a09, 0x27},
{0x3a0a, 0x00},
{0x3a0b, 0xf6},
{0x3a0d, 0x04},
{0x3a0e, 0x03},
{0x3a0f, 0x30},//ae_level
{0x3a10, 0x28},//ae_level
{0x3a11, 0x60},//ae_level
{0x3a13, 0x43},
{0x3a14, 0x03},
{0x3a15, 0xd8},
{0x3a18, 0x00},//gainceiling
{0x3a19, 0xf8},//gainceiling
{0x3a1b, 0x30},//ae_level
{0x3a1e, 0x26},//ae_level
{0x3a1f, 0x14},//ae_level
//vcm debug
{0x3600, 0x08},
{0x3601, 0x33},
//50/60Hz
{0x3c01, 0xa4},
{0x3c04, 0x28},
{0x3c05, 0x98},
{0x3c06, 0x00},
{0x3c07, 0x08},
{0x3c08, 0x00},
{0x3c09, 0x1c},
{0x3c0a, 0x9c},
{0x3c0b, 0x40},
{0x460c, 0x22},//disable jpeg footer
//BLC
{0x4001, 0x02},
{0x4004, 0x02},
//AWB
{0x5180, 0xff},
{0x5181, 0xf2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x09},
{0x5187, 0x09},
{0x5188, 0x09},
{0x5189, 0x75},
{0x518a, 0x54},
{0x518b, 0xe0},
{0x518c, 0xb2},
{0x518d, 0x42},
{0x518e, 0x3d},
{0x518f, 0x56},
{0x5190, 0x46},
{0x5191, 0xf8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xf0},
{0x5195, 0xf0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519a, 0x04},
{0x519b, 0x00},
{0x519c, 0x06},
{0x519d, 0x82},
{0x519e, 0x38},
//color matrix (Saturation)
{0x5381, 0x1e},
{0x5382, 0x5b},
{0x5383, 0x08},
{0x5384, 0x0a},
{0x5385, 0x7e},
{0x5386, 0x88},
{0x5387, 0x7c},
{0x5388, 0x6c},
{0x5389, 0x10},
{0x538a, 0x01},
{0x538b, 0x98},
//CIP control (Sharpness)
{0x5300, 0x10},//sharpness
{0x5301, 0x10},//sharpness
{0x5302, 0x18},//sharpness
{0x5303, 0x19},//sharpness
{0x5304, 0x10},
{0x5305, 0x10},
{0x5306, 0x08},//denoise
{0x5307, 0x16},
{0x5308, 0x40},
{0x5309, 0x10},//sharpness
{0x530a, 0x10},//sharpness
{0x530b, 0x04},//sharpness
{0x530c, 0x06},//sharpness
//GAMMA
{0x5480, 0x01},
{0x5481, 0x00},
{0x5482, 0x1e},
{0x5483, 0x3b},
{0x5484, 0x58},
{0x5485, 0x66},
{0x5486, 0x71},
{0x5487, 0x7d},
{0x5488, 0x83},
{0x5489, 0x8f},
{0x548a, 0x98},
{0x548b, 0xa6},
{0x548c, 0xb8},
{0x548d, 0xca},
{0x548e, 0xd7},
{0x548f, 0xe3},
{0x5490, 0x1d},
//Special Digital Effects (SDE) (UV adjust)
{0x5580, 0x06},//enable brightness and contrast
{0x5583, 0x40},//special_effect
{0x5584, 0x10},//special_effect
{0x5586, 0x20},//contrast
{0x5587, 0x00},//brightness
{0x5588, 0x00},//brightness
{0x5589, 0x10},
{0x558a, 0x00},
{0x558b, 0xf8},
{0x501d, 0x40},// enable manual offset of contrast
//power on
{0x3008, 0x02},
//50Hz
{0x3c00, 0x04},
{REG_DLY, 300},
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_jpeg[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x30}, // YUYV
{0x3002, 0x00},//0x1c to 0x00 !!!
{0x3006, 0xff},//0xc3 to 0xff !!!
{0x471c, 0x50},//0xd0 to 0x50 !!!
{REGLIST_TAIL, 0x00}, // tail
};
static const uint16_t sensor_fmt_raw[][2] = {
{FORMAT_CTRL, 0x03}, // RAW (DPC)
{FORMAT_CTRL00, 0x00}, // RAW
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_grayscale[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x10}, // Y8
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_yuv422[][2] = {
{FORMAT_CTRL, 0x00}, // YUV422
{FORMAT_CTRL00, 0x30}, // YUYV
{REGLIST_TAIL, 0x00}
};
static const uint16_t sensor_fmt_rgb565[][2] = {
{FORMAT_CTRL, 0x01}, // RGB
{FORMAT_CTRL00, 0x61}, // RGB565 (BGR)
{REGLIST_TAIL, 0x00}
};
static const uint8_t sensor_saturation_levels[9][11] = {
{0x1d, 0x60, 0x03, 0x07, 0x48, 0x4f, 0x4b, 0x40, 0x0b, 0x01, 0x98},//-4
{0x1d, 0x60, 0x03, 0x08, 0x54, 0x5c, 0x58, 0x4b, 0x0d, 0x01, 0x98},//-3
{0x1d, 0x60, 0x03, 0x0a, 0x60, 0x6a, 0x64, 0x56, 0x0e, 0x01, 0x98},//-2
{0x1d, 0x60, 0x03, 0x0b, 0x6c, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98},//-1
{0x1d, 0x60, 0x03, 0x0c, 0x78, 0x84, 0x7d, 0x6b, 0x12, 0x01, 0x98},//0
{0x1d, 0x60, 0x03, 0x0d, 0x84, 0x91, 0x8a, 0x76, 0x14, 0x01, 0x98},//+1
{0x1d, 0x60, 0x03, 0x0e, 0x90, 0x9e, 0x96, 0x80, 0x16, 0x01, 0x98},//+2
{0x1d, 0x60, 0x03, 0x10, 0x9c, 0xac, 0xa2, 0x8b, 0x17, 0x01, 0x98},//+3
{0x1d, 0x60, 0x03, 0x11, 0xa8, 0xb9, 0xaf, 0x96, 0x19, 0x01, 0x98},//+4
};
static const uint8_t sensor_special_effects[7][4] = {
{0x06, 0x40, 0x2c, 0x08},//Normal
{0x46, 0x40, 0x28, 0x08},//Negative
{0x1e, 0x80, 0x80, 0x08},//Grayscale
{0x1e, 0x80, 0xc0, 0x08},//Red Tint
{0x1e, 0x60, 0x60, 0x08},//Green Tint
{0x1e, 0xa0, 0x40, 0x08},//Blue Tint
{0x1e, 0x40, 0xa0, 0x08},//Sepia
};
static const uint16_t sensor_regs_gamma0[][2] = {
{0x5480, 0x01},
{0x5481, 0x08},
{0x5482, 0x14},
{0x5483, 0x28},
{0x5484, 0x51},
{0x5485, 0x65},
{0x5486, 0x71},
{0x5487, 0x7d},
{0x5488, 0x87},
{0x5489, 0x91},
{0x548a, 0x9a},
{0x548b, 0xaa},
{0x548c, 0xb8},
{0x548d, 0xcd},
{0x548e, 0xdd},
{0x548f, 0xea},
{0x5490, 0x1d}
};
static const uint16_t sensor_regs_gamma1[][2] = {
{0x5480, 0x1},
{0x5481, 0x0},
{0x5482, 0x1e},
{0x5483, 0x3b},
{0x5484, 0x58},
{0x5485, 0x66},
{0x5486, 0x71},
{0x5487, 0x7d},
{0x5488, 0x83},
{0x5489, 0x8f},
{0x548a, 0x98},
{0x548b, 0xa6},
{0x548c, 0xb8},
{0x548d, 0xca},
{0x548e, 0xd7},
{0x548f, 0xe3},
{0x5490, 0x1d}
};
static const uint16_t sensor_regs_awb0[][2] = {
{0x5180, 0xff},
{0x5181, 0xf2},
{0x5182, 0x00},
{0x5183, 0x14},
{0x5184, 0x25},
{0x5185, 0x24},
{0x5186, 0x09},
{0x5187, 0x09},
{0x5188, 0x09},
{0x5189, 0x75},
{0x518a, 0x54},
{0x518b, 0xe0},
{0x518c, 0xb2},
{0x518d, 0x42},
{0x518e, 0x3d},
{0x518f, 0x56},
{0x5190, 0x46},
{0x5191, 0xf8},
{0x5192, 0x04},
{0x5193, 0x70},
{0x5194, 0xf0},
{0x5195, 0xf0},
{0x5196, 0x03},
{0x5197, 0x01},
{0x5198, 0x04},
{0x5199, 0x12},
{0x519a, 0x04},
{0x519b, 0x00},
{0x519c, 0x06},
{0x519d, 0x82},
{0x519e, 0x38}
};
/* 1920x1080 YUV422 3.5fps Pclk_out=19.2MHz */
static const uint16_t sensor_default_regs[][2] = {
{0x3103,0x11}, // system clock from pad, bit[1]
{0x3008,0x82}, // software reset, bit[7]
{REG_DLY, 10}, // delay 10ms
{0x3008,0x42}, // software power down, bit[6]
{0x3103,0x03}, // system clock from PLL, bit[1]
{0x3017,0xff}, // FREX, Vsync, HREF, PCLK, D[9:6] output enable
{0x3018,0xff}, // D[5:0], GPIO[1:0] output enable
//pll and clock setting
{0x3034,0x18}, // MIPI 10-bit
{0x3035,0x21}, //
{0x3036,0x46}, // 0x46->30fps
{0x3037,0x13}, // PLL root divider, bit[4], PLL pre-divider, bit[3:0]
{0x3108,0x01}, // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2], SCLK root divider, bit[1:0]
{0x3824,0x01}, //
{0x3630,0x36}, //
{0x3631,0x0e}, //
{0x3632,0xe2}, //
{0x3633,0x12}, //
{0x3621,0xe0}, //
{0x3704,0xa0}, //
{0x3703,0x5a}, //
{0x3715,0x78}, //
{0x3717,0x01}, //
{0x370b,0x60}, //
{0x3705,0x1a}, //
{0x3905,0x02}, //
{0x3906,0x10}, //
{0x3901,0x0a}, //
{0x3731,0x12}, //
{0x3600,0x08}, //
{0x3601,0x33}, //
// {0x302d,0x60);//
{0x3620,0x52}, //
{0x371b,0x20}, //
{0x471c,0x50}, //
{0x3a13,0x43}, //
{0x3a18,0x00}, //
{0x3a19,0x88}, //
{0x3635,0x13}, //
{0x3636,0x03}, //
{0x3634,0x40}, //
{0x3622,0x01}, //
{0x3c01,0x34}, //
{0x3c04,0x28}, //
{0x3c05,0x98}, //
{0x3c06,0x00}, //
{0x3c07,0x08}, //
{0x3c08,0x00}, //
{0x3c09,0x1c}, //
{0x3c0a,0x9c}, //
{0x3c0b,0x40}, //
// {0x3820,0x41);// binning
// {0x3821,0x41);// binning
{0x3814,0x31}, //
{0x3815,0x31}, //
{0x3800,0x00}, //
{0x3801,0x00}, //
{0x3802,0x00}, //
{0x3803,0x04}, //
{0x3804,0x0a}, //
{0x3805,0x3f}, //
{0x3806,0x07}, //
{0x3807,0x9b}, //
{0x3808,0x02}, //
{0x3809,0x80}, //
{0x380a,0x01}, //
{0x380b,0xe0}, //
{0x380c,0x07}, //
{0x380d,0x68}, //
{0x380e,0x03}, //
{0x380f,0xd8}, //
{0x3810,0x00}, //
{0x3811,0x10}, //
{0x3812,0x00}, //
{0x3813,0x06}, //
{0x3618,0x00}, //
{0x3612,0x29}, //
{0x3708,0x64}, //
{0x3709,0x52}, //
{0x370c,0x03}, //
{0x3a00,0x78},
{0x3a02,0x03}, //
{0x3a03,0xd8}, //
{0x3a08,0x01}, //
{0x3a09,0x27}, //
{0x3a0a,0x00}, //
{0x3a0b,0xf6}, //
{0x3a0e,0x03}, //
{0x3a0d,0x04}, //
{0x3a14,0x03}, //
{0x3a15,0xd8}, //
{0x4001,0x02}, //
{0x4004,0x02}, //
{0x3000,0x00}, //
{0x3002,0x1c}, //
{0x3004,0xff}, //
{0x3006,0xc3}, //
{0x300e,0x58}, //
// {0x302e,0x00);//
{0x302c,0x42}, //bit[7:6]: output drive capability: 00: 1x 01: 2x 10: 3x 11: 4x
/* pixel_format */
{0x4300, 0x30}, // YUV422 0x30:YUYV 0x31:YVYU 0x32:UYVY 0x33:VYUY
{0x501f, 0x00}, // bit[2:0]: 000:YUV422, 001:RGB, 010:dither, 011: RAW(DPC), 100:SNR RAW, 101: RAW(CIP)
//{0x4300, 0x40}, // YUV420 0x40:YY_YU 0x41:YY_YV 0x42:YY_UY 0x43:YY_VY 0x44:YU_YY 0x45:YV_YY 0x46:UY_YY 0x47:VY_YY
//{0x501f, 0x00}, // bit[2:0]: 000:YUV422, 001:RGB, 010:dither, 011: RAW(DPC), 100:SNR RAW, 101: RAW(CIP)
//{0x4300, 0x20}, // YUV444:YUVYUV
//{0x501f, 0x00}, // bit[2:0]: 000:YUV422, 001:RGB, 010:dither, 011: RAW(DPC), 100:SNR RAW, 101: RAW(CIP)
//{0x4300, 0x00}, // RAW:BGGR
//{0x501f, 0x05}, // bit[2:0]: 000:YUV422, 001:RGB, 010:dither, 011: RAW(DPC), 100:SNR RAW, 101: RAW(CIP)
{0x4713,0x03}, //
{0x4407,0x04}, //
{0x440e,0x00}, //
{0x460b,0x35}, //
{0x460c,0x20}, //
{0x4837,0x22},
{0x5000,0xa7}, //
{0x5001,0xa3}, //
//ov5640_write_register(iic_id, 0x4740,0x21);//hsync,vsync,clock pol,reference to application note,spec is wrong
//AWB
{0x3406,0x00}, //0x00); // LA ORG
{0x5180,0xff}, //0xff); // 0xff 0xff
{0x5181,0xf2}, //0x50); // 0xf2 0x50
{0x5182,0x00}, //0x11); // 0x00 0x11
{0x5183,0x14}, //0x14); // 0x14 0x14
{0x5184,0x25}, //0x25); // 0x25 0x25
{0x5185,0x24}, //0x24); // 0x24 0x24
{0x5186,0x16}, //0x1c); // 0x09 0x1c
{0x5187,0x16}, //0x18); // 0x09 0x18
{0x5188,0x16}, //0x18); // 0x09 0x18
{0x5189,0x6e}, //0x6e); // 0x75 0x6e
{0x518a,0x68}, //0x68); // 0x54 0x68
{0x518b,0xe0}, //0xa8); // 0xe0 0xa8
{0x518c,0xb2}, //0xa8); // 0xb2 0xa8
{0x518d,0x42}, //0x3d); // 0x42 0x3d
{0x518e,0x3e}, //0x3d); // 0x3d 0x3d
{0x518f,0x4c}, //0x54); // 0x56 0x54
{0x5190,0x56}, //0x54); // 0x46 0x54
{0x5191,0xf8}, //0xf8); // 0xf8 0xf8
{0x5192,0x04}, //0x04); // 0x04 0x04
{0x5193,0x70}, //0x70); // 0x70 0x70
{0x5194,0xf0}, //0xf0); // 0xf0 0xf0
{0x5195,0xf0}, //0xf0); // 0xf0 0xf0
{0x5196,0x03}, //0x03); // 0x03 0x03
{0x5197,0x01}, //0x01); // 0x01 0x01
{0x5198,0x04}, //0x05); // 0x04 0x05
{0x5199,0x12}, //0x7c); // 0x12 0x7c
{0x519a,0x04}, //0x04); // 0x04 0x04
{0x519b,0x00}, //0x00); // 0x00 0x00
{0x519c,0x06}, //0x06); // 0x06 0x06
{0x519d,0x82}, //0x79); // 0x82 0x79
{0x519e,0x38}, //0x38); // 0x38 0x38
//Color // LA ORG
{0x5381,0x1e}, //0x1e); // 0x1e 0x1e
{0x5382,0x5b}, //0x5b); // 0x5b 0x5b
{0x5383,0x14}, //0x08); // 0x08 0x08
{0x5384,0x05}, //0x0a); // 0x0a 0x05
{0x5385,0x77}, //0x7e); // 0x7e 0x72
{0x5386,0x7c}, //0x88); // 0x88 0x77
{0x5387,0x72}, //0x7c); // 0x7c 0x6d
{0x5388,0x58}, //0x6c); // 0x6c 0x4d
{0x5389,0x1a}, //0x10); // 0x10 0x20
{0x538a,0x01}, //0x01); // 0x01 0x01
{0x538b,0x98}, //0x98); // 0x98 0x98
//Sharpness/Denoise
{0x5300,0x08},
{0x5301,0x30},
{0x5302,0x30},
{0x5303,0x10},
{0x5308,0x25}, //sharpness/noise auto
{0x5304,0x08},
{0x5305,0x30},
{0x5306,0x1c},
{0x5307,0x2c},
{0x5309,0x08},
{0x530a,0x30},
{0x530b,0x04},
{0x530c,0x06},
//Gamma
{0x5480,0x01}, //??? // LA ORG
{0x5481,0x06}, //0x08); // 0x08 0x06
{0x5482,0x12}, //0x14); // 0x14 0x15
{0x5483,0x1e}, //0x28); // 0x28 0x28
{0x5484,0x4a}, //0x51); // 0x51 0x3b
{0x5485,0x58}, //0x65); // 0x65 0x50
{0x5486,0x65}, //0x71); // 0x71 0x5d
{0x5487,0x72}, //0x7d); // 0x7d 0x6a
{0x5488,0x7d}, //0x87); // 0x87 0x75
{0x5489,0x88}, //0x91); // 0x91 0x80
{0x548a,0x92}, //0x9a); // 0x9a 0x8a
{0x548b,0xa3}, //0xaa); // 0xaa 0x9b
{0x548c,0xb2}, //0xb8); // 0xb8 0xaa
{0x548d,0xc8}, //0xcd); // 0xcd 0xc0
{0x548e,0xdd}, //0xdd); // 0xdd 0xd5
{0x548f,0xf0}, //0xea); // 0xea 0xe8
{0x5490,0x15}, //0x1d); // 0x1d 0x20
//UV
{0x5580,0x06},
{0x5583,0x40},
{0x5584,0x10},
{0x5589,0x10},
{0x558a,0x00},
{0x558b,0xf8},
{0x501d,0x40},
// {0x5587,0x05);
// {0x5588,0x09);
//Lens Shading
{0x5800,0x15}, //0xa7); //LA org
{0x5801,0x10}, //0x23); //0x23 0x17
{0x5802,0x0D}, //0x14); //0x14 0x10
{0x5803,0x0D}, //0x0f); //0x0f 0x0e
{0x5804,0x0F}, //0x0f); //0x0f 0x0e
{0x5805,0x15}, //0x12); //0x12 0x11
{0x5806,0x0A}, //0x26); //0x26 0x1b
{0x5807,0x07}, //0x0c); //0x0c 0x0b
{0x5808,0x05}, //0x08); //0x08 0x07
{0x5809,0x05}, //0x05); //0x05 0x05
{0x580A,0x07}, //0x05); //0x05 0x06
{0x580B,0x0B}, //0x08); //0x08 0x09
{0x580C,0x07}, //0x0d); //0x0d 0x0e
{0x580D,0x03}, //0x08); //0x08 0x06
{0x580E,0x01}, //0x03); //0x03 0x02
{0x580F,0x01}, //0x00); //0x00 0x00
{0x5810,0x03}, //0x00); //0x00 0x00
{0x5811,0x07}, //0x03); //0x03 0x03
{0x5812,0x07}, //0x09); //0x09 0x09
{0x5813,0x03}, //0x07); //0x07 0x06
{0x5814,0x01}, //0x03); //0x03 0x03
{0x5815,0x01}, //0x00); //0x00 0x00
{0x5816,0x03}, //0x01); //0x01 0x00
{0x5817,0x06}, //0x03); //0x03 0x03
{0x5818,0x0D}, //0x08); //0x08 0x09
{0x5819,0x08}, //0x0d); //0x0d 0x0b
{0x581A,0x06}, //0x08); //0x08 0x08
{0x581B,0x06}, //0x05); //0x05 0x05
{0x581C,0x07}, //0x06); //0x06 0x05
{0x581D,0x0B}, //0x08); //0x08 0x08
{0x581E,0x14}, //0x0e); //0x0e 0x0e
{0x581F,0x13}, //0x29); //0x29 0x18
{0x5820,0x0E}, //0x17); //0x17 0x12
{0x5821,0x0E}, //0x11); //0x11 0x0f
{0x5822,0x12}, //0x11); //0x11 0x0f
{0x5823,0x12}, //0x15); //0x15 0x12
{0x5824,0x46}, //0x28); //0x28 0x1a
{0x5825,0x26}, //0x46); //0x46 0x0a
{0x5826,0x06}, //0x26); //0x26 0x0a
{0x5827,0x46}, //0x08); //0x08 0x0a
{0x5828,0x44}, //0x26); //0x26 0x0a
{0x5829,0x26}, //0x64); //0x64 0x46
{0x582A,0x24}, //0x26); //0x26 0x2a
{0x582B,0x42}, //0x24); //0x24 0x24
{0x582C,0x24}, //0x22); //0x22 0x44
{0x582D,0x46}, //0x24); //0x24 0x24
{0x582E,0x24}, //0x24); //0x24 0x28
{0x582F,0x42}, //0x06); //0x06 0x08
{0x5830,0x60}, //0x22); //0x22 0x42
{0x5831,0x42}, //0x40); //0x40 0x40
{0x5832,0x24}, //0x42); //0x42 0x42
{0x5833,0x26}, //0x24); //0x24 0x28
{0x5834,0x24}, //0x26); //0x26 0x0a
{0x5835,0x24}, //0x24); //0x24 0x26
{0x5836,0x24}, //0x22); //0x22 0x24
{0x5837,0x46}, //0x22); //0x22 0x26
{0x5838,0x44}, //0x26); //0x26 0x28
{0x5839,0x46}, //0x44); //0x44 0x4a
{0x583A,0x26}, //0x24); //0x24 0x0a
{0x583B,0x48}, //0x26); //0x26 0x0c
{0x583C,0x44}, //0x28); //0x28 0x2a
{0x583D,0xBF}, //0x42); //0x42 0x28
//EV
{0x3a0f,0x30},
{0x3a10,0x28},
{0x3a1b,0x30},
{0x3a1e,0x26},
{0x3a11,0x60},
{0x3a1f,0x14},
/* WIDTH 1920*1080*/
{0x3035,0x21}, //0x11:60fps 0x21:30fps 0x41:15fps 0xa1:7.5fps
{0x3036,0x54}, // PLL
{0x3820,0x46},
{0x3821,0x00},
{0x3034,0x1a},
{0x3037,0x13},
{0x3108,0x01},
{0x3824,0x01},
{REG_DLY, 10}, // delay 10ms
//timing
//1920x1080
{0x3808,0x07}, //H size MSB
{0x3809,0x80}, //H size LSB
{0x380a,0x04}, //V size MSB
{0x380b,0x40}, //V size LSB
{0x380c,0x09}, //HTS MSB
{0x380d,0xc4}, //HTS LSB
{0x380e,0x04}, //VTS MSB
{0x380f,0x60}, //VTS LSB
//banding step
{0x3a08,0x00}, //50HZ step MSB
{0x3a09,0x54}, //50HZ step LSB
{0x3a0a,0x00}, //60HZ step MSB
{0x3a0b,0x46}, //60HZ step LSB
{0x3a0e,0x0d}, //50HZ step max
{0x3a0d,0x10}, //60HZ step max
{0x3503,0x00}, //AEC enable
{0x350c,0x00},
{0x350d,0x00},
{0x3c07,0x07}, //light meter 1 thereshold
{0x3814,0x11}, //horizton subsample
{0x3815,0x11}, //vertical subsample
{0x3800,0x01}, //x address start high byte
{0x3801,0x50}, //x address start low byte
{0x3802,0x01}, //y address start high byte
{0x3803,0xb2}, //y address start low byte
{0x3804,0x08}, //x address end high byte
{0x3805,0xef}, //x address end low byte
{0x3806,0x05}, //y address end high byte
{0x3807,0xf9}, //y address end low byte
{0x3810,0x00}, //isp hortizontal offset high byte
{0x3811,0x10}, //isp hortizontal offset low byte
{0x3812,0x00}, //isp vertical offset high byte
{0x3813,0x04}, //isp vertical offset low byte
// {0x5308,0x65); //sharpen manual
// {0x5302,0x00); //sharpen offset 1
{0x4002,0x45}, //BLC related
{0x4005,0x18}, //BLC related
{0x3618,0x04},
{0x3612,0x2b},
{0x3709,0x12},
{0x370c,0x00},
{0x3a02,0x04}, //60HZ max exposure limit MSB
{0x3a03,0x60}, //60HZ max exposure limit LSB
{0x3a14,0x04}, //50HZ max exposure limit MSB
{0x3a15,0x60}, //50HZ max exposure limit LSB
{0x4004,0x06}, //BLC line number
{0x3002,0x1c}, //reset JFIFO SFIFO JPG
{0x3006,0xc3}, //enable xx clock
{0x460b,0x37}, //debug mode
{0x460c,0x20}, //PCLK Manuale
{0x4837,0x16}, //PCLK period
{0x5001,0x83}, //ISP effect
// {0x3503,0x00);//AEC enable
{0x302c,0x42}, //bit[7:6]: output drive capability: 00: 1x 01: 2x 10: 3x 11: 4x
{0x3a18,0x00}, //
{0x3a19,0x80}, //
/* DVP */
{0x471b, 0x02}, //bit0: hsync mode enable
{0x471d, 0x02}, //bit1-0: vsync mode
{0x4740, 0x21}, //bit5: pclk polarity, bit1: hsync polarity, bit0: vsync polarity
/* BT601 */
//{0x4711, 0x00}, //HSYNC Mode left padding
//{0x4712, 0x00}, //HSYNC Mode right padding
//{0x471b, 0x03}, //bit0: hsync mode enable
//{0x471d, 0x02}, //bit1-0: vsync mode
//{0x4740, 0x22}, //bit5: pclk polarity, bit1: hsync polarity, bit0: vsync polarity
/* BT656 */
//{0x4715, 0x01}, //bit3-0: CCIR656 dummy line number at beginning of frame
//{0x4719, 0x00}, //bit1-0: SAV/EAV option
//{0x4730, 0x21}, //CCIR656 CTRL00
//{0x4731, 0x00}, //bit0: blanking toggle data order option
//{0x4732, 0xff}, //CCIR656 FS
//{0x4733, 0xfe}, //CCIR656 FE
//{0x4734, 0xfd}, //CCIR656 LS
//{0x4735, 0xfc}, //CCIR656 LE
//{0x4736, 0x00}, //bit3-2: blanking toggle data0 [9:8], bit1-0: blanking toggle data1[9:8]
//{0x4737, 0x00}, //blanking toggle data0
//{0x4738, 0x00}, //blanking toggle data1
//{0x4740, 0x22}, //bit5: pclk polarity, bit1: hsync polarity, bit0: vsync polarity
/* test pattern */
//ov5640_write_register(iic_id, 0x4741,0x07);
{0x5025,0x00},
//ov5640_write_register(iic_id, 0x3031,0x08); //disable internal LDO
// {0x4005,0x1a);
// //power down release
{0x503d,0x00}, //0xd2 square test pattern
{0x3008,0x02},
};
#endif

View File

@@ -0,0 +1,33 @@
/*
* This file is part of the OpenMV project.
* author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV7670 driver.
*
*/
#ifndef __OV7670_H__
#define __OV7670_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov7670_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov7670_init(sensor_t *sensor);
#endif // __OV7670_H__

View File

@@ -0,0 +1,354 @@
/*
* This file is for the OpenMV project so the OV7670 can be used
* author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
*
* OV7670 register definitions.
*/
#ifndef __OV7670_REG_REGS_H__
#define __OV7670_REG_REGS_H__
#define GAIN 0x00 /* AGC Gain control gain setting */
#define BLUE 0x01 /* AWB Blue channel gain setting */
#define RED 0x02 /* AWB Red channel gain setting */
#define VREF 0x03 /* AWB Green channel gain setting */
#define COM1 0x04 /* Common Control 1 */
#define BAVG 0x05 /* U/B Average Level */
#define GAVG 0x06 /* Y/Gb Average Level */
#define AECH 0x07 /* Exposure VAlue - AEC MSB 5 bits */
#define RAVG 0x08 /* V/R Average Level */
#define COM2 0x09 /* Common Control 2 */
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
#define REG_PID 0x0A /* Product ID Number MSB */
#define REG_VER 0x0B /* Product ID Number LSB */
#define COM3 0x0C /* Common Control 3 */
#define COM3_SWAP_OUT 0x40 /* Output data MSB/LSB swap */
#define COM3_TRI_CLK 0x20 /* Tri-state output clock */
#define COM3_TRI_DATA 0x10 /* Tri-state option output */
#define COM3_SCALE_EN 0x08 /* Scale enable */
#define COM3_DCW 0x04 /* DCW enable */
#define COM4 0x0D /* Common Control 4 */
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
#define COM5 0x0E /* Common Control 5 */
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
#define COM6 0x0F /* Common Control 6 */
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
#define CLKRC 0x11 /* Internal Clock */
#define COM7 0x12 /* Common Control 7 */
#define COM7_RESET 0x80 /* SCCB Register Reset */
#define COM7_RES_VGA 0x00 /* Resolution VGA */
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
#define COM7_FMT_YUV 0x00 /* Output format YUV */
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
#define COM7_FMT_RGB 0x04 /* Output format RGB */
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x5)<<0))
#define COM8 0x13 /* Common Control 8 */
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
#define COM8_AGC_EN 0x04 /* AGC Enable */
#define COM8_AWB_EN 0x02 /* AWB Enable */
#define COM8_AEC_EN 0x01 /* AEC Enable */
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
#define COM9 0x14 /* Common Control 9 */
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
#define COM10 0x15 /* Common Control 10 */
#define COM10_NEGATIVE 0x80 /* Output negative data */
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
#define COM10_HREF_REV 0x08 /* HREF reverse */
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
#define RSVD_16 0x16 /* Reserved register */
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start high 8-bit(low 3 bits are at HREF[2:0]) */
#define HSTOP 0x18 /* Horizontal Frame (HREF column) end high 8-bit (low 3 bits are at HREF[5:3]) */
#define VSTART 0x19 /* Vertical Frame (row) Start high 8-bit (low 2 bits are at VREF[1:0]) */
#define VSTOP 0x1A /* Vertical Frame (row) End high 8-bit (low 2 bits are at VREF[3:2]) */
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
#define REG_MIDH 0x1C /* Manufacturer ID Byte High */
#define REG_MIDL 0x1D /* Manufacturer ID Byte Low */
#define MVFP 0x1E /* Mirror/Vflip Enable */
#define MVFP_MIRROR 0x20 /* Mirror image */
#define MVFP_FLIP 0x10 /* Vertical flip */
#define MVFP_SUN 0x02 /* Black sun enable */
#define MVFP_SET_MIRROR(r,x) ((r&0xDF)|((x&1)<<5)) /* change only bit5 according to x */
#define MVFP_SET_FLIP(r,x) ((r&0xEF)|((x&1)<<4)) /* change only bit4 according to x */
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period (Reserved?) */
#define ADCCTR0 0x20 /* ADC control */
#define ADCCTR1 0x21 /* reserved */
#define ADCCTR2 0x22 /* reserved */
#define ADCCTR3 0x23 /* reserved */
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
#define BBIAS 0x27 /* B channel signal output bias (effective only when COM6[3]=1) */
#define GbBIAS 0x28 /* Gb channel signal output bias (effective only when COM6[3]=1) */
#define RSVD_29 0x29 /* reserved */
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
#define RBIAS 0x2C /* R channel signal output bias (effective only when COM6[3]=1) */
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
#define YAVE 0x2F /* Y/G Channel Average Value */
#define HSYST 0x30 /* HSync rising edge delay */
#define HSYEN 0x31 /* HSync falling edge delay */
#define HREF 0x32 /* Image Start and Size Control DIFFERENT CONTROL SEQUENCE */
#define CHLF 0x33 /* Array Current control */
#define ARBLM 0x34 /* Array reference control */
#define RSVD_35 0x35 /* Reserved */
#define RSVD_36 0x36 /* Reserved */
#define ADC 0x37 /* ADC control */
#define ACOM 0x38 /* ADC and analog common mode control */
#define OFON 0x39 /* ADC offset control */
#define TSLB 0x3A /* Line buffer test option */
#define COM11 0x3B /* Common control 11 */
#define COM11_EXP 0x02
#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */
#define COM12 0x3C /* Common control 12 */
#define COM13 0x3D /* Common control 13 */
#define COM13_GAMMA 0x80 /* Gamma enable */
#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */
#define COM14 0x3E /* Common Control 14 */
#define EDGE 0x3F /* edge enhancement adjustment */
#define COM15 0x40 /* Common Control 15 DIFFERENT CONTROLS */
#define COM15_SET_RGB565(r,x) ((r&0xEF)|((x&1)<<4)) /* set rgb565 mode */
#define COM15_RGB565 0x10 /* RGB565 output */
#define COM15_R00FF 0xC0 /* Output range: [00] to [FF] */
#define COM16 0x41 /* Common Control 16 DIFFERENT CONTROLS */
#define COM16_AWBGAIN 0x08 /* AWB gain enable */
#define COM17 0x42 /* Common Control 17 */
#define AWBC1 0x43 /* Reserved */
#define AWBC2 0x44 /* Reserved */
#define AWBC3 0x45 /* Reserved */
#define AWBC4 0x46 /* Reserved */
#define AWBC5 0x47 /* Reserved */
#define AWBC6 0x48 /* Reserved */
#define RSVD_49 0x49 /* Reserved */
#define RSVD_4A 0x4A /* Reserved */
#define REG4B 0x4B /* Register 4B */
#define DNSTH 0x4C /* Denoise strength */
#define RSVD_4D 0x4D /* Reserved */
#define RSVD_4E 0x4E /* Reserved */
#define MTX1 0x4F /* Matrix coefficient 1 */
#define MTX2 0x50 /* Matrix coefficient 2 */
#define MTX3 0x51 /* Matrix coefficient 3 */
#define MTX4 0x52 /* Matrix coefficient 4 */
#define MTX5 0x53 /* Matrix coefficient 5 */
#define MTX6 0x54 /* Matrix coefficient 6 */
#define BRIGHTNESS 0x55 /* Brightness control */
#define CONTRAST 0x56 /* Contrast control */
#define CONTRASCENTER 0x57 /* Contrast center */
#define MTXS 0x58 /* Matrix coefficient sign for coefficient 5 to 0*/
#define RSVD_59 0x59 /* Reserved */
#define RSVD_5A 0x5A /* Reserved */
#define RSVD_5B 0x5B /* Reserved */
#define RSVD_5C 0x5C /* Reserved */
#define RSVD_5D 0x5D /* Reserved */
#define RSVD_5E 0x5E /* Reserved */
#define RSVD_5F 0x5F /* Reserved */
#define RSVD_60 0x60 /* Reserved */
#define RSVD_61 0x61 /* Reserved */
#define LCC1 0x62 /* Lens correction option 1 */
#define LCC2 0x63 /* Lens correction option 2 */
#define LCC3 0x64 /* Lens correction option 3 */
#define LCC4 0x65 /* Lens correction option 4 */
#define LCC5 0x66 /* Lens correction option 5 */
#define MANU 0x67 /* Manual U Value */
#define MANV 0x68 /* Manual V Value */
#define GFIX 0x69 /* Fix gain control */
#define GGAIN 0x6A /* G channel AWB gain */
#define DBLV 0x6B /* PLL and clock ? */
#define AWBCTR3 0x6C /* AWB Control 3 */
#define AWBCTR2 0x6D /* AWB Control 2 */
#define AWBCTR1 0x6E /* AWB Control 1 */
#define AWBCTR0 0x6F /* AWB Control 0 */
#define SCALING_XSC 0x70 /* test pattern and horizontal scaling factor */
#define SCALING_XSC_CBAR(r) (r&0x7F) /* make sure bit7 is 0 for color bar */
#define SCALING_YSC 0x71 /* test pattern and vertical scaling factor */
#define SCALING_YSC_CBAR(r,x) ((r&0x7F)|((x&1)<<7)) /* change bit7 for color bar on/off */
#define SCALING_DCWCTR 0x72 /* DCW control */
#define SCALING_PCLK_DIV 0x73 /* */
#define REG74 0x74 /* */
#define REG75 0x75 /* */
#define REG76 0x76 /* */
#define REG77 0x77 /* */
#define RSVD_78 0x78 /* Reserved */
#define RSVD_79 0x79 /* Reserved */
#define SLOP 0x7A /* Gamma curve highest segment slope */
#define GAM1 0x7B /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
#define GAM2 0x7C /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
#define GAM3 0x7D /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
#define GAM4 0x7E /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
#define GAM5 0x7F /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
#define GAM6 0x80 /* Gamma Curve 6rd Segment Input End Point 0x30 Output Value */
#define GAM7 0x81 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
#define GAM8 0x82 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
#define GAM9 0x83 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
#define GAM10 0x84 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
#define GAM11 0x85 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
#define GAM12 0x86 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
#define GAM13 0x87 /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
#define GAM14 0x88 /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
#define GAM15 0x89 /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
#define RSVD_8A 0x8A /* Reserved */
#define RSVD_8B 0x8B /* Reserved */
#define RGB444 0x8C /* */
#define RSVD_8D 0x8D /* Reserved */
#define RSVD_8E 0x8E /* Reserved */
#define RSVD_8F 0x8F /* Reserved */
#define RSVD_90 0x90 /* Reserved */
#define RSVD_91 0x91 /* Reserved */
#define DM_LNL 0x92 /* Dummy line low 8 bit */
#define DM_LNH 0x93 /* Dummy line high 8 bit */
#define LCC6 0x94 /* Lens correction option 6 */
#define LCC7 0x95 /* Lens correction option 7 */
#define RSVD_96 0x96 /* Reserved */
#define RSVD_97 0x97 /* Reserved */
#define RSVD_98 0x98 /* Reserved */
#define RSVD_99 0x99 /* Reserved */
#define RSVD_9A 0x9A /* Reserved */
#define RSVD_9B 0x9B /* Reserved */
#define RSVD_9C 0x9C /* Reserved */
#define BD50ST 0x9D /* 50 Hz banding filter value */
#define BD60ST 0x9E /* 60 Hz banding filter value */
#define HAECC1 0x9F /* Histogram-based AEC/AGC control 1 */
#define HAECC2 0xA0 /* Histogram-based AEC/AGC control 2 */
#define RSVD_A1 0xA1 /* Reserved */
#define SCALING_PCLK_DELAY 0xA2 /* Pixel clock delay */
#define RSVD_A3 0xA3 /* Reserved */
#define NT_CNTRL 0xA4 /* */
#define BD50MAX 0xA5 /* 50 Hz banding step limit */
#define HAECC3 0xA6 /* Histogram-based AEC/AGC control 3 */
#define HAECC4 0xA7 /* Histogram-based AEC/AGC control 4 */
#define HAECC5 0xA8 /* Histogram-based AEC/AGC control 5 */
#define HAECC6 0xA9 /* Histogram-based AEC/AGC control 6 */
#define HAECC7 0xAA /* Histogram-based AEC/AGC control 7 */
#define HAECC_EN 0x80 /* Histogram-based AEC algorithm enable */
#define BD60MAX 0xAB /* 60 Hz banding step limit */
#define STR_OPT 0xAC /* Register AC */
#define STR_R 0xAD /* R gain for led output frame */
#define STR_G 0xAE /* G gain for led output frame */
#define STR_B 0xAF /* B gain for led output frame */
#define RSVD_B0 0xB0 /* Reserved */
#define ABLC1 0xB1 /* */
#define RSVD_B2 0xB2 /* Reserved */
#define THL_ST 0xB3 /* ABLC target */
#define THL_DLT 0xB5 /* ABLC stable range */
#define RSVD_B6 0xB6 /* Reserved */
#define RSVD_B7 0xB7 /* Reserved */
#define RSVD_B8 0xB8 /* Reserved */
#define RSVD_B9 0xB9 /* Reserved */
#define RSVD_BA 0xBA /* Reserved */
#define RSVD_BB 0xBB /* Reserved */
#define RSVD_BC 0xBC /* Reserved */
#define RSVD_BD 0xBD /* Reserved */
#define AD_CHB 0xBE /* blue channel black level compensation */
#define AD_CHR 0xBF /* Red channel black level compensation */
#define AD_CHGb 0xC0 /* Gb channel black level compensation */
#define AD_CHGr 0xC1 /* Gr channel black level compensation */
#define RSVD_C2 0xC2 /* Reserved */
#define RSVD_C3 0xC3 /* Reserved */
#define RSVD_C4 0xC4 /* Reserved */
#define RSVD_C5 0xC5 /* Reserved */
#define RSVD_C6 0xC6 /* Reserved */
#define RSVD_C7 0xC7 /* Reserved */
#define RSVD_C8 0xC8 /* Reserved */
#define SATCTR 0xC9 /* Saturation control */
#define SET_REG(reg, x) (##reg_DEFAULT|x)
#endif //__OV7670_REG_REGS_H__

View File

@@ -0,0 +1,33 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV7725 driver.
*
*/
#ifndef __OV7725_H__
#define __OV7725_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov7725_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov7725_init(sensor_t *sensor);
#endif // __OV7725_H__

View File

@@ -0,0 +1,335 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV2640 register definitions.
*/
#ifndef __REG_REGS_H__
#define __REG_REGS_H__
#define GAIN 0x00 /* AGC Gain control gain setting */
#define BLUE 0x01 /* AWB Blue channel gain setting */
#define RED 0x02 /* AWB Red channel gain setting */
#define GREEN 0x03 /* AWB Green channel gain setting */
#define BAVG 0x05 /* U/B Average Level */
#define GAVG 0x06 /* Y/Gb Average Level */
#define RAVG 0x07 /* V/R Average Level */
#define AECH 0x08 /* Exposure Value AEC MSBs */
#define COM2 0x09 /* Common Control 2 */
#define COM2_SOFT_SLEEP 0x10 /* Soft sleep mode */
#define COM2_OUT_DRIVE_1x 0x00 /* Output drive capability 1x */
#define COM2_OUT_DRIVE_2x 0x01 /* Output drive capability 2x */
#define COM2_OUT_DRIVE_3x 0x02 /* Output drive capability 3x */
#define COM2_OUT_DRIVE_4x 0x03 /* Output drive capability 4x */
#define REG_PID 0x0A /* Product ID Number MSB */
#define REG_VER 0x0B /* Product ID Number LSB */
#define COM3 0x0C /* Common Control 3 */
#define COM3_VFLIP 0x80 /* Vertical flip image ON/OFF selection */
#define COM3_MIRROR 0x40 /* Horizontal mirror image ON/OFF selection */
#define COM3_SWAP_BR 0x20 /* Swap B/R output sequence in RGB output mode */
#define COM3_SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV output mode */
#define COM3_SWAP_MSB 0x08 /* Swap output MSB/LSB */
#define COM3_TRI_CLOCK 0x04 /* Tri-state option for output clock at power-down period */
#define COM3_TRI_DATA 0x02 /* Tri-state option for output data at power-down period */
#define COM3_COLOR_BAR 0x01 /* Sensor color bar test pattern output enable */
#define COM3_SET_CBAR(r, x) ((r&0xFE)|((x&1)<<0))
#define COM3_SET_MIRROR(r, x) ((r&0xBF)|((x&1)<<6))
#define COM3_SET_FLIP(r, x) ((r&0x7F)|((x&1)<<7))
#define COM4 0x0D /* Common Control 4 */
#define COM4_PLL_BYPASS 0x00 /* Bypass PLL */
#define COM4_PLL_4x 0x40 /* PLL frequency 4x */
#define COM4_PLL_6x 0x80 /* PLL frequency 6x */
#define COM4_PLL_8x 0xc0 /* PLL frequency 8x */
#define COM4_AEC_FULL 0x00 /* AEC evaluate full window */
#define COM4_AEC_1_2 0x10 /* AEC evaluate 1/2 window */
#define COM4_AEC_1_4 0x20 /* AEC evaluate 1/4 window */
#define COM4_AEC_2_3 0x30 /* AEC evaluate 2/3 window */
#define COM5 0x0E /* Common Control 5 */
#define COM5_AFR 0x80 /* Auto frame rate control ON/OFF selection (night mode) */
#define COM5_AFR_SPEED 0x40 /* Auto frame rate control speed selection */
#define COM5_AFR_0 0x00 /* No reduction of frame rate */
#define COM5_AFR_1_2 0x10 /* Max reduction to 1/2 frame rate */
#define COM5_AFR_1_4 0x20 /* Max reduction to 1/4 frame rate */
#define COM5_AFR_1_8 0x30 /* Max reduction to 1/8 frame rate */
#define COM5_AFR_4x 0x04 /* Add frame when AGC reaches 4x gain */
#define COM5_AFR_8x 0x08 /* Add frame when AGC reaches 8x gain */
#define COM5_AFR_16x 0x0c /* Add frame when AGC reaches 16x gain */
#define COM5_AEC_NO_LIMIT 0x01 /* No limit to AEC increase step */
#define COM6 0x0F /* Common Control 6 */
#define COM6_AUTO_WINDOW 0x01 /* Auto window setting ON/OFF selection when format changes */
#define AEC 0x10 /* AEC[7:0] (see register AECH for AEC[15:8]) */
#define CLKRC 0x11 /* Internal Clock */
#define COM7 0x12 /* Common Control 7 */
#define COM7_RESET 0x80 /* SCCB Register Reset */
#define COM7_RES_VGA 0x00 /* Resolution VGA */
#define COM7_RES_QVGA 0x40 /* Resolution QVGA */
#define COM7_BT656 0x20 /* BT.656 protocol ON/OFF */
#define COM7_SENSOR_RAW 0x10 /* Sensor RAW */
#define COM7_FMT_GBR422 0x00 /* RGB output format GBR422 */
#define COM7_FMT_RGB565 0x04 /* RGB output format RGB565 */
#define COM7_FMT_RGB555 0x08 /* RGB output format RGB555 */
#define COM7_FMT_RGB444 0x0C /* RGB output format RGB444 */
#define COM7_FMT_YUV 0x00 /* Output format YUV */
#define COM7_FMT_P_BAYER 0x01 /* Output format Processed Bayer RAW */
#define COM7_FMT_RGB 0x02 /* Output format RGB */
#define COM7_FMT_R_BAYER 0x03 /* Output format Bayer RAW */
#define COM7_SET_FMT(r, x) ((r&0xFC)|((x&0x3)<<0))
#define COM7_SET_RGB(r, x) ((r&0xF0)|(x&0x0C)|COM7_FMT_RGB)
#define COM8 0x13 /* Common Control 8 */
#define COM8_FAST_AUTO 0x80 /* Enable fast AGC/AEC algorithm */
#define COM8_STEP_VSYNC 0x00 /* AEC - Step size limited to vertical blank */
#define COM8_STEP_UNLIMIT 0x40 /* AEC - Step size unlimited step size */
#define COM8_BANDF_EN 0x20 /* Banding filter ON/OFF */
#define COM8_AEC_BANDF 0x10 /* Enable AEC below banding value */
#define COM8_AEC_FINE_EN 0x08 /* Fine AEC ON/OFF control */
#define COM8_AGC_EN 0x04 /* AGC Enable */
#define COM8_AWB_EN 0x02 /* AWB Enable */
#define COM8_AEC_EN 0x01 /* AEC Enable */
#define COM8_SET_AGC(r, x) ((r&0xFB)|((x&0x1)<<2))
#define COM8_SET_AWB(r, x) ((r&0xFD)|((x&0x1)<<1))
#define COM8_SET_AEC(r, x) ((r&0xFE)|((x&0x1)<<0))
#define COM9 0x14 /* Common Control 9 */
#define COM9_HISTO_AVG 0x80 /* Histogram or average based AEC/AGC selection */
#define COM9_AGC_GAIN_2x 0x00 /* Automatic Gain Ceiling 2x */
#define COM9_AGC_GAIN_4x 0x10 /* Automatic Gain Ceiling 4x */
#define COM9_AGC_GAIN_8x 0x20 /* Automatic Gain Ceiling 8x */
#define COM9_AGC_GAIN_16x 0x30 /* Automatic Gain Ceiling 16x */
#define COM9_AGC_GAIN_32x 0x40 /* Automatic Gain Ceiling 32x */
#define COM9_DROP_VSYNC 0x04 /* Drop VSYNC output of corrupt frame */
#define COM9_DROP_HREF 0x02 /* Drop HREF output of corrupt frame */
#define COM9_SET_AGC(r, x) ((r&0x8F)|((x&0x07)<<4))
#define COM10 0x15 /* Common Control 10 */
#define COM10_NEGATIVE 0x80 /* Output negative data */
#define COM10_HSYNC_EN 0x40 /* HREF changes to HSYNC */
#define COM10_PCLK_FREE 0x00 /* PCLK output option: free running PCLK */
#define COM10_PCLK_MASK 0x20 /* PCLK output option: masked during horizontal blank */
#define COM10_PCLK_REV 0x10 /* PCLK reverse */
#define COM10_HREF_REV 0x08 /* HREF reverse */
#define COM10_VSYNC_FALLING 0x00 /* VSYNC changes on falling edge of PCLK */
#define COM10_VSYNC_RISING 0x04 /* VSYNC changes on rising edge of PCLK */
#define COM10_VSYNC_NEG 0x02 /* VSYNC negative */
#define COM10_OUT_RANGE_8 0x01 /* Output data range: Full range */
#define COM10_OUT_RANGE_10 0x00 /* Output data range: Data from [10] to [F0] (8 MSBs) */
#define REG16 0x16 /* Register 16 */
#define REG16_BIT_SHIFT 0x80 /* Bit shift test pattern options */
#define HSTART 0x17 /* Horizontal Frame (HREF column) Start 8 MSBs (2 LSBs are at HREF[5:4]) */
#define HSIZE 0x18 /* Horizontal Sensor Size (2 LSBs are at HREF[1:0]) */
#define VSTART 0x19 /* Vertical Frame (row) Start 8 MSBs (1 LSB is at HREF[6]) */
#define VSIZE 0x1A /* Vertical Sensor Size (1 LSB is at HREF[2]) */
#define PSHFT 0x1B /* Data Format - Pixel Delay Select */
#define REG_MIDH 0x1C /* Manufacturer ID Byte High */
#define REG_MIDL 0x1D /* Manufacturer ID Byte Low */
#define LAEC 0x1F /* Fine AEC Value - defines exposure value less than one row period */
#define COM11 0x20 /* Common Control 11 */
#define COM11_SNGL_FRAME_EN 0x02 /* Single frame ON/OFF selection */
#define COM11_SNGL_XFR_TRIG 0x01 /* Single frame transfer trigger */
#define BDBASE 0x22 /* Banding Filter Minimum AEC Value */
#define DBSTEP 0x23 /* Banding Filter Maximum Step */
#define AEW 0x24 /* AGC/AEC - Stable Operating Region (Upper Limit) */
#define AEB 0x25 /* AGC/AEC - Stable Operating Region (Lower Limit) */
#define VPT 0x26 /* AGC/AEC Fast Mode Operating Region */
#define REG28 0x28 /* Selection on the number of dummy rows, N */
#define HOUTSIZE 0x29 /* Horizontal Data Output Size MSBs (2 LSBs at register EXHCH[1:0]) */
#define EXHCH 0x2A /* Dummy Pixel Insert MSB */
#define EXHCL 0x2B /* Dummy Pixel Insert LSB */
#define VOUTSIZE 0x2C /* Vertical Data Output Size MSBs (LSB at register EXHCH[2]) */
#define ADVFL 0x2D /* LSB of Insert Dummy Rows in Vertical Sync (1 bit equals 1 row) */
#define ADVFH 0x2E /* MSB of Insert Dummy Rows in Vertical Sync */
#define YAVE 0x2F /* Y/G Channel Average Value */
#define LUMHTH 0x30 /* Histogram AEC/AGC Luminance High Level Threshold */
#define LUMLTH 0x31 /* Histogram AEC/AGC Luminance Low Level Threshold */
#define HREF 0x32 /* Image Start and Size Control */
#define DM_LNL 0x33 /* Dummy Row Low 8 Bits */
#define DM_LNH 0x34 /* Dummy Row High 8 Bits */
#define ADOFF_B 0x35 /* AD Offset Compensation Value for B Channel */
#define ADOFF_R 0x36 /* AD Offset Compensation Value for R Channel */
#define ADOFF_GB 0x37 /* AD Offset Compensation Value for GB Channel */
#define ADOFF_GR 0x38 /* AD Offset Compensation Value for GR Channel */
#define OFF_B 0x39 /* AD Offset Compensation Value for B Channel */
#define OFF_R 0x3A /* AD Offset Compensation Value for R Channel */
#define OFF_GB 0x3B /* AD Offset Compensation Value for GB Channel */
#define OFF_GR 0x3C /* AD Offset Compensation Value for GR Channel */
#define COM12 0x3D /* DC offset compensation for analog process */
#define COM13 0x3E /* Common Control 13 */
#define COM13_BLC_EN 0x80 /* BLC enable */
#define COM13_ADC_EN 0x40 /* ADC channel BLC ON/OFF control */
#define COM13_ANALOG_BLC 0x20 /* Analog processing channel BLC ON/OFF control */
#define COM13_ABLC_GAIN_EN 0x04 /* ABLC gain trigger enable */
#define COM14 0x3F /* Common Control 14 */
#define COM15 0x40 /* Common Control 15 */
#define COM16 0x41 /* Common Control 16 */
#define TGT_B 0x42 /* BLC Blue Channel Target Value */
#define TGT_R 0x43 /* BLC Red Channel Target Value */
#define TGT_GB 0x44 /* BLC Gb Channel Target Value */
#define TGT_GR 0x45 /* BLC Gr Channel Target Value */
#define LC_CTR 0x46 /* Lens Correction Control */
#define LC_CTR_RGB_COMP_1 0x00 /* R, G, and B channel compensation coefficient is set by LC_COEF (0x49) */
#define LC_CTR_RGB_COMP_3 0x04 /* R, G, and B channel compensation coefficient is set by registers
LC_COEFB (0x4B), LC_COEF (0x49), and LC_COEFR (0x4C), respectively */
#define LC_CTR_EN 0x01 /* Lens correction enable */
#define LC_XC 0x47 /* X Coordinate of Lens Correction Center Relative to Array Center */
#define LC_YC 0x48 /* Y Coordinate of Lens Correction Center Relative to Array Center */
#define LC_COEF 0x49 /* Lens Correction Coefficient */
#define LC_RADI 0x4A /* Lens Correction Radius */
#define LC_COEFB 0x4B /* Lens Correction B Channel Compensation Coefficient */
#define LC_COEFR 0x4C /* Lens Correction R Channel Compensation Coefficient */
#define FIXGAIN 0x4D /* Analog Fix Gain Amplifier */
#define AREF0 0x4E /* Sensor Reference Control */
#define AREF1 0x4F /* Sensor Reference Current Control */
#define AREF2 0x50 /* Analog Reference Control */
#define AREF3 0x51 /* ADC Reference Control */
#define AREF4 0x52 /* ADC Reference Control */
#define AREF5 0x53 /* ADC Reference Control */
#define AREF6 0x54 /* Analog Reference Control */
#define AREF7 0x55 /* Analog Reference Control */
#define UFIX 0x60 /* U Channel Fixed Value Output */
#define VFIX 0x61 /* V Channel Fixed Value Output */
#define AWBB_BLK 0x62 /* AWB Option for Advanced AWB */
#define AWB_CTRL0 0x63 /* AWB Control Byte 0 */
#define AWB_CTRL0_GAIN_EN 0x80 /* AWB gain enable */
#define AWB_CTRL0_CALC_EN 0x40 /* AWB calculate enable */
#define AWB_CTRL0_WBC_MASK 0x0F /* WBC threshold 2 */
#define DSP_CTRL1 0x64 /* DSP Control Byte 1 */
#define DSP_CTRL1_FIFO_EN 0x80 /* FIFO enable/disable selection */
#define DSP_CTRL1_UV_EN 0x40 /* UV adjust function ON/OFF selection */
#define DSP_CTRL1_SDE_EN 0x20 /* SDE enable */
#define DSP_CTRL1_MTRX_EN 0x10 /* Color matrix ON/OFF selection */
#define DSP_CTRL1_INTRP_EN 0x08 /* Interpolation ON/OFF selection */
#define DSP_CTRL1_GAMMA_EN 0x04 /* Gamma function ON/OFF selection */
#define DSP_CTRL1_BLACK_EN 0x02 /* Black defect auto correction ON/OFF */
#define DSP_CTRL1_WHITE_EN 0x01 /* White defect auto correction ON/OFF */
#define DSP_CTRL2 0x65 /* DSP Control Byte 2 */
#define DSP_CTRL2_VDCW_EN 0x08 /* Vertical DCW enable */
#define DSP_CTRL2_HDCW_EN 0x04 /* Horizontal DCW enable */
#define DSP_CTRL2_VZOOM_EN 0x02 /* Vertical zoom out enable */
#define DSP_CTRL2_HZOOM_EN 0x01 /* Horizontal zoom out enable */
#define DSP_CTRL3 0x66 /* DSP Control Byte 3 */
#define DSP_CTRL3_UV_EN 0x80 /* UV output sequence option */
#define DSP_CTRL3_CBAR_EN 0x20 /* DSP color bar ON/OFF selection */
#define DSP_CTRL3_FIFO_EN 0x08 /* FIFO power down ON/OFF selection */
#define DSP_CTRL3_SCAL1_PWDN 0x04 /* Scaling module power down control 1 */
#define DSP_CTRL3_SCAL2_PWDN 0x02 /* Scaling module power down control 2 */
#define DSP_CTRL3_INTRP_PWDN 0x01 /* Interpolation module power down control */
#define DSP_CTRL3_SET_CBAR(r, x) ((r&0xDF)|((x&1)<<5))
#define DSP_CTRL4 0x67 /* DSP Control Byte 4 */
#define DSP_CTRL4_YUV_RGB 0x00 /* Output selection YUV or RGB */
#define DSP_CTRL4_RAW8 0x02 /* Output selection RAW8 */
#define DSP_CTRL4_RAW10 0x03 /* Output selection RAW10 */
#define AWB_BIAS 0x68 /* AWB BLC Level Clip */
#define AWB_CTRL1 0x69 /* AWB Control 1 */
#define AWB_CTRL2 0x6A /* AWB Control 2 */
#define AWB_CTRL3 0x6B /* AWB Control 3 */
#define AWB_CTRL3_ADVANCED 0x80 /* AWB mode select - Advanced AWB */
#define AWB_CTRL3_SIMPLE 0x00 /* AWB mode select - Simple AWB */
#define AWB_CTRL4 0x6C /* AWB Control 4 */
#define AWB_CTRL5 0x6D /* AWB Control 5 */
#define AWB_CTRL6 0x6E /* AWB Control 6 */
#define AWB_CTRL7 0x6F /* AWB Control 7 */
#define AWB_CTRL8 0x70 /* AWB Control 8 */
#define AWB_CTRL9 0x71 /* AWB Control 9 */
#define AWB_CTRL10 0x72 /* AWB Control 10 */
#define AWB_CTRL11 0x73 /* AWB Control 11 */
#define AWB_CTRL12 0x74 /* AWB Control 12 */
#define AWB_CTRL13 0x75 /* AWB Control 13 */
#define AWB_CTRL14 0x76 /* AWB Control 14 */
#define AWB_CTRL15 0x77 /* AWB Control 15 */
#define AWB_CTRL16 0x78 /* AWB Control 16 */
#define AWB_CTRL17 0x79 /* AWB Control 17 */
#define AWB_CTRL18 0x7A /* AWB Control 18 */
#define AWB_CTRL19 0x7B /* AWB Control 19 */
#define AWB_CTRL20 0x7C /* AWB Control 20 */
#define AWB_CTRL21 0x7D /* AWB Control 21 */
#define GAM1 0x7E /* Gamma Curve 1st Segment Input End Point 0x04 Output Value */
#define GAM2 0x7F /* Gamma Curve 2nd Segment Input End Point 0x08 Output Value */
#define GAM3 0x80 /* Gamma Curve 3rd Segment Input End Point 0x10 Output Value */
#define GAM4 0x81 /* Gamma Curve 4th Segment Input End Point 0x20 Output Value */
#define GAM5 0x82 /* Gamma Curve 5th Segment Input End Point 0x28 Output Value */
#define GAM6 0x83 /* Gamma Curve 6th Segment Input End Point 0x30 Output Value */
#define GAM7 0x84 /* Gamma Curve 7th Segment Input End Point 0x38 Output Value */
#define GAM8 0x85 /* Gamma Curve 8th Segment Input End Point 0x40 Output Value */
#define GAM9 0x86 /* Gamma Curve 9th Segment Input End Point 0x48 Output Value */
#define GAM10 0x87 /* Gamma Curve 10th Segment Input End Point 0x50 Output Value */
#define GAM11 0x88 /* Gamma Curve 11th Segment Input End Point 0x60 Output Value */
#define GAM12 0x89 /* Gamma Curve 12th Segment Input End Point 0x70 Output Value */
#define GAM13 0x8A /* Gamma Curve 13th Segment Input End Point 0x90 Output Value */
#define GAM14 0x8B /* Gamma Curve 14th Segment Input End Point 0xB0 Output Value */
#define GAM15 0x8C /* Gamma Curve 15th Segment Input End Point 0xD0 Output Value */
#define SLOP 0x8D /* Gamma Curve Highest Segment Slope */
#define DNSTH 0x8E /* De-noise Threshold */
#define EDGE0 0x8F /* Edge Enhancement Strength Control */
#define EDGE1 0x90 /* Edge Enhancement Threshold Control */
#define DNSOFF 0x91 /* Auto De-noise Threshold Control */
#define EDGE2 0x92 /* Edge Enhancement Strength Upper Limit */
#define EDGE3 0x93 /* Edge Enhancement Strength Upper Limit */
#define MTX1 0x94 /* Matrix Coefficient 1 */
#define MTX2 0x95 /* Matrix Coefficient 2 */
#define MTX3 0x96 /* Matrix Coefficient 3 */
#define MTX4 0x97 /* Matrix Coefficient 4 */
#define MTX5 0x98 /* Matrix Coefficient 5 */
#define MTX6 0x99 /* Matrix Coefficient 6 */
#define MTX_CTRL 0x9A /* Matrix Control */
#define MTX_CTRL_DBL_EN 0x80 /* Matrix double ON/OFF selection */
#define BRIGHTNESS 0x9B /* Brightness Control */
#define CONTRAST 0x9C /* Contrast Gain */
#define UVADJ0 0x9E /* Auto UV Adjust Control 0 */
#define UVADJ1 0x9F /* Auto UV Adjust Control 1 */
#define SCAL0 0xA0 /* DCW Ratio Control */
#define SCAL1 0xA1 /* Horizontal Zoom Out Control */
#define SCAL2 0xA2 /* Vertical Zoom Out Control */
#define FIFODLYM 0xA3 /* FIFO Manual Mode Delay Control */
#define FIFODLYA 0xA4 /* FIFO Auto Mode Delay Control */
#define SDE 0xA6 /* Special Digital Effect Control */
#define SDE_NEGATIVE_EN 0x40 /* Negative image enable */
#define SDE_GRAYSCALE_EN 0x20 /* Gray scale image enable */
#define SDE_V_FIXED_EN 0x10 /* V fixed value enable */
#define SDE_U_FIXED_EN 0x08 /* U fixed value enable */
#define SDE_CONT_BRIGHT_EN 0x04 /* Contrast/Brightness enable */
#define SDE_SATURATION_EN 0x02 /* Saturation enable */
#define SDE_HUE_EN 0x01 /* Hue enable */
#define USAT 0xA7 /* U Component Saturation Gain */
#define VSAT 0xA8 /* V Component Saturation Gain */
#define HUECOS 0xA9 /* Cosine value × 0x80 */
#define HUESIN 0xAA /* Sine value × 0x80 */
#define SIGN_BIT 0xAB /* Sign Bit for Hue and Brightness */
#define DSPAUTO 0xAC /* DSP Auto Function ON/OFF Control */
#define DSPAUTO_AWB_EN 0x80 /* AWB auto threshold control */
#define DSPAUTO_DENOISE_EN 0x40 /* De-noise auto threshold control */
#define DSPAUTO_EDGE_EN 0x20 /* Sharpness (edge enhancement) auto strength control */
#define DSPAUTO_UV_EN 0x10 /* UV adjust auto slope control */
#define DSPAUTO_SCAL0_EN 0x08 /* Auto scaling factor control (register SCAL0 (0xA0)) */
#define DSPAUTO_SCAL1_EN 0x04 /* Auto scaling factor control (registers SCAL1 (0xA1 and SCAL2 (0xA2))*/
#define SET_REG(reg, x) (##reg_DEFAULT|x)
#endif //__REG_REGS_H__

View File

@@ -0,0 +1,28 @@
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __OV9655_H
#define __OV9655_H
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int ov9655_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int ov9655_init(sensor_t *sensor);
#endif /* __OV9655_H */

View File

@@ -0,0 +1,238 @@
/*
* GC032A register definitions.
*/
#ifndef __OV9655_REG_REGS_H__
#define __OV9655_REG_REGS_H__
/* OV9655 Registers definition */
#define OV9655_REG_GAIN 0x00
#define OV9655_REG_BLUE 0x01
#define OV9655_REG_RED 0x02
#define OV9655_REG_VREF 0x03
#define OV9655_REG_COM1 0x04
#define OV9655_REG_BAVE 0x05
#define OV9655_REG_GbAVE 0x06
#define OV9655_REG_GrAVE 0x07
#define OV9655_REG_RAVE 0x08
#define OV9655_REG_COM2 0x09
#define OV9655_REG_PID 0x0A
#define OV9655_REG_VER 0x0B
#define OV9655_REG_COM3 0x0C
#define OV9655_REG_COM4 0x0D
#define OV9655_REG_COM5 0x0E
#define OV9655_REG_COM6 0x0F
#define OV9655_REG_AEC 0x10
#define OV9655_REG_CLKRC 0x11
#define OV9655_REG_COM7 0x12
#define OV9655_REG_COM8 0x13
#define OV9655_REG_COM9 0x14
#define OV9655_REG_COM10 0x15
#define OV9655_REG_REG16 0x16
#define OV9655_REG_HSTART 0x17
#define OV9655_REG_HSTOP 0x18
#define OV9655_REG_VSTART 0x19
#define OV9655_REG_VSTOP 0x1A
#define OV9655_REG_PSHFT 0x1B
#define OV9655_REG_MIDH 0x1C
#define OV9655_REG_MIDL 0x1D
#define OV9655_REG_MVFP 0x1E
#define OV9655_REG_BOS 0x20
#define OV9655_REG_GBOS 0x21
#define OV9655_REG_GROS 0x22
#define OV9655_REG_ROS 0x23
#define OV9655_REG_AEW 0x24
#define OV9655_REG_AEB 0x25
#define OV9655_REG_VPT 0x26
#define OV9655_REG_BBIAS 0x27
#define OV9655_REG_GbBIAS 0x28
#define OV9655_REG_PREGAIN 0x29
#define OV9655_REG_EXHCH 0x2A
#define OV9655_REG_EXHCL 0x2B
#define OV9655_REG_RBIAS 0x2C
#define OV9655_REG_ADVFL 0x2D
#define OV9655_REG_ADVFH 0x2E
#define OV9655_REG_YAVE 0x2F
#define OV9655_REG_HSYST 0x30
#define OV9655_REG_HSYEN 0x31
#define OV9655_REG_HREF 0x32
#define OV9655_REG_CHLF 0x33
#define OV9655_REG_AREF1 0x34
#define OV9655_REG_AREF2 0x35
#define OV9655_REG_AREF3 0x36
#define OV9655_REG_ADC1 0x37
#define OV9655_REG_ADC2 0x38
#define OV9655_REG_AREF4 0x39
#define OV9655_REG_TSLB 0x3A
#define OV9655_REG_COM11 0x3B
#define OV9655_REG_COM12 0x3C
#define OV9655_REG_COM13 0x3D
#define OV9655_REG_COM14 0x3E
#define OV9655_REG_EDGE 0x3F
#define OV9655_REG_COM15 0x40
#define OV9655_REG_COM16 0x41
#define OV9655_REG_COM17 0x42
#define OV9655_REG_MTX1 0x4F
#define OV9655_REG_MTX2 0x50
#define OV9655_REG_MTX3 0x51
#define OV9655_REG_MTX4 0x52
#define OV9655_REG_MTX5 0x53
#define OV9655_REG_MTX6 0x54
#define OV9655_REG_BRTN 0x55
#define OV9655_REG_CNST1 0x56
#define OV9655_REG_CNST2 0x57
#define OV9655_REG_MTXS 0x58
#define OV9655_REG_AWBOP1 0x59
#define OV9655_REG_AWBOP2 0x5A
#define OV9655_REG_AWBOP3 0x5B
#define OV9655_REG_AWBOP4 0x5C
#define OV9655_REG_AWBOP5 0x5D
#define OV9655_REG_AWBOP6 0x5E
#define OV9655_REG_BLMT 0x5F
#define OV9655_REG_RLMT 0x60
#define OV9655_REG_GLMT 0x61
#define OV9655_REG_LCC1 0x62
#define OV9655_REG_LCC2 0x63
#define OV9655_REG_LCC3 0x64
#define OV9655_REG_LCC4 0x65
#define OV9655_REG_MANU 0x66
#define OV9655_REG_MANV 0x67
#define OV9655_REG_MANY 0x68
#define OV9655_REG_VARO 0x69
#define OV9655_REG_BD50MAX 0x6A
#define OV9655_REG_DBLV 0x6B
#define OV9655_REG_DNSTH 0x70
#define OV9655_REG_POIDX 0x72
#define OV9655_REG_PCKDV 0x73
#define OV9655_REG_XINDX 0x74
#define OV9655_REG_YINDX 0x75
#define OV9655_REG_SLOP 0x7A
#define OV9655_REG_GAM1 0x7B
#define OV9655_REG_GAM2 0x7C
#define OV9655_REG_GAM3 0x7D
#define OV9655_REG_GAM4 0x7E
#define OV9655_REG_GAM5 0x7F
#define OV9655_REG_GAM6 0x80
#define OV9655_REG_GAM7 0x81
#define OV9655_REG_GAM8 0x82
#define OV9655_REG_GAM9 0x83
#define OV9655_REG_GAM10 0x84
#define OV9655_REG_GAM11 0x85
#define OV9655_REG_GAM12 0x86
#define OV9655_REG_GAM13 0x87
#define OV9655_REG_GAM14 0x88
#define OV9655_REG_GAM15 0x89
#define OV9655_REG_COM18 0x8B
#define OV9655_REG_COM19 0x8C
#define OV9655_REG_COM20 0x8D
#define OV9655_REG_DMLNL 0x92
#define OV9655_REG_DMLNH 0x93
#define OV9655_REG_LCC6 0x9D
#define OV9655_REG_LCC7 0x9E
#define OV9655_REG_AECH 0xA1
#define OV9655_REG_BD50 0xA2
#define OV9655_REG_BD60 0xA3
#define OV9655_REG_COM21 0xA4
#define OV9655_REG_GREEN 0xA6
#define OV9655_REG_VZST 0xA7
#define OV9655_REG_REFA8 0xA8
#define OV9655_REG_REFA9 0xA9
#define OV9655_REG_BLC1 0xAC
#define OV9655_REG_BLC2 0xAD
#define OV9655_REG_BLC3 0xAE
#define OV9655_REG_BLC4 0xAF
#define OV9655_REG_BLC5 0xB0
#define OV9655_REG_BLC6 0xB1
#define OV9655_REG_BLC7 0xB2
#define OV9655_REG_BLC8 0xB3
#define OV9655_REG_CTRLB4 0xB4
#define OV9655_REG_FRSTL 0xB7
#define OV9655_REG_FRSTH 0xB8
#define OV9655_REG_ADBOFF 0xBC
#define OV9655_REG_ADROFF 0xBD
#define OV9655_REG_ADGbOFF 0xBE
#define OV9655_REG_ADGrOFF 0xBF
#define OV9655_REG_COM23 0xC4
#define OV9655_REG_BD60MAX 0xC5
#define OV9655_REG_COM24 0xC7
/* Registers bit definition */
/* COM1 Register */
#define CCIR656_FORMAT 0x40
#define HREF_SKIP_0 0x00
#define HREF_SKIP_1 0x04
#define HREF_SKIP_3 0x08
/* COM2 Register */
#define SOFT_SLEEP_MODE 0x10
#define ODCAP_1x 0x00
#define ODCAP_2x 0x01
#define ODCAP_3x 0x02
#define ODCAP_4x 0x03
/* COM3 Register */
#define COLOR_BAR_OUTPUT 0x80
#define OUTPUT_MSB_LAS_SWAP 0x40
#define PIN_REMAP_RESETB_EXPST 0x08
#define RGB565_FORMAT 0x00
#define RGB_OUTPUT_AVERAGE 0x04
#define SINGLE_FRAME 0x01
/* COM5 Register */
#define SLAM_MODE_ENABLE 0x40
#define EXPOSURE_NORMAL_MODE 0x01
/* COM7 Register */
#define SCCB_REG_RESET 0x80
#define FORMAT_CTRL_15fpsVGA 0x00
#define FORMAT_CTRL_30fpsVGA_NoVArioPixel 0x50
#define FORMAT_CTRL_30fpsVGA_VArioPixel 0x60
#define OUTPUT_FORMAT_RAWRGB 0x00
#define OUTPUT_FORMAT_RAWRGB_DATA 0x00
#define OUTPUT_FORMAT_RAWRGB_INTERP 0x01
#define OUTPUT_FORMAT_YUV 0x02
#define OUTPUT_FORMAT_RGB 0x03
/* COM9 Register */
#define GAIN_2x 0x00
#define GAIN_4x 0x10
#define GAIN_8x 0x20
#define GAIN_16x 0x30
#define GAIN_32x 0x40
#define GAIN_64x 0x50
#define GAIN_128x 0x60
#define DROP_VSYNC 0x04
#define DROP_HREF 0x02
/* COM10 Register */
#define RESETb_REMAP_SLHS 0x80
#define HREF_CHANGE_HSYNC 0x40
#define PCLK_ON 0x00
#define PCLK_OFF 0x20
#define PCLK_POLARITY_REV 0x10
#define HREF_POLARITY_REV 0x08
#define RESET_ENDPOINT 0x04
#define VSYNC_NEG 0x02
#define HSYNC_NEG 0x01
/* TSLB Register */
#define PCLK_DELAY_0 0x00
#define PCLK_DELAY_2 0x40
#define PCLK_DELAY_4 0x80
#define PCLK_DELAY_6 0xC0
#define OUTPUT_BITWISE_REV 0x20
#define UV_NORMAL 0x00
#define UV_FIXED 0x10
#define YUV_SEQ_YUYV 0x00
#define YUV_SEQ_YVYU 0x02
#define YUV_SEQ_VYUY 0x04
#define YUV_SEQ_UYVY 0x06
#define BANDING_FREQ_50 0x02
#define RGB_NORMAL 0x00
#define RGB_565 0x10
#define RGB_555 0x30
#endif //__GC032A_REG_REGS_H__

View File

@@ -0,0 +1,449 @@
#ifndef _OV9655_SETTINGS_H_
#define _OV9655_SETTINGS_H_
#include <stdint.h>
#include <stdbool.h>
#include "ov9655_regs.h"
#define REG_DLY 0xff
/* Initialization sequence for VGA resolution (640x480)*/
static const uint8_t OV9655_VGA[][2]=
{
{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0xb5, 0x00 },{ 0x35, 0x00 },{ 0xa8, 0xc1 },{ 0x3a, 0xcc },
{ 0x3d, 0x99 },{ 0x77, 0x02 },{ 0x13, 0xe7 },{ 0x26, 0x72 },{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x2c, 0x08 },
{ 0xab, 0x04 },{ 0x6e, 0x00 },{ 0x6d, 0x55 },{ 0x00, 0x11 },{ 0x10, 0x7b },{ 0xbb, 0xae },{ 0x11, 0x03 },
{ 0x72, 0x00 },{ 0x3e, 0x0c },{ 0x74, 0x3a },{ 0x76, 0x01 },{ 0x75, 0x35 },{ 0x73, 0x00 },{ 0xc7, 0x80 },
{ 0x62, 0x00 },{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x01 },{ 0xc3, 0x4e },{ 0x33, 0x00 },
{ 0xa4, 0x50 },{ 0xaa, 0x92 },{ 0xc2, 0x01 },{ 0xc1, 0xc8 },{ 0x1e, 0x04 },{ 0xa9, 0xef },{ 0x0e, 0x61 },
{ 0x39, 0x57 },{ 0x0f, 0x48 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x12, 0x63 },{ 0x03, 0x12 },{ 0x32, 0xff },
{ 0x17, 0x16 },{ 0x18, 0x02 },{ 0x19, 0x01 },{ 0x1a, 0x3d },{ 0x36, 0xfa },{ 0x69, 0x0a },{ 0x8c, 0x8d },
{ 0xc0, 0xaa },{ 0x40, 0xd0 },{ 0x43, 0x14 },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },
{ 0x48, 0x3c },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },
{ 0x6c, 0x0c },{ 0xc6, 0x85 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0x71, 0x78 },{ 0xa5, 0x68 },{ 0x6f, 0x9e },
{ 0x42, 0xc0 },{ 0x3f, 0x82 },{ 0x8a, 0x23 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x3b, 0xcc },{ 0x34, 0x3d },
{ 0x41, 0x40 },{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcd, 0x93 },{ 0x7a, 0x20 },{ 0x7b, 0x1c },{ 0x7c, 0x28 },
{ 0x7d, 0x3c },{ 0x7e, 0x5a },{ 0x7f, 0x68 },{ 0x80, 0x76 },{ 0x81, 0x80 },{ 0x82, 0x88 },{ 0x83, 0x8f },
{ 0x84, 0x96 },{ 0x85, 0xa3 },{ 0x86, 0xaf },{ 0x87, 0xc4 },{ 0x88, 0xd7 },{ 0x89, 0xe8 },{ 0x4f, 0x98 },
{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x52, 0x28 },{ 0x53, 0x70 },{ 0x54, 0x98 },{ 0x58, 0x1a },{ 0x6b, 0x5a },
{ 0x90, 0x92 },{ 0x91, 0x92 },{ 0x9f, 0x90 },{ 0xa0, 0x90 },{ 0x16, 0x24 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },
{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },{ 0xb4, 0x20 },
{ 0xb6, 0xaf },{ 0x29, 0x15 },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9e, 0x02 },{ 0x04, 0x03 },{ 0x05, 0x2e },
{ 0x06, 0x2e },{ 0x07, 0x2e },{ 0x08, 0x2e },{ 0x2f, 0x2e },{ 0x4a, 0xe9 },{ 0x4b, 0xdd },{ 0x4c, 0xdd },
{ 0x4d, 0xdd },{ 0x4e, 0xdd },{ 0x70, 0x06 },{ 0xa6, 0x40 },{ 0xbc, 0x02 },{ 0xbd, 0x01 },{ 0xbe, 0x02 },
{ 0xbf, 0x01 },
};
/* Initialization sequence for VGA resolution (640x480) in YUV format */
static const uint8_t OV9655_VGA_YUV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },
{ 0x17, 0x18 },{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },
{ 0x26, 0x72 },{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },
{ 0x32, 0x24 },{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },
{ 0x3a, 0x0c },{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x01 },
{ 0x42, 0xc0 },{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },
{ 0x4a, 0xfc },{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },
{ 0x54, 0xb0 },{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },
{ 0x5b, 0x64 },{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },
{ 0x62, 0x00 },{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },
{ 0x6c, 0x04 },{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x00 },
{ 0x73, 0x01 },{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },
{ 0x7c, 0x15 },{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },
{ 0x83, 0x96 },{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },
{ 0x8a, 0x24 },{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },
{ 0xa0, 0x79 },{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },
{ 0xaa, 0x92 },{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },
{ 0xb3, 0x20 },{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },
{ 0xbe, 0x7f },{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },
{ 0xc7, 0x81 },{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },
{ 0xFF, 0xFF },
};
/* Initialization sequence for VGA resolution (640x480) in YUV format with fixed UV data*/
static const uint8_t OV9655_VGA_YUV_FIXED_UV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x18 },
{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },{ 0x32, 0x24 },
{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3a, 0x1c },//{ 0x3a, 0x0c },
{ 0x67, 0x5a },{ 0x68, 0xa5 },
{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x00 },{ 0x42, 0xc0 },
{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },
{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },{ 0x54, 0xb0 },
{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },
{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },
{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },{ 0x6c, 0x04 },
{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x33 },{ 0x73, 0x01 }, // 0x72 changed from 0x11 to 0x10
{ 0x74, 0x03 },{ 0x75, 0x03 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },{ 0x7c, 0x15 },
{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },{ 0x83, 0x96 },
{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x24 },
{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },{ 0xa0, 0x79 },
{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },{ 0xaa, 0x92 },
{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },
{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },{ 0xbe, 0x7f },
{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },////{ 0xc7, 0x81 },
{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },{ 0xFF, 0xFF },
};
/* Initialization sequence for QVGA resolution (320x240) */
static const uint8_t OV9655_QVGA[][2]=
{
{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x09, 0x01 },{ 0x0b, 0x57 },
{ 0x0e, 0x61 },{ 0x0f, 0x40 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xc7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },
{ 0x17, 0x18 },{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x00 },{ 0x24, 0x3c },{ 0x25, 0x36 },
{ 0x26, 0x72 },{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },
{ 0x32, 0x12 },{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },
{ 0x3a, 0xcc },{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x02 },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x41 },
{ 0x42, 0xc0 },{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },
{ 0x4a, 0xfc },{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x4f, 0x98 },{ 0x50, 0x98 },
{ 0x51, 0x00 },{ 0x52, 0x28 },{ 0x53, 0x70 },{ 0x54, 0x98 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },
{ 0x5b, 0x64 },{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },
{ 0x62, 0x00 },{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },
{ 0x6c, 0x04 },{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },
{ 0x73, 0x01 },{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7A, 0x12 },{ 0x7B, 0x08 },
{ 0x7C, 0x16 },{ 0x7D, 0x30 },{ 0x7E, 0x5e },{ 0x7F, 0x72 },{ 0x80, 0x82 },{ 0x81, 0x8e },{ 0x82, 0x9a },
{ 0x83, 0xa4 },{ 0x84, 0xac },{ 0x85, 0xb8 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },
{ 0x8a, 0x24 },{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },
{ 0xa0, 0x79 },{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },
{ 0xaa, 0x92 },{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },
{ 0xb3, 0x20 },{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },
{ 0xbd, 0x7f },{ 0xbe, 0x7f },{ 0xbf, 0x7f },{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },
{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },
{ 0xcd, 0x93 },{ 0x12, 0x63 },{ 0x40, 0x10 },
};
/* Initialization sequence for QVGA resolution (320x240) in YUV format */
static const uint8_t OV9655_QVGA_YUV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x18 },
{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },{ 0x32, 0x24 },
{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3a, 0x0c },
{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x01 },{ 0x42, 0xc0 },
{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },
{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },{ 0x54, 0xb0 },
{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },
{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },
{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },{ 0x6c, 0x04 },
{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },{ 0x73, 0x01 },
{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },{ 0x7c, 0x15 },
{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },{ 0x83, 0x96 },
{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x24 },
{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },{ 0xa0, 0x79 },
{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },{ 0xaa, 0x92 },
{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },
{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },{ 0xbe, 0x7f },
{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },////{ 0xc7, 0x81 },
{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },{ 0xFF, 0xFF },
};
/* Initialization sequence for QVGA resolution (320x240) in YUV format with fixed UV data*/
static const uint8_t OV9655_QVGA_YUV_FIXED_UV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x18 },
{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },{ 0x32, 0x24 },
{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3a, 0x1c },
{ 0x67, 0x5a },{ 0x68, 0xa5 },
{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x01 },{ 0x42, 0xc0 },
{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },
{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },{ 0x54, 0xb0 },
{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },
{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },
{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },{ 0x6c, 0x04 },
{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },{ 0x73, 0x01 },
{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },{ 0x7c, 0x15 },
{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },{ 0x83, 0x96 },
{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x24 },
{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },{ 0xa0, 0x79 },
{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },{ 0xaa, 0x92 },
{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },
{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },{ 0xbe, 0x7f },
{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },////{ 0xc7, 0x81 },
{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },{ 0xFF, 0xFF },
};
/* Initialization sequence for QQVGA resolution (160x120) */
static const uint8_t OV9655_QQVGA[][2]=
{
{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x09, 0x01 },{ 0x0b, 0x57 },
{ 0x0e, 0x61 },{ 0x0f, 0x40 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xc7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },
{ 0x17, 0x18 },{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x00 },{ 0x24, 0x3c },{ 0x25, 0x36 },
{ 0x26, 0x72 },{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },
{ 0x32, 0x12 },{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },
{ 0x3a, 0xcc },{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x02 },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x41 },
{ 0x42, 0xc0 },{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },
{ 0x4a, 0xfc },{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x4f, 0x98 },{ 0x50, 0x98 },
{ 0x51, 0x00 },{ 0x52, 0x28 },{ 0x53, 0x70 },{ 0x54, 0x98 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },
{ 0x5b, 0x64 },{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },
{ 0x62, 0x00 },{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },
{ 0x6c, 0x04 },{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },
{ 0x73, 0x01 },{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7A, 0x12 },{ 0x7B, 0x08 },
{ 0x7C, 0x16 },{ 0x7D, 0x30 },{ 0x7E, 0x5e },{ 0x7F, 0x72 },{ 0x80, 0x82 },{ 0x81, 0x8e },{ 0x82, 0x9a },
{ 0x83, 0xa4 },{ 0x84, 0xac },{ 0x85, 0xb8 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },
{ 0x8a, 0x24 },{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },
{ 0xa0, 0x79 },{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },
{ 0xaa, 0x92 },{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },
{ 0xb3, 0x20 },{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },
{ 0xbd, 0x7f },{ 0xbe, 0x7f },{ 0xbf, 0x7f },{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },
{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x82 },{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },
{ 0xcd, 0x93 },{ 0x12, 0x63 },{ 0x40, 0x10 },
};
/* Initialization sequence for QQVGA resolution (160x120) in YUV format */
static const uint8_t OV9655_QQVGA_YUV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x18 },
{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },{ 0x32, 0x24 },
{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3a, 0x0c },
{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x01 },{ 0x42, 0xc0 },
{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },
{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },{ 0x54, 0xb0 },
{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },
{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },
{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },{ 0x6c, 0x04 },
{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },{ 0x73, 0x01 },
{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },{ 0x7c, 0x15 },
{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },{ 0x83, 0x96 },
{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x24 },
{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },{ 0xa0, 0x79 },
{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },{ 0xaa, 0x92 },
{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },
{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },{ 0xbe, 0x7f },
{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },////{ 0xc7, 0x81 },
{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },{ 0xFF, 0xFF },
};
/* Initialization sequence for QQVGA resolution (160x120) in YUV format with fixed UV data*/
static const uint8_t OV9655_QQVGA_YUV_FIXED_UV[][2]=
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x02 },{ 0x04, 0x03 },{ 0x0e, 0x61 },
{ 0x0f, 0x42 },{ 0x11, 0x01 },{ 0x12, 0x62 },{ 0x13, 0xe7 },{ 0x14, 0x3a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x18 },
{ 0x18, 0x04 },{ 0x19, 0x01 },{ 0x1a, 0x81 },{ 0x1e, 0x04 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2a, 0x00 },{ 0x2b, 0x00 },{ 0x2c, 0x08 },{ 0x32, 0x24 },
{ 0x33, 0x00 },{ 0x34, 0x3f },{ 0x35, 0x00 },{ 0x36, 0x3a },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3a, 0x1c },
{ 0x67, 0x5a },{ 0x68, 0xa5 },
{ 0x3b, 0x04 },{ 0x3d, 0x99 },{ 0x3e, 0x0e },{ 0x3f, 0xc1 },{ 0x40, 0xc0 },{ 0x41, 0x01 },{ 0x42, 0xc0 },
{ 0x43, 0x0a },{ 0x44, 0xf0 },{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },
{ 0x4b, 0xfc },{ 0x4c, 0x7f },{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x52, 0x28 },{ 0x53, 0x88 },{ 0x54, 0xb0 },
{ 0x4f, 0x98 },{ 0x50, 0x98 },{ 0x51, 0x00 },{ 0x58, 0x1a },{ 0x59, 0x85 },{ 0x5a, 0xa9 },{ 0x5b, 0x64 },
{ 0x5c, 0x84 },{ 0x5d, 0x53 },{ 0x5e, 0x0e },{ 0x5f, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },
{ 0x63, 0x00 },{ 0x64, 0x02 },{ 0x65, 0x20 },{ 0x66, 0x00 },{ 0x69, 0x0a },{ 0x6b, 0x5a },{ 0x6c, 0x04 },
{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },{ 0x72, 0x11 },{ 0x73, 0x01 },
{ 0x74, 0x10 },{ 0x75, 0x10 },{ 0x76, 0x01 },{ 0x77, 0x02 },{ 0x7a, 0x12 },{ 0x7b, 0x08 },{ 0x7c, 0x15 },
{ 0x7d, 0x24 },{ 0x7e, 0x45 },{ 0x7f, 0x55 },{ 0x80, 0x6a },{ 0x81, 0x78 },{ 0x82, 0x87 },{ 0x83, 0x96 },
{ 0x84, 0xa3 },{ 0x85, 0xb4 },{ 0x86, 0xc3 },{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x24 },
{ 0x8c, 0x80 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9d, 0x02 },{ 0x9e, 0x02 },{ 0x9f, 0x7a },{ 0xa0, 0x79 },
{ 0xa1, 0x40 },{ 0xa4, 0x50 },{ 0xa5, 0x68 },{ 0xa6, 0x4a },{ 0xa8, 0xc1 },{ 0xa9, 0xef },{ 0xaa, 0x92 },
{ 0xab, 0x04 },{ 0xac, 0x80 },{ 0xad, 0x80 },{ 0xae, 0x80 },{ 0xaf, 0x80 },{ 0xb2, 0xf2 },{ 0xb3, 0x20 },
{ 0xb4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xbc, 0x7f },{ 0xbd, 0x7f },{ 0xbe, 0x7f },
{ 0xbf, 0x7f },{ 0xc0, 0xaa },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x05 },{ 0xc7, 0x81 },////{ 0xc7, 0x81 },
{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0xcd, 0x93 },{ 0xFF, 0xFF },
};
/* sensor output 2560 * 512 somehow*/
static const uint8_t OV9655_QVGA_RAW_DATA[][2] =
{
{ 0x12, 0x80 },{ 0x00, 0x00 },{ 0x01, 0x80 },{ 0x02, 0x80 },{ 0x03, 0x1b },{ 0x04, 0x07 },{ 0x0E, 0x61 },
{ 0x0F, 0x40 },{ 0x11, 0x03 },{ 0x12, 0x02 },{ 0x13, 0xe7 },{ 0x14, 0x2a },{ 0x15, 0x00 },{ 0x16, 0x24 },{ 0x17, 0x1d },
{ 0x18, 0xbd },{ 0x19, 0x01 },{ 0x1A, 0x81 },{ 0x1E, 0x00 },{ 0x24, 0x3c },{ 0x25, 0x36 },{ 0x26, 0x72 },
{ 0x27, 0x08 },{ 0x28, 0x08 },{ 0x29, 0x15 },{ 0x2A, 0x00 },{ 0x2B, 0x00 },{ 0x2C, 0x08 },{ 0x32, 0x3f },
{ 0x33, 0x00 },{ 0x35, 0x00 },{ 0x38, 0x72 },{ 0x39, 0x57 },{ 0x3A, 0x80 },{ 0x3B, 0xcc },{ 0x3D, 0x99 },
{ 0x3E, 0x0c },{ 0x3F, 0x82 },{ 0x40, 0xc0 },{ 0x41, 0x01 },{ 0x42, 0xc1 },{ 0x43, 0x0a },{ 0x44, 0xf0 },
{ 0x45, 0x46 },{ 0x46, 0x62 },{ 0x47, 0x2a },{ 0x48, 0x3c },{ 0x4a, 0xfc },{ 0x4b, 0xfc },{ 0x4c, 0x7f },
{ 0x4d, 0x7f },{ 0x4e, 0x7f },{ 0x59, 0x85 },{ 0x5A, 0xa9 },{ 0x5B, 0x64 },{ 0x5C, 0x84 },{ 0x5D, 0x53 },
{ 0x5E, 0x0e },{ 0x5F, 0xf0 },{ 0x60, 0xf0 },{ 0x61, 0xf0 },{ 0x62, 0x00 },{ 0x63, 0x00 },{ 0x64, 0x02 },
{ 0x6B, 0xda },{ 0x6c, 0x04 },{ 0x6d, 0x55 },{ 0x6e, 0x00 },{ 0x6f, 0x9d },{ 0x70, 0x21 },{ 0x71, 0x78 },
{ 0x77, 0x02 },{ 0x7A, 0x12 },{ 0x7B, 0x08 },{ 0x7C, 0x16 },{ 0x7D, 0x30 },{ 0x7E, 0x5e },{ 0x7F, 0x72 },
{ 0x80, 0x82 },{ 0x81, 0x8e },{ 0x82, 0x9a },{ 0x83, 0xa4 },{ 0x84, 0xac },{ 0x85, 0xb8 },{ 0x86, 0xc3 },
{ 0x87, 0xd6 },{ 0x88, 0xe6 },{ 0x89, 0xf2 },{ 0x8a, 0x03 },{ 0x90, 0x7d },{ 0x91, 0x7b },{ 0x9D, 0x03 },
{ 0x9f, 0x7a },{ 0xa0, 0x79 },{ 0xA1, 0x40 },{ 0xA4, 0x50 },{ 0xa5, 0x68 },{ 0xA6, 0x4a },{ 0xA8, 0xc1 },
{ 0xA9, 0xef },{ 0xaa, 0x92 },{ 0xab, 0x04 },{ 0xAC, 0x80 },{ 0xAD, 0x80 },{ 0xAE, 0x80 },{ 0xAF, 0x80 },
{ 0xB2, 0xf2 },{ 0xB3, 0x20 },{ 0xB4, 0x20 },{ 0xb5, 0x00 },{ 0xb6, 0xaf },{ 0xbb, 0xae },{ 0xBC, 0x7f },
{ 0xBD, 0x7f },{ 0xBE, 0x7f },{ 0xBF, 0x7f },{ 0xc1, 0xc0 },{ 0xc2, 0x01 },{ 0xc3, 0x4e },{ 0xc6, 0x85 },
{ 0xC7, 0x80 },{ 0xc9, 0xe0 },{ 0xca, 0xe8 },{ 0xcb, 0xf0 },{ 0xcc, 0xd8 },{ 0xcd, 0x93 },{ 0x34, 0x3d },
{ 0x36, 0x34 },{ 0x65, 0x16 },{ 0x66, 0x01 },{ 0x69, 0x02 },{ 0x8C, 0x0d },{ 0x8D, 0x03 },{ 0x9E, 0x04 },
{ 0xc0, 0xe2 },{ 0x6A, 0x05 },{ 0xA2, 0x9d },{ 0xA3, 0x83 },{ 0xC5, 0x07 },{ 0x76, 0x01 },{ 0x12, 0x00 },
};
static const uint8_t ov9655_default_regs[][2] = {
{0x3a, 0x0c},
{0x40, 0xd0},
{0x12, 0x14},
{0x32, 0x80},
{0x17, 0x16},
{0x18, 0x04},
{0x19, 0x02},//我抉忌批?我技
{0x1a, 0x7a},//0x7a,
{0x03, 0x03},//我抉忌批?我技
{0x3e, 0x00},//
{0x70, 0x00},
{0x71, 0x00},
{0x72, 0x11},
{0x73, 0x00},//
{0xa2, 0x02},
{0x11, 0x01},
{0x7a, 0x20},
{0x7b, 0x1c},
{0x7c, 0x28},
{0x7d, 0x3c},
{0x7e, 0x55},
{0x7f, 0x68},
{0x80, 0x76},
{0x81, 0x80},
{0x82, 0x88},
{0x83, 0x8f},
{0x84, 0x96},
{0x85, 0xa3},
{0x86, 0xaf},
{0x87, 0xc4},
{0x88, 0xd7},
{0x89, 0xe8},
{0x13, 0xe7},
{0x00, 0x00},//AGC
{0x10, 0x00},
{0x0d, 0x00},
{0x14, 0x31},//0x38, limit the max gain
{0xa5, 0x05},
{0xab, 0x07},
{0x24, 0x75},
{0x25, 0x63},
{0x26, 0xA5},
{0x9f, 0x78},
{0xa0, 0x68},
{0xa1, 0x03},
{0xa6, 0xdf},
{0xa7, 0xdf},
{0xa8, 0xf0},
{0xa9, 0x90},
{0xaa, 0x94},
{0x13, 0xe7},
{0x0e, 0x61},
{0x0f, 0x82},
{0x16, 0x02},
{0x1e, 0x07},
{0x21, 0x02},
{0x22, 0x91},
{0x29, 0x07},
{0x33, 0x0b},
{0x35, 0x0b},
{0x37, 0x1d},
{0x38, 0x71},
{0x39, 0x2a},//
{0x3c, 0x78},
{0x4d, 0x40},
{0x4e, 0x20},
{0x69, 0x0c},
{0x6b, 0x00},//PLL
{0x74, 0x19},
{0x8d, 0x4f},
{0x8e, 0x00},
{0x8f, 0x00},
{0x90, 0x00},
{0x91, 0x00},
{0x92, 0x00},
{0x96, 0x00},
{0x9a, 0x80},
{0xb0, 0x84},
{0xb1, 0x0c},
{0xb2, 0x0e},
{0xb3, 0x82},
{0xb8, 0x0a},
{0x43, 0x14},
{0x44, 0xf0},
{0x45, 0x34},
{0x46, 0x58},
{0x47, 0x28},
{0x48, 0x3a},
{0x59, 0x88},
{0x5a, 0x88},
{0x5b, 0x44},
{0x5c, 0x67},
{0x5d, 0x49},
{0x5e, 0x0e},
{0x64, 0x04},
{0x65, 0x20},
{0x66, 0x05},
{0x94, 0x04},
{0x95, 0x08},
{0x6c, 0x0a},
{0x6d, 0x55},
{0x6e, 0x11},
{0x6f, 0x9f},//0x9e for advance AWB
{0x6a, 0x40},
{0x01, 0x40},
{0x02, 0x40},
{0x13, 0xe7},
{0x15, 0x18},
{0x4f, 0x80},
{0x50, 0x80},
{0x51, 0x00},
{0x52, 0x22},
{0x53, 0x5e},
{0x54, 0x80},
{0x58, 0x9e},
{0x41, 0x08},
{0x3f, 0x00},
{0x75, 0x05},
{0x76, 0xe1},
{0x4c, 0x00},
{0x77, 0x01},
{0x3d, 0xc0},
{0x4b, 0x09},
{0xc9, 0x60},
{0x41, 0x38},
{0x56, 0x40},
{0x34, 0x11},
{0x3b, 0x12},
{0xa4, 0x89},
{0x96, 0x00},
{0x97, 0x30},
{0x98, 0x20},
{0x99, 0x30},
{0x9a, 0x84},
{0x9b, 0x29},
{0x9c, 0x03},
{0x9d, 0x4c},
{0x9e, 0x3f},
{0x78, 0x04},
{0x79, 0x01},
{0xc8, 0xf0},
{0x79, 0x0f},
{0xc8, 0x00},
{0x79, 0x10},
{0xc8, 0x7e},
{0x79, 0x0a},
{0xc8, 0x80},
{0x79, 0x0b},
{0xc8, 0x01},
{0x79, 0x0c},
{0xc8, 0x0f},
{0x79, 0x0d},
{0xc8, 0x20},
{0x79, 0x09},
{0xc8, 0x80},
{0x79, 0x02},
{0xc8, 0xc0},
{0x79, 0x03},
{0xc8, 0x40},
{0x79, 0x05},
{0xc8, 0x30},
{0x79, 0x26},
{0x09, 0x03},
{0x55, 0x00},
{0x56, 0x40},
{0x3b, 0x42},
{0x07, 0x08},
{0x10, 0x80},
};
#endif

View File

@@ -0,0 +1,31 @@
/*
*
* SC030IOT DVP driver.
*
*/
#ifndef __SC030IOT_H__
#define __SC030IOT_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int sc030iot_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int sc030iot_init(sensor_t *sensor);
#endif // __SC030IOT_H__

View File

@@ -0,0 +1,491 @@
//version: V01P00_20220303
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
//pin :BIT0 pwdn// BIT1:reset
//avdd 0:3.3V// 1:2.5V// 2:1.8V
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
/*
[DataBase]
DBName=Dothinkey
[Vendor]
VendorName=SmartSens
[Sensor]
SensorName=SC031IOT
width=640
height=480
port=1
type=2
pin=3
SlaveID=0xd0
mode=0
FlagReg=0xf7
FlagMask=0xff
FlagData=0xfa
FlagReg1=0xf8
FlagMask1=0xff
FlagData1=0x46
outformat=0
mclk=20
avdd=2.80000
dovdd=2.800000
dvdd=1.5
Ext0=0
Ext1=0
Ext2=0
AFVCC=0.0000
VPP=0.000000
*/
#include <stdint.h>
static const uint8_t sc030iot_default_init_regs[][2] = {
{0xf0, 0x30},
{0x01, 0xff},
{0x02, 0xff},
{0x22, 0x07},
{0x19, 0xff},
{0x3f, 0x82},
{0x30, 0x02},
{0xf0, 0x01},
{0x70, 0x00},
{0x71, 0x80},
{0x72, 0x20},
{0x73, 0x00},
{0x74, 0xe0},
{0x75, 0x10},
{0x76, 0x81},
{0x77, 0x88},
{0x78, 0xe1},
{0x79, 0x01},
{0xf5, 0x01},
{0xf4, 0x0a},
{0xf0, 0x36},
{0x37, 0x79},
{0x31, 0x82},
{0x3e, 0x60},
{0x30, 0xf0},
{0x33, 0x33},
{0xf0, 0x32},
{0x48, 0x02},
{0xf0, 0x33},
{0x02, 0x12},
{0x7c, 0x02},
{0x7d, 0x0e},
{0xa2, 0x04},
{0x5e, 0x06},
{0x5f, 0x0a},
{0x0b, 0x58},
{0x06, 0x38},
{0xf0, 0x32},
{0x48, 0x02},
{0xf0, 0x39},
{0x02, 0x70},
{0xf0, 0x45},
{0x09, 0x1c},
{0xf0, 0x37},
{0x22, 0x0d},
{0xf0, 0x33},
{0x33, 0x10},
{0xb1, 0x80},
{0x34, 0x40},
{0x0b, 0x54},
{0xb2, 0x78},
{0xf0, 0x36},
{0x11, 0x80},
{0xf0, 0x30},
{0x38, 0x44},
{0xf0, 0x33},
{0xb3, 0x51},
{0x01, 0x10},
{0x0b, 0x6c},
{0x06, 0x24},
{0xf0, 0x36},
{0x31, 0x82},
{0x3e, 0x60},
{0x30, 0xf0},
{0x33, 0x33},
{0xf0, 0x34},
{0x9f, 0x02},
{0xa6, 0x40},
{0xa7, 0x47},
{0xe8, 0x5f},
{0xa8, 0x51},
{0xa9, 0x44},
{0xe9, 0x36},
{0xf0, 0x33},
{0xb3, 0x51},
{0x64, 0x17},
{0x90, 0x01},
{0x91, 0x03},
{0x92, 0x07},
{0x01, 0x10},
{0x93, 0x10},
{0x94, 0x10},
{0x95, 0x10},
{0x96, 0x01},
{0x97, 0x07},
{0x98, 0x1f},
{0x99, 0x10},
{0x9a, 0x20},
{0x9b, 0x28},
{0x9c, 0x28},
{0xf0, 0x36},
{0x70, 0x54},
{0xb6, 0x40},
{0xb7, 0x41},
{0xb8, 0x43},
{0xb9, 0x47},
{0xba, 0x4f},
{0xb0, 0x8b},
{0xb1, 0x8b},
{0xb2, 0x8b},
{0xb3, 0x9b},
{0xb4, 0xb8},
{0xb5, 0xf0},
{0x7e, 0x41},
{0x7f, 0x47},
{0x77, 0x80},
{0x78, 0x84},
{0x79, 0x8a},
{0xa0, 0x47},
{0xa1, 0x5f},
{0x96, 0x43},
{0x97, 0x44},
{0x98, 0x54},
{0xf0, 0x00},
{0xf0, 0x01},
{0x73, 0x00},
{0x74, 0xe0},
{0x70, 0x00},
{0x71, 0x80},
{0xf0, 0x36},
{0x37, 0x74},
{0xf0, 0x3f},
{0x03, 0xa1},
{0xf0, 0x36},//cvbs_off
{0x11, 0x80},
{0xf0, 0x01},
{0x79, 0xc1},
{0xf0, 0x37},
{0x24, 0x21},
{0xf0, 0x36},
{0x41, 0x00},
{0xea, 0x09},
{0xeb, 0x03},
{0xec, 0x19},
{0xed, 0x38},
{0xe9, 0x30},
{0xf0, 0x33},
{0x33, 0x00},
{0x34, 0x00},
{0xb1, 0x00},
{0xf0, 0x00},
{0xe0, 0x04},
{0xf0, 0x01},
{0x73, 0x00},
{0x74, 0xe0},
{0x70, 0x00},
{0x71, 0x80},
{0xf0, 0x36},
{0x32, 0x44},
{0xf0, 0x36},
{0x3e, 0xe0},
{0x70, 0x56},
{0x7c, 0x43},
{0x7d, 0x47},
{0x74, 0x00},
{0x75, 0x00},
{0x76, 0x00},
{0xa0, 0x47},
{0xa1, 0x5f},
{0x96, 0x22},
{0x97, 0x22},
{0x98, 0x22},
{0xf0, 0x00},
{0x72, 0x38},
{0x7a, 0x80},
{0x85, 0x18},
{0x9b, 0x35},
{0x9e, 0x20},
{0xd0, 0x66},
{0xd1, 0x34},
{0Xd3, 0x44},
{0xd6, 0x44},
{0xb0, 0x41},
{0xb2, 0x48},
{0xb3, 0xf4},
{0xb4, 0x0b},
{0xb5, 0x78},
{0xba, 0xff},
{0xbb, 0xc0},
{0xbc, 0x90},
{0xbd, 0x3a},
{0xc1, 0x67},
{0xf0, 0x01},
{0x20, 0x11},
{0x23, 0x90},
{0x24, 0x15},
{0x25, 0x87},
{0xbc, 0x9f},
{0xbd, 0x3a},
{0x48, 0xe6},
{0x49, 0xc0},
{0x4a, 0xd0},
{0x4b, 0x48},
// [cvbs_on]
{0xf0, 0x36},
{0x11, 0x00},
{0xf0, 0x01},
{0x79, 0xf1},
// [cvbs_off]
{0xf0, 0x36},
{0x11, 0x80},
{0xf0, 0x01},
{0x79, 0xc1},
};
/*
[Sensor]
SensorName=SC031IOT
width=640
height=480
port=1
type=2
pin=3
SlaveID=0xd0
mode=0
FlagReg=0xf7
FlagMask=0xff
FlagData=0xfa
FlagReg1=0xf8
FlagMask1=0xff
FlagData1=0x46
outformat=0
mclk=27
avdd=2.80000
dovdd=2.800000
dvdd=1.5
Ext0=0
Ext1=0
Ext2=0
AFVCC=0.0000
VPP=0.000000
*/
/* 27M MCLK, 30fps
static const uint8_t sc030iot_default_init_regs[][2] = {
{0xf0, 0x30},
{0x01, 0xff},
{0x02, 0xff},
{0x22, 0x07},
{0x19, 0xff},
{0x3f, 0x82},
{0x30, 0x02},
{0xf0, 0x01},
{0x70, 0x00},
{0x71, 0x80},
{0x72, 0x20},
{0x73, 0x00},
{0x74, 0xe0},
{0x75, 0x10},
{0x76, 0x81},
{0x77, 0x88},
{0x78, 0xe1},
{0x79, 0x01},
{0xf5, 0x01},
{0xf4, 0x0a},
{0xf0, 0x36},
{0x37, 0x79},
{0x31, 0x82},
{0x3e, 0x60},
{0x30, 0xf0},
{0x33, 0x33},
{0xf0, 0x32},
{0x48, 0x02},
{0xf0, 0x33},
{0x02, 0x12},
{0x7c, 0x02},
{0x7d, 0x0e},
{0xa2, 0x04},
{0x5e, 0x06},
{0x5f, 0x0a},
{0x0b, 0x58},
{0x06, 0x38},
{0xf0, 0x32},
{0x48, 0x02},
{0xf0, 0x39},
{0x02, 0x70},
{0xf0, 0x45},
{0x09, 0x1c},
{0xf0, 0x37},
{0x22, 0x0d},
{0xf0, 0x33},
{0x33, 0x10},
{0xb1, 0x80},
{0x34, 0x40},
{0x0b, 0x54},
{0xb2, 0x78},
{0xf0, 0x36},
{0x11, 0x80},
{0xf0, 0x30},
{0x38, 0x44},
{0xf0, 0x33},
{0xb3, 0x51},
{0x01, 0x10},
{0x0b, 0x6c},
{0x06, 0x24},
{0xf0, 0x36},
{0x31, 0x82},
{0x3e, 0x60},
{0x30, 0xf0},
{0x33, 0x33},
{0xf0, 0x34},
{0x9f, 0x02},
{0xa6, 0x40},
{0xa7, 0x47},
{0xe8, 0x5f},
{0xa8, 0x51},
{0xa9, 0x44},
{0xe9, 0x36},
{0xf0, 0x33},
{0xb3, 0x51},
{0x64, 0x17},
{0x90, 0x01},
{0x91, 0x03},
{0x92, 0x07},
{0x01, 0x10},
{0x93, 0x10},
{0x94, 0x10},
{0x95, 0x10},
{0x96, 0x01},
{0x97, 0x07},
{0x98, 0x1f},
{0x99, 0x10},
{0x9a, 0x20},
{0x9b, 0x28},
{0x9c, 0x28},
{0xf0, 0x36},
{0x70, 0x54},
{0xb6, 0x40},
{0xb7, 0x41},
{0xb8, 0x43},
{0xb9, 0x47},
{0xba, 0x4f},
{0xb0, 0x8b},
{0xb1, 0x8b},
{0xb2, 0x8b},
{0xb3, 0x9b},
{0xb4, 0xb8},
{0xb5, 0xf0},
{0x7e, 0x41},
{0x7f, 0x47},
{0x77, 0x80},
{0x78, 0x84},
{0x79, 0x8a},
{0xa0, 0x47},
{0xa1, 0x5f},
{0x96, 0x43},
{0x97, 0x44},
{0x98, 0x54},
{0xf0, 0x00},
{0xf0, 0x01},
{0x73, 0x00},
{0x74, 0xe0},
{0x70, 0x00},
{0x71, 0x80},
{0xf0, 0x36},
{0x37, 0x74},
{0xf0, 0x3f},
{0x03, 0x93},
{0xf0, 0x36},//cvbs_off
{0x11, 0x80},
{0xf0, 0x01},
{0x79, 0xc1},
{0xf0, 0x37},
{0x24, 0x21},
{0xf0, 0x36},
{0x41, 0x00},
{0xe9, 0x2c},
{0xf0, 0x33},
{0x33, 0x00},
{0x34, 0x00},
{0xb1, 0x00},
{0xf0, 0x00},
{0xe0, 0x04},
{0xf0, 0x01},
{0x73, 0x00},
{0x74, 0xe0},
{0x70, 0x00},
{0x71, 0x80},
{0xf0, 0x36},
{0x32, 0x44},
{0xf0, 0x36},
{0x3e, 0xe0},
{0x70, 0x56},
{0x7c, 0x43},
{0x7d, 0x47},
{0x74, 0x00},
{0x75, 0x00},
{0x76, 0x00},
{0xa0, 0x47},
{0xa1, 0x5f},
{0x96, 0x22},
{0x97, 0x22},
{0x98, 0x22},
{0xf0, 0x00},
{0x72, 0x38},
{0x7a, 0x80},
{0x85, 0x18},
{0x9b, 0x35},
{0x9e, 0x20},
{0xd0, 0x66},
{0xd1, 0x34},
{0Xd3, 0x44},
{0xd6, 0x44},
{0xb0, 0x41},
{0xb2, 0x48},
{0xb3, 0xf4},
{0xb4, 0x0b},
{0xb5, 0x78},
{0xba, 0xff},
{0xbb, 0xc0},
{0xbc, 0x90},
{0xbd, 0x3a},
{0xc1, 0x67},
{0xf0, 0x01},
{0x20, 0x11},
{0x23, 0x90},
{0x24, 0x15},
{0x25, 0x87},
{0xbc, 0x9f},
{0xbd, 0x3a},
{0x48, 0xe6},
{0x49, 0xc0},
{0x4a, 0xd0},
{0x4b, 0x48},
// [cvbs_on]
{0xf0, 0x36},
{0x11, 0x00},
{0xf0, 0x01},
{0x79, 0xf1},
// [cvbs_off]
{0xf0, 0x36},
{0x11, 0x80},
{0xf0, 0x01},
{0x79, 0xc1},
};
*/

View File

@@ -0,0 +1,31 @@
/*
*
* SC031GS DVP driver.
*
*/
#ifndef __SC031GS_H__
#define __SC030GS_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int sc031gs_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int sc031gs_init(sensor_t *sensor);
#endif // __SC031GS_H__

View File

@@ -0,0 +1,316 @@
// Copyright 2022-2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
//pin :BIT0 pwdn// BIT1:reset
//avdd 0:2.8V// 1:2.5V// 2:1.8V
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
/*
[database]
DBName=Dothinkey
[vendor]
VendorName=SmartSens
[sensor]
SensorName=SC031GS
width=200
height=200
port=1
type=1
pin=2
SlaveID=0x60
mode=3
FlagReg=0x36FF
FlagMask=0xff
FlagData=0x00
FlagReg1=0x36FF
FlagMask1=0xff
FlagData1=0x00
outformat=3
mclk=10
avdd=2.800000
dovdd=2.800000
dvdd=1.500000
Ext0=0
Ext1=0
Ext2=0
AFVCC=2.513000
VPP=0.000000
*/
#include <stdint.h>
#define SC031GS_OUTPUT_WINDOW_START_X_H_REG 0x3212
#define SC031GS_OUTPUT_WINDOW_START_X_L_REG 0x3213
#define SC031GS_OUTPUT_WINDOW_START_Y_H_REG 0x3210
#define SC031GS_OUTPUT_WINDOW_START_Y_L_REG 0x3211
#define SC031GS_OUTPUT_WINDOW_WIDTH_H_REG 0x3208
#define SC031GS_OUTPUT_WINDOW_WIDTH_L_REG 0x3209
#define SC031GS_OUTPUT_WINDOW_HIGH_H_REG 0x320a
#define SC031GS_OUTPUT_WINDOW_HIGH_L_REG 0x320b
#define SC031GS_LED_STROBE_ENABLE_REG 0x3361 // When the camera is in exposure, this PAD LEDSTROBE will be high to drive the external LED.
#define REG_NULL 0xFFFF
#define REG_DELAY 0X0000
struct sc031gs_regval {
uint16_t addr;
uint8_t val;
};
static const struct sc031gs_regval sc031gs_reset_regs[] = {
{0x0103, 0x01}, // soft reset.
{REG_DELAY, 10}, // delay.
};
// 200*200, xclk=10M, fps=120fps
static const struct sc031gs_regval sc031gs_200x200_init_regs[] = {
{0x0100, 0x00},
{0x36e9, 0x80},
{0x36f9, 0x80},
{0x300f, 0x0f},
{0x3018, 0x1f},
{0x3019, 0xff},
{0x301c, 0xb4},
{0x301f, 0x7b},
{0x3028, 0x82},
{0x3200, 0x00},
{0x3201, 0xdc},
{0x3202, 0x00},
{0x3203, 0x98},
{0x3204, 0x01},
{0x3205, 0xb3},
{0x3206, 0x01},
{0x3207, 0x67},
{SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, 0xc8},
{SC031GS_OUTPUT_WINDOW_HIGH_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_HIGH_L_REG, 0xc8},
{0x320c, 0x03},
{0x320d, 0x6b},
{0x320e, 0x01}, //default 120fps: {0x320e, 0x01},{0x320f, 0x40}, 58fps: {0x320e, 0x02},{0x320f, 0xab}; 30fps: {0x320e, 0x05}, {0x320f, 0x34}
{0x320f, 0x40},
{SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08},
{SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x04},
{0x3220, 0x10},
{0x3223, 0x50},
{0x3250, 0xf0},
{0x3251, 0x02},
{0x3252, 0x01},
{0x3253, 0x3b},
{0x3254, 0x02},
{0x3255, 0x07},
{0x3304, 0x48},
{0x3306, 0x38},
{0x3309, 0x50},
{0x330b, 0xe0},
{0x330c, 0x18},
{0x330f, 0x20},
{0x3310, 0x10},
{0x3314, 0x70},
{0x3315, 0x38},
{0x3316, 0x68},
{0x3317, 0x0d},
{0x3329, 0x5c},
{0x332d, 0x5c},
{0x332f, 0x60},
{0x3335, 0x64},
{0x3344, 0x64},
{0x335b, 0x80},
{0x335f, 0x80},
{0x3366, 0x06},
{0x3385, 0x41},
{0x3387, 0x49},
{0x3389, 0x01},
{0x33b1, 0x03},
{0x33b2, 0x06},
{0x3621, 0xa4},
{0x3622, 0x05},
{0x3624, 0x47},
{0x3631, 0x48},
{0x3633, 0x52},
{0x3635, 0x18},
{0x3636, 0x25},
{0x3637, 0x89},
{0x3638, 0x0f},
{0x3639, 0x08},
{0x363a, 0x00},
{0x363b, 0x48},
{0x363c, 0x06},
{0x363e, 0xf8},
{0x3640, 0x00},
{0x3641, 0x01},
{0x36ea, 0x39},
{0x36eb, 0x1e},
{0x36ec, 0x0e},
{0x36ed, 0x23},
{0x36fa, 0x39},
{0x36fb, 0x10},
{0x36fc, 0x01},
{0x36fd, 0x03},
{0x3908, 0x91},
{0x3d08, 0x01},
{0x3d04, 0x04},
{0x3e01, 0x13},
{0x3e02, 0xa0},
{0x3e06, 0x0c},
{0x3f04, 0x03},
{0x3f05, 0x4b},
{0x4500, 0x59},
{0x4501, 0xc4},
{0x4809, 0x01},
{0x4837, 0x39},
{0x5011, 0x00},
{0x36e9, 0x04},
{0x36f9, 0x04},
{0x0100, 0x01},
//delay 10ms
{REG_DELAY, 0X0a},
{0x4418, 0x08},
{0x4419, 0x80},
{0x363d, 0x10},
{0x3630, 0x48},
// [gain<4]
{0x3317, 0x0d},
{0x3314, 0x70},
// [gain>=4]
{0x3314, 0x68},
{0x3317, 0x0e},
{REG_NULL, 0x00},
};
// 640*480, xclk=20M, fps=50fps, xclk=10M, fps=25fps
static const struct sc031gs_regval sc031gs_640x480_50fps_init_regs[] = {
{0x0100, 0x00},
{0x36e9, 0x80},
{0x36f9, 0x80},
{0x300f, 0x0f},
{0x3018, 0x1f},
{0x3019, 0xff},
{0x301c, 0xb4},
{0x301f, 0x6c},
{0x3028, 0x82},
{0x3200, 0x00},
{0x3201, 0x00},
{0x3202, 0x00},
{0x3203, 0x08},
{0x3204, 0x02},
{0x3205, 0x8f},
{0x3206, 0x01},
{0x3207, 0xf7},
{SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, 0x02},
{SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, 0x80},
{SC031GS_OUTPUT_WINDOW_HIGH_H_REG, 0x01},
{SC031GS_OUTPUT_WINDOW_HIGH_L_REG, 0xe0},
{0x320c, 0x03},
{0x320d, 0x6e},
{0x320e, 0x04},
{0x320f, 0x72},
{SC031GS_OUTPUT_WINDOW_START_Y_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_START_Y_L_REG, 0x08},
{SC031GS_OUTPUT_WINDOW_START_X_H_REG, 0x00},
{SC031GS_OUTPUT_WINDOW_START_X_L_REG, 0x08},
{0x3220, 0x10},
{0x3223, 0x50},
{0x3250, 0xf0},
{0x3251, 0x02},
{0x3252, 0x03},
{0x3253, 0xb0},
{0x3254, 0x02},
{0x3255, 0x07},
{0x3304, 0x48},
{0x3306, 0x38},
{0x3309, 0x68},
{0x330b, 0xe0},
{0x330c, 0x18},
{0x330f, 0x20},
{0x3310, 0x10},
{0x3314, 0x6d},
{0x3315, 0x38},
{0x3316, 0x68},
{0x3317, 0x0f},
{0x3329, 0x5c},
{0x332d, 0x5c},
{0x332f, 0x60},
{0x3335, 0x64},
{0x3344, 0x64},
{0x335b, 0x80},
{0x335f, 0x80},
{0x3366, 0x06},
{0x3385, 0x31},
{0x3387, 0x51},
{0x3389, 0x01},
{0x33b1, 0x03},
{0x33b2, 0x06},
{0x3621, 0xa4},
{0x3622, 0x05},
{0x3624, 0x47},
{0x3631, 0x48},
{0x3633, 0x52},
{0x3635, 0x18},
{0x3636, 0x25},
{0x3637, 0x89},
{0x3638, 0x0f},
{0x3639, 0x08},
{0x363a, 0x00},
{0x363b, 0x48},
{0x363c, 0x06},
{0x363e, 0xf8},
{0x3640, 0x00},
{0x3641, 0x01},
{0x36ea, 0x36},
{0x36eb, 0x1a},
{0x36ec, 0x0a},
{0x36ed, 0x23},
{0x36fa, 0x36},
{0x36fb, 0x10},
{0x36fc, 0x01},
{0x36fd, 0x03},
{0x3908, 0x91},
{0x3d08, 0x01},
{0x3e01, 0x14},
{0x3e02, 0x80},
{0x3e06, 0x0c},
{0x3f04, 0x03},
{0x3f05, 0x4e},
{0x4500, 0x59},
{0x4501, 0xc4},
{0x4809, 0x01},
{0x4837, 0x1b},
{0x5011, 0x00},
{0x36e9, 0x20},
{0x36f9, 0x24},
{0x0100, 0x01}, // must write 0x0100 with 0x01, must delay no less then 7ms
//delay 10ms
{REG_DELAY, 0X0a},
{0x4418, 0x08},
{0x4419, 0x80},
{0x363d, 0x10},
{0x3630, 0x48},
{REG_NULL, 0x00},
};

View File

@@ -0,0 +1,31 @@
/*
*
* SC101IOT DVP driver.
*
*/
#ifndef __SC101IOT_H__
#define __SC101IOT_H__
#include "sensor.h"
/**
* @brief Detect sensor pid
*
* @param slv_addr SCCB address
* @param id Detection result
* @return
* 0: Can't detect this sensor
* Nonzero: This sensor has been detected
*/
int sc101iot_detect(int slv_addr, sensor_id_t *id);
/**
* @brief initialize sensor function pointers
*
* @param sensor pointer of sensor
* @return
* Always 0
*/
int sc101iot_init(sensor_t *sensor);
#endif // __SC101IOT_H__

View File

@@ -0,0 +1,257 @@
//Preview Type:0:DVP Raw 10 bit// 1:Raw 8 bit// 2:YUV422// 3:RAW16
//Preview Type:4:RGB565// 5:Pixart SPI// 6:MIPI 10bit// 7:MIPI 12bit// 8: MTK SPI
//port 0:MIPI// 1:Parallel// 2:MTK// 3:SPI// 4:TEST// 5: HISPI// 6 : Z2P/Z4P
//I2C Mode :0:Normal 8Addr,8Data// 1:Samsung 8 Addr,8Data// 2:Micron 8 Addr,16Data
//I2C Mode :3:Stmicro 16Addr,8Data//4:Micron2 16 Addr,16Data
//Out Format :0:YCbYCr/RG_GB// 1:YCrYCb/GR_BG// 2:CbYCrY/GB_RG// 3:CrYCbY/BG_GR
//MCLK Speed :0:6M//1:8M//2:10M//3:11.4M//4:12M//5:12.5M//6:13.5M//7:15M//8:18M//9:24M
//pin :BIT0 pwdn// BIT1:reset
//avdd 0:2.8V// 1:2.5V// 2:1.8V
//dovdd 0:2.8V// 1:2.5V// 2:1.8V
//dvdd 0:1.8V// 1:1.5V// 2:1.2V
/*
[DataBase]
DBName=DemoSens
[Vendor]
VendorName=SmartSens
I2C_CRC=0
[Sensor]
SensorName=SC101AP_raw
width=1280
height=720
port=1
type=2
pin=3
SlaveID=0xd0
mode=0
FlagReg=0xf7
FlagMask=0xff
FlagData=0xda
FlagReg1=0xf8
FlagMask1=0xff
FlagData1=0x4a
outformat=0
mclk=20
avdd=2.800000
dovdd=2.800000
dvdd=1.200000
Ext0=0
Ext1=0
Ext2=0
AFVCC=0.00
VPP=0.000000
*/
#include <stdint.h>
static const uint8_t sc101iot_default_init_regs[][2] = {
#if CONFIG_SC101IOT_720P_15FPS_ENABLED // 720P+YUV422+15FPS sensor default regs
/* Here are some test results:
# size xclk fps pic pclk
# ------- ------- ------ --------- ------- --- --- --- --- ---
# 720p 4 3 err
# 720p 8 5 normal 15
# 720p 10 7.8 normal 19
# 720p 20 15 warning 37.5
# VGA 8 6 normal
# VGA 20 16 normal
*/
{0xf0, 0x30},
{0x01, 0xff},
{0x02, 0xe0},
{0x30, 0x10},
{0x3f, 0x81},
{0xf0, 0x00},
{0x70, 0x6b},
{0x72, 0x30},
{0x84, 0xb4},
{0x8b, 0x00},
{0x8c, 0x20},
{0x8d, 0x02},
{0x8e, 0xec},
{0x9e, 0x10},
{0xb0, 0xc1},
{0xc8, 0x10},
{0xc9, 0x10},
{0xc6, 0x00},
{0xe0, 0x0f},
{0xb5, 0xf0},
{0xde, 0x80},
{0xb5, 0xf0},
{0xde, 0x80},
{0xb2, 0x50},
{0xb3, 0xfc},
{0xb4, 0x40},
{0xb5, 0xc0},
{0xb6, 0x50},
{0xb7, 0xfc},
{0xb8, 0x40},
{0xb9, 0xc0},
{0xba, 0xff},
{0xbb, 0xcc},
{0xbc, 0xa9},
{0xbd, 0x7d},
{0xc1, 0x77},
{0xf0, 0x01},
{0x70, 0x02},
{0x71, 0x02},
{0x72, 0x50},
{0x73, 0x02},
{0x74, 0xd2},
{0x75, 0x20},
{0x76, 0x81},
{0x77, 0x8c},
{0x78, 0x81},
{0xf4, 0x01},
{0xf5, 0x00},
{0xf6, 0x00},
{0xf0, 0x36},
{0x40, 0x03},
{0x41, 0x01},
{0xf0, 0x39},
{0x02, 0x70},
{0xf0, 0x32},
{0x41, 0x00},
{0x43, 0x01},
{0x48, 0x02},
{0xf0, 0x45},
{0x09, 0x20},
{0xf0, 0x33},
{0x33, 0x10},
{0xf0, 0x30},
{0x38, 0x44},
{0xf0, 0x39},
{0x07, 0x00},
{0x08, 0x19},
{0x47, 0x00},
{0x48, 0x00},
{0xf0, 0x37},
{0x24, 0x31},
{0xf0, 0x34},
{0x9f, 0x02},
{0xa6, 0x51},
{0xa7, 0x57},
{0xe8, 0x5f},
{0xa8, 0x50},
{0xa9, 0x50},
{0xe9, 0x50},
{0xf0, 0x33},
{0xb3, 0x58},
{0xb2, 0x78},
{0xf0, 0x34},
{0x9f, 0x03},
{0xa6, 0x51},
{0xa7, 0x57},
{0xaa, 0x01},
{0xab, 0x28},
{0xac, 0x01},
{0xad, 0x38},
{0xf0, 0x33},
{0x0a, 0x01},
{0x0b, 0x28},
{0xf0, 0x33},
{0x64, 0x0f},
{0xec, 0x51},
{0xed, 0x57},
{0x06, 0x58},
{0xe9, 0x58},
{0xeb, 0x68},
{0xf0, 0x33},
{0x64, 0x0f},
{0xf0, 0x36},
{0x70, 0xdf},
{0xb6, 0x40},
{0xb7, 0x51},
{0xb8, 0x53},
{0xb9, 0x57},
{0xba, 0x5f},
{0xb0, 0x84},
{0xb1, 0x82},
{0xb2, 0x84},
{0xb3, 0x88},
{0xb4, 0x90},
{0xb5, 0x90},
{0xf0, 0x36},
{0x7e, 0x50},
{0x7f, 0x51},
{0x77, 0x81},
{0x78, 0x86},
{0x79, 0x89},
{0xf0, 0x36},
{0x70, 0xdf},
{0x9c, 0x51},
{0x9d, 0x57},
{0x90, 0x54},
{0x91, 0x54},
{0x92, 0x56},
{0xf0, 0x36},
{0xa0, 0x51},
{0xa1, 0x57},
{0x96, 0x33},
{0x97, 0x43},
{0x98, 0x43},
{0xf0, 0x36},
{0x70, 0xdf},
{0x7c, 0x40},
{0x7d, 0x53},
{0x74, 0xd0},
{0x75, 0xf0},
{0x76, 0xf0},
{0xf0, 0x37},
{0x0f, 0xd5},
{0x7a, 0x40},
{0x7b, 0x57},
{0x71, 0x09},
{0x72, 0x09},
{0x73, 0x05},
{0xf0, 0x33},
{0x01, 0x44},
{0xf0, 0x36},
{0x37, 0xfb},
{0xf0, 0x36},
{0x3c, 0x0d},
{0xf0, 0x33},
{0x14, 0x95},
{0xf0, 0x33},
{0x8f, 0x80},
{0xf0, 0x37},
{0x27, 0x14},
{0x28, 0x03},
{0xf0, 0x36},
{0x37, 0xf4},
{0xf0, 0x33},
{0x01, 0x44},
{0xf0, 0x36},
{0x79, 0x89},
{0xf0, 0x34},
{0xac, 0x01},
{0xad, 0x40},
{0xf0, 0x33},
{0xeb, 0x70},
{0xf0, 0x34},
{0xa8, 0x50},
{0xa9, 0x50},
{0xf0, 0x33},
{0xb3, 0x58},
{0xf0, 0x36},
{0x11, 0x80},
{0xf0, 0x36},
{0x41, 0x51},
{0xf0, 0x3f},
{0x03, 0x09},
{0xf0, 0x32},
{0x0c, 0x06},
{0x0d, 0x82},
{0x0e, 0x02},
{0x0f, 0xee},
{0xf0, 0x36},
{0xea, 0x09},
{0xeb, 0xf5},
{0xec, 0x11},
{0xed, 0x27},
{0xe9, 0x20},
#endif
};

View File

@@ -0,0 +1,26 @@
#ifndef __LISA_CAMERA_SENSOR_H__
#define __LISA_CAMERA_SENSOR_H__
#include "../lisa_camera.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int (*init)(lisa_device_t *dev);
int (*deinit)(lisa_device_t *dev);
int (*read_id)(lisa_device_t *dev, uint16_t *id);
int (*set_pixformat)(lisa_device_t *dev, lisa_camera_pixel_format_t format);
int (*set_framesize)(lisa_device_t *dev, lisa_camera_framesize_t framesize);
int (*set_hmirror)(lisa_device_t *dev, bool enable);
int (*set_vflip)(lisa_device_t *dev, bool enable);
int (*set_exposure)(lisa_device_t *dev, int exposure);
int (*set_gain)(lisa_device_t *dev, int gain);
} lisa_camera_sensor_if_t;
#ifdef __cplusplus
}
#endif
#endif // __LISA_CAMERA_SENSOR_H__

View File

@@ -0,0 +1,226 @@
#include <stdbool.h>
#include <stdint.h>
#include "sensor.h"
#include "lisa_device.h"
#include "lisa_i2c.h"
#define TAG "camera_sensor"
#include "lisa_log.h"
static lisa_device_t *i2c_dev = NULL;
const camera_sensor_info_t camera_sensor[CAMERA_MODEL_MAX] = {
{CAMERA_OV7725, "OV7725", OV7725_SCCB_ADDR, OV7725_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_OV2640, "OV2640", OV2640_SCCB_ADDR, OV2640_PID, 1600, 1200, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_OV3660, "OV3660", OV3660_SCCB_ADDR, OV3660_PID, 2048, 1536, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_OV5640, "OV5640", OV5640_SCCB_ADDR, OV5640_PID, 2560, 1920, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_OV7670, "OV7670", OV7670_SCCB_ADDR, OV7670_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_NT99141, "NT99141", NT99141_SCCB_ADDR, NT99141_PID, 1280, 720, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_GC2145, "GC2145", GC2145_SCCB_ADDR, GC2145_PID, 1600, 1200, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_GC032A, "GC032A", GC032A_SCCB_ADDR, GC032A_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_GC0328, "GC0328", GC0328_SCCB_ADDR, GC0328_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_GC0310, "GC0310", GC0310_SCCB_ADDR, GC0310_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_GC0308, "GC0308", GC0308_SCCB_ADDR, GC0308_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_BF3005, "BF3005", BF3005_SCCB_ADDR, BF3005_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_BF20A6, "BF20A6", BF20A6_SCCB_ADDR, BF20A6_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_BF3901, "BF3901", BF3901_SCCB_ADDR, BF3901_PID, 320, 240, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_SC101IOT, "SC101IOT", SC101IOT_SCCB_ADDR, SC101IOT_PID, 1280, 720, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_SC030IOT, "SC030IOT", SC030IOT_SCCB_ADDR, SC030IOT_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_SC031GS, "SC031GS", SC031GS_SCCB_ADDR, SC031GS_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
{CAMERA_OV9655, "OV9655", OV9655_SCCB_ADDR, OV9655_PID, 640, 480, (PIXFORMAT_MASK_RGB565 | PIXFORMAT_MASK_YUV422)},
};
camera_sensor_info_t *camera_sensor_get_info(camera_model_t model)
{
for (int i = 0; i < CAMERA_MODEL_MAX; i++) {
if (camera_sensor[i].model == model) {
return (camera_sensor_info_t *)&camera_sensor[i];
}
}
return NULL;
}
int32_t sensor_twi_init(lisa_device_t *dev)
{
int ret = 0;
// 获取I2C设备
i2c_dev = dev;
if (!i2c_dev || !lisa_device_ready(i2c_dev)) {
LOGE("I2C device not ready");
return -1;
}
// 配置I2C总线
lisa_i2c_config_t i2c_cfg = {
.speed = LISA_I2C_SPEED_FAST,
.master_mode = true,
.slave_addr = 0,
};
ret = lisa_i2c_configure(i2c_dev, &i2c_cfg);
if (ret != LISA_DEVICE_OK) {
LOGE("lisa_i2c_configure failed %d", ret);
return ret;
}
return 0;
}
static int _twi_write_raw8(uint8_t slvaddr, uint8_t *values, int count, bool xfstop)
{
return lisa_i2c_write(i2c_dev, slvaddr, values, count);
}
static int _twi_write(uint8_t slv_addr, uint16_t reg, unsigned char value, bool reg16_width)
{
uint8_t buf[3];
uint32_t num = 0;
if (reg16_width) {
buf[0] = ((reg >> 8) & 0xff);
buf[1] = (reg & 0xff);
buf[2] = value;
num = 3;
}
else {
buf[0] = reg;
buf[1] = value;
num = 2;
}
return lisa_i2c_write(i2c_dev, slv_addr, buf, num);
}
static int _twi_read(uint8_t slv_addr, uint16_t reg, unsigned char *value, bool reg16_width)
{
lisa_i2c_msg_t msgs[2];
uint8_t reg_buf[2];
int reg_len = reg16_width ? 2 : 1;
// 准备寄存器地址
if (reg16_width) {
reg_buf[0] = (reg >> 8) & 0xff;
reg_buf[1] = reg & 0xff;
} else {
reg_buf[0] = reg & 0xff;
}
// 消息1写入寄存器地址不发送STOP
msgs[0].addr = slv_addr;
msgs[0].flags = LISA_I2C_FLAG_NO_STOP;
msgs[0].len = reg_len;
msgs[0].buf = reg_buf;
// 消息2读取数据
msgs[1].addr = slv_addr;
msgs[1].flags = LISA_I2C_FLAG_READ;
msgs[1].len = 1;
msgs[1].buf = value;
int ret = lisa_i2c_transfer(i2c_dev, msgs, 2);
if (ret != LISA_DEVICE_OK) {
LOGE("i2c transfer failed %d", ret);
}
return ret;
}
uint8_t sensor_twi_read_reg8(uint8_t slv_addr, uint8_t reg)
{
int ret = 0;
uint8_t value = 0;
int cnt = 0;
ret = _twi_read(slv_addr, reg, &value, false);
while (ret != 0 && cnt < 2) {
CLOGW("twi read retry %d\r\n", cnt);
ret = _twi_read(slv_addr, reg, &value, false);
// log....
cnt++;
}
// if (cnt > 0)
// log...
return value;
}
uint8_t sensor_twi_read_reg16(uint8_t slv_addr, uint16_t reg)
{
int ret = 0;
uint8_t value = 0;
int cnt = 0;
ret = _twi_read(slv_addr, reg, &value, true);
while (ret != 0 && cnt < 2) {
CLOGW("twi read retry %d\r\n", cnt);
ret = _twi_read(slv_addr, reg, &value, true);
// log....
cnt++;
}
// if (cnt > 0)
// log...
return value;
}
int sensor_twi_write_reg8(uint8_t slv_addr, uint8_t reg, uint8_t value)
{
int ret = 0;
int cnt = 0;
ret = _twi_write(slv_addr, reg, value, false);
while (ret != 0 && cnt < 2) {
ret = _twi_write(slv_addr, reg, value, false);
cnt++;
CLOGW("twi write retry %d %d\r\n", cnt, ret);
}
return ret;
}
int sensor_twi_write_reg16(uint8_t slv_addr, uint16_t reg, uint8_t value)
{
int ret = 0;
int cnt = 0;
ret = _twi_write(slv_addr, reg, value, true);
while (ret != 0 && cnt < 2) {
ret = _twi_write(slv_addr, reg, value, true);
cnt++;
CLOGW("twi write retry %d %d\r\n", cnt, ret);
}
return ret;
}
int sensor_twi_write_raw8(uint8_t slvaddr, uint8_t *values, int count)
{
return _twi_write_raw8(slvaddr, values, count, true);
}
// int sensor_twi_write_array(uint8_t slv_addr, struct regval_list *regs, int array_size)
// {
// int ret = 0;
// int i = 0;
// if (!regs)
// return -1;
// while (i < array_size) {
// if (regs->addr == REG_DLY) {
// // hal_msleep(regs->data);
// }
// else {
// ret = sensor_twi_write_reg8(slv_addr, regs->addr, regs->data);
// if (ret != 0) {
// // Log....
// CLOGE("spi_sensor_twi_write failed %d\r\n", ret);
// return ret;
// }
// }
// i++;
// regs++;
// }
// return ret;
// }

View File

@@ -0,0 +1,307 @@
#pragma once
#include "lisa_device.h"
#include "lisa_log.h"
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
CAMERA_OV2640,
CAMERA_OV3660,
CAMERA_OV5640,
CAMERA_OV7670,
CAMERA_OV7725,
CAMERA_OV9655,
CAMERA_NT99141,
CAMERA_GC0308,
CAMERA_GC0310,
CAMERA_GC0328,
CAMERA_GC032A,
CAMERA_GC2145,
CAMERA_BF20A6,
CAMERA_BF3005,
CAMERA_BF3901,
CAMERA_SC030IOT,
CAMERA_SC031GS,
CAMERA_SC101IOT,
CAMERA_MODEL_MAX,
CAMERA_NONE,
} camera_model_t;
typedef enum {
OV2640_SCCB_ADDR = 0x30,// 0x60 >> 1
OV3660_SCCB_ADDR = 0x3C,// 0x78 >> 1
OV5640_SCCB_ADDR = 0x3C,// 0x78 >> 1
OV7670_SCCB_ADDR = 0x21,// 0x42 >> 1
OV7725_SCCB_ADDR = 0x21,// 0x42 >> 1
OV9655_SCCB_ADDR = 0x30,// 0x60 >> 1
NT99141_SCCB_ADDR = 0x2A,// 0x54 >> 1
GC0308_SCCB_ADDR = 0x21,// 0x42 >> 1
GC0310_SCCB_ADDR = 0x21,// 0x42 >> 1
GC0328_SCCB_ADDR = 0x21,// 0x42 >> 1
GC032A_SCCB_ADDR = 0x21,// 0x42 >> 1
GC2145_SCCB_ADDR = 0x3C,// 0x78 >> 1
BF20A6_SCCB_ADDR = 0x6E,
BF3005_SCCB_ADDR = 0x6E,
BF3901_SCCB_ADDR = 0x6E,
SC030IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
SC031GS_SCCB_ADDR = 0x30,
SC101IOT_SCCB_ADDR = 0x68,// 0xd0 >> 1
} camera_sccb_addr_t;
typedef enum {
OV2640_PID = 0x26,
OV3660_PID = 0x3660,
OV5640_PID = 0x5640,
OV7670_PID = 0x76,
OV7725_PID = 0x77,
OV9650_PID = 0x96,//
OV9655_PID = 0x96,//
NT99141_PID = 0x1410,
GC0308_PID = 0x9B,
GC0310_PID = 0xA310,
GC0328_PID = 0x9D,
GC032A_PID = 0x232A,
GC2145_PID = 0x2145,
BF20A6_PID = 0x20A6,
BF3005_PID = 0x30,
BF3901_PID = 0x3901,
SC030IOT_PID = 0x9A46,
SC031GS_PID = 0x0031,
SC101IOT_PID = 0xdA4A,
} camera_pid_t;
typedef enum {
PIXFORMAT_RGB565, // 2BPP/RGB565
PIXFORMAT_YUV422, // 2BPP/YUV422
PIXFORMAT_YUV420, // 1.5BPP/YUV420
PIXFORMAT_GRAYSCALE, // 1BPP/GRAYSCALE
PIXFORMAT_JPEG, // JPEG/COMPRESSED
PIXFORMAT_RGB888, // 3BPP/RGB888
PIXFORMAT_RAW, // RAW
PIXFORMAT_RGB444, // 3BP2P/RGB444
PIXFORMAT_RGB555, // 3BP2P/RGB555
PIXFORMAT_INVALID,
} pixformat_t;
typedef enum {
FRAMESIZE_96X96, // 96x96
FRAMESIZE_128X180, // 128x180
FRAMESIZE_QQVGA, // 160x120
FRAMESIZE_QCIF, // 176x144
FRAMESIZE_HQVGA, // 240x176
FRAMESIZE_240X240, // 240x240
FRAMESIZE_QVGA, // 320x240
FRAMESIZE_CIF, // 400x296
FRAMESIZE_HVGA, // 480x320
FRAMESIZE_VGA, // 640x480
FRAMESIZE_SVGA, // 800x600
FRAMESIZE_XGA, // 1024x768
FRAMESIZE_HD, // 1280x720
FRAMESIZE_SXGA, // 1280x1024
FRAMESIZE_UXGA, // 1600x1200
// 3MP Sensors
FRAMESIZE_FHD, // 1920x1080
FRAMESIZE_P_HD, // 720x1280
FRAMESIZE_P_3MP, // 864x1536
FRAMESIZE_QXGA, // 2048x1536
// 5MP Sensors
FRAMESIZE_QHD, // 2560x1440
FRAMESIZE_WQXGA, // 2560x1600
FRAMESIZE_P_FHD, // 1080x1920
FRAMESIZE_QSXGA, // 2560x1920
FRAMESIZE_INVALID
} framesize_t;
typedef struct {
const camera_model_t model;
const char *name;
const camera_sccb_addr_t sccb_addr;
const camera_pid_t pid;
const uint16_t max_width;
const uint16_t max_height;
const uint16_t supported_formats;
} camera_sensor_info_t;
typedef enum {
ASPECT_RATIO_4X3,
ASPECT_RATIO_3X2,
ASPECT_RATIO_16X10,
ASPECT_RATIO_5X3,
ASPECT_RATIO_16X9,
ASPECT_RATIO_21X9,
ASPECT_RATIO_5X4,
ASPECT_RATIO_1X1,
ASPECT_RATIO_9X16
} aspect_ratio_t;
typedef enum {
GAINCEILING_2X,
GAINCEILING_4X,
GAINCEILING_8X,
GAINCEILING_16X,
GAINCEILING_32X,
GAINCEILING_64X,
GAINCEILING_128X,
} gainceiling_t;
typedef struct {
uint16_t max_width;
uint16_t max_height;
uint16_t start_x;
uint16_t start_y;
uint16_t end_x;
uint16_t end_y;
uint16_t offset_x;
uint16_t offset_y;
uint16_t total_x;
uint16_t total_y;
} ratio_settings_t;
typedef struct {
const uint16_t width;
const uint16_t height;
const aspect_ratio_t aspect_ratio;
} resolution_info_t;
// Resolution table (in sensor.c)
extern const resolution_info_t resolution[];
// camera sensor table (in sensor.c)
extern const camera_sensor_info_t camera_sensor[];
typedef struct {
uint8_t MIDH;
uint8_t MIDL;
uint16_t PID;
uint8_t VER;
} sensor_id_t;
typedef struct {
framesize_t framesize;//0 - 10
bool scale;
bool binning;
uint8_t quality;//0 - 63
int8_t brightness;//-2 - 2
int8_t contrast;//-2 - 2
int8_t saturation;//-2 - 2
int8_t sharpness;//-2 - 2
uint8_t denoise;
uint8_t special_effect;//0 - 6
uint8_t wb_mode;//0 - 4
uint8_t awb;
uint8_t awb_gain;
uint8_t aec;
uint8_t aec2;
int8_t ae_level;//-2 - 2
uint16_t aec_value;//0 - 1200
uint8_t agc;
uint8_t agc_gain;//0 - 30
uint8_t gainceiling;//0 - 6
uint8_t bpc;
uint8_t wpc;
uint8_t raw_gma;
uint8_t lenc;
uint8_t hmirror;
uint8_t vflip;
uint8_t dcw;
uint8_t colorbar;
} camera_status_t;
typedef struct sensor_s sensor_t;
typedef struct sensor_s {
sensor_id_t id; // Sensor ID.
uint8_t slv_addr; // Sensor I2C slave address.
pixformat_t pixformat;
camera_status_t status;
uint32_t xclk_freq_hz;
// Sensor function pointers
int (*init_status) (sensor_t *sensor);
int (*reset) (sensor_t *sensor); // Reset the configuration of the sensor, and return 0 if reset is successful
int (*start) (sensor_t *sensor); // Start sensor and return 0 if successful
int (*stop) (sensor_t *sensor); // Stop sensor and return 0 if successful
int (*set_pixformat) (sensor_t *sensor, pixformat_t pixformat);
pixformat_t (*get_pixformat) (sensor_t *sensor);
// int (*set_framesize) (sensor_t *sensor, framesize_t framesize);
int (*set_contrast) (sensor_t *sensor, int level);
int (*set_brightness) (sensor_t *sensor, int level);
int (*set_saturation) (sensor_t *sensor, int level);
int (*set_sharpness) (sensor_t *sensor, int level);
int (*set_denoise) (sensor_t *sensor, int level);
int (*set_gainceiling) (sensor_t *sensor, gainceiling_t gainceiling);
int (*set_quality) (sensor_t *sensor, int quality);
int (*set_colorbar) (sensor_t *sensor, int enable);
int (*set_whitebal) (sensor_t *sensor, int enable);
int (*set_gain_ctrl) (sensor_t *sensor, int enable);
int (*set_exposure_ctrl) (sensor_t *sensor, int enable);
int (*set_hmirror) (sensor_t *sensor, int enable);
int (*set_vflip) (sensor_t *sensor, int enable);
int (*set_window) (sensor_t *sensor, int16_t x, int16_t y, uint16_t w, uint16_t h);
int (*get_window) (sensor_t *sensor, uint16_t *w, uint16_t *h);
int (*set_aec2) (sensor_t *sensor, int enable);
int (*set_awb_gain) (sensor_t *sensor, int enable);
int (*set_agc_gain) (sensor_t *sensor, int gain);
int (*set_aec_value) (sensor_t *sensor, int gain);
int (*set_special_effect) (sensor_t *sensor, int effect);
int (*set_wb_mode) (sensor_t *sensor, int mode);
int (*set_ae_level) (sensor_t *sensor, int level);
int (*set_dcw) (sensor_t *sensor, int enable);
int (*set_bpc) (sensor_t *sensor, int enable);
int (*set_wpc) (sensor_t *sensor, int enable);
int (*set_raw_gma) (sensor_t *sensor, int enable);
int (*set_lenc) (sensor_t *sensor, int enable);
int (*get_reg) (sensor_t *sensor, int reg, int mask);
int (*set_reg) (sensor_t *sensor, int reg, int mask, int value);
int (*set_res_raw) (sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning);
int (*set_pll) (sensor_t *sensor, int bypass, int mul, int sys, int root, int pre, int seld5, int pclken, int pclk);
int (*set_xclk) (sensor_t *sensor, int timer, int xclk);
} sensor_t;
uint8_t sensor_twi_read_reg8(uint8_t slv_addr, uint8_t reg);
uint8_t sensor_twi_read_reg16(uint8_t slv_addr, uint16_t reg);
int sensor_twi_write_reg8(uint8_t slv_addr, uint8_t reg, uint8_t value);
int sensor_twi_write_reg16(uint8_t slv_addr, uint16_t reg, uint8_t value);
int sensor_twi_write_raw8(uint8_t slvaddr, uint8_t *values, int count);
#define PIXFORMAT_MASK_RGB565 (1 << PIXFORMAT_RGB565)
#define PIXFORMAT_MASK_YUV422 (1 << PIXFORMAT_YUV422)
#define PIXFORMAT_MASK_YUV420 (1 << PIXFORMAT_YUV420)
#define PIXFORMAT_MASK_GRAYSCALE (1 << PIXFORMAT_GRAYSCALE)
#define PIXFORMAT_MASK_JPEG (1 << PIXFORMAT_JPEG)
#define PIXFORMAT_MASK_RGB888 (1 << PIXFORMAT_RGB888)
#define PIXFORMAT_MASK_RAW (1 << PIXFORMAT_RAW)
#define PIXFORMAT_MASK_RGB444 (1 << PIXFORMAT_RGB444)
#define PIXFORMAT_MASK_RGB555 (1 << PIXFORMAT_RGB555)
#define CAMERA_READ_REG8(addr, reg) sensor_twi_read_reg8(addr, reg)
#define CAMERA_READ_REG16(addr, reg) sensor_twi_read_reg16(addr, reg)
#define CAMERA_WRITE_REG8(addr, reg, value) sensor_twi_write_reg8(addr, reg, value)
#define CAMERA_WRITE_REG16(addr, reg, value) sensor_twi_write_reg16(addr, reg, value)
#define CAMERA_WRITE_RAW8(addr, values, count) sensor_twi_write_raw8(addr, values, count)
#define CAMERA_DELAY_MS(ums) SysTick_Delay_Ms(ums)
#define CAMERA_LOG(fmt, ...) LOGI("CAMERA: "fmt"\r\n", ##__VA_ARGS__)
#define CAMERA_LOGD(fmt, ...) LOGD("CAMERA: "fmt"\r\n", ##__VA_ARGS__)
#define CAMERA_LOGI(fmt, ...) LOGI("CAMERA: "fmt"\r\n", ##__VA_ARGS__)
#define CAMERA_LOGE(fmt, ...) LOGE("CAMERA: "fmt"\r\n", ##__VA_ARGS__)
#define CAMERA_LOGW(fmt, ...) LOGW("CAMERA: "fmt"\r\n", ##__VA_ARGS__)
camera_sensor_info_t *camera_sensor_get_info(camera_model_t model);
int32_t sensor_twi_init(lisa_device_t *dev);
// int sensor_twi_write_array(uint8_t slv_addr, struct regval_list *reglist, int array_size);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,405 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "bf20a6.h"
#include "bf20a6_regs.h"
#include "bf20a6_settings.h"
#define TAG "bf20a6: "
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
// CAMERA_LOGI(TAG"READ Register 0x%02x VALUE: 0x%02x", reg, ret);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
#ifdef DEBUG_PRINT_REG
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static void print_regs(uint8_t slv_addr)
{
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x40; i <= 0x95; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
}
static int read_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
{
int i = 0, ret = 0;
while (regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = read_reg(slv_addr, regs[i][0]);
}
i++;
}
return ret;
}
#endif
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if (ret < 0) {
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint16_t(*regs)[2])
{
int i = 0, ret = 0;
while (!ret && regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int reset(sensor_t *sensor)
{
int ret;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x01);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, bf20a6_default_init_regs);
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
}
// int test_value = read_regs(sensor->slv_addr, bf20a6_default_init_regs);
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_YUV422:
set_reg_bits(sensor, 0x12, 0, 1, 0);
break;
case PIXFORMAT_RAW:
set_reg_bits(sensor, 0x12, 0, 1, 0x1);
break;
case PIXFORMAT_GRAYSCALE:
write_reg(sensor->slv_addr, 0x12, 0x23);
write_reg(sensor->slv_addr, 0x3a, 0x00);
write_reg(sensor->slv_addr, 0xe1, 0x92);
write_reg(sensor->slv_addr, 0xe3, 0x02);
break;
default:
CAMERA_LOGW(TAG"set_pix unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
if (framesize > FRAMESIZE_VGA) {
return -1;
}
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
sensor->status.framesize = framesize;
// Write MSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, 0);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, w >> 2);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, 0);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, h >> 2);
// Write LSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1b, 0);
if ((w <= 320) && (h <= 240)) {
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, (80 - w / 4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, (80 + w / 4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, (60 - h / 4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, (60 + h / 4));
} else if ((w <= 640) && (h <= 480)) {
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, (80 - w / 8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, (80 + w / 8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, (60 - h / 8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, (60 + h / 8));
}
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor, 0x4a, 3, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
//ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor, 0x4a, 2, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int value)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, 0xb6, value);
if (ret == 0) {
sensor->status.colorbar = value;
CAMERA_LOGD(TAG"Set colorbar to: %d", value);
}
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x70, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set sharpness to: %d", level);
sensor->status.sharpness = level;
}
return ret;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
// write_reg(sensor->slv_addr, 0xfe, 0x00);
sensor->status.brightness = CAMERA_READ_REG8(sensor->slv_addr, 0x6f);
sensor->status.contrast = CAMERA_READ_REG8(sensor->slv_addr, 0xd6);
sensor->status.saturation = 0;
sensor->status.sharpness = CAMERA_READ_REG8(sensor->slv_addr, 0x70);
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = CAMERA_READ_REG8(sensor->slv_addr, 0x13);
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
sensor->status.vflip = 0;// check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"dummy Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"gainceiling Unsupported");
return -1;
}
int bf20a6_detect(int slv_addr, sensor_id_t *id)
{
if (BF20A6_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG8(slv_addr, SENSOR_ID_LOW);
uint8_t MIDH = CAMERA_READ_REG8(slv_addr, SENSOR_ID_HIGH);
uint16_t PID = MIDH << 8 | MIDL;
if (BF20A6_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int bf20a6_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_sharpness;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror; // set_hmirror;
sensor->set_vflip = set_vflip; // set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"BF20A6 Attached");
return 0;
}

View File

@@ -0,0 +1,535 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* BF3005 driver.
*
* Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "bf3005.h"
#include "bf3005_regs.h"
#define TAG "bf3005: "
static const uint8_t default_regs[][2] = {
{0x12, 0x40}, //soft reset
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0xff, 0xff}, //delay
{0x13, 0x10},
{0x8c, 0x00},
{0x8d, 0x64},
{0x87, 0x10},
{0x13, 0x17},
{0x00, 0x20},
{0x01, 0x1a},
{0x02, 0x22},
{0x09, 0x03},
{0x0c, 0x80},
{0x0d, 0x24},
{0x0e, 0x21},
{0x0f, 0x28},
{0x11, 0x08},
{0x15, 0x10}, // 0X10
{0x16, 0x03},
{0x1e, 0x30},
{0x20, 0x8a},
{0x21, 0x03},
{0x23, 0x55},
{0x24, 0x68},
{0x25, 0x78},
{0x2a, 0x00},
{0x2b, 0x00},
{0x2d, 0x4f},
{0x2e, 0x98},
{0x2f, 0x04},
{0x30, 0xad},
{0x31, 0x17},
{0x32, 0x6e},
{0x33, 0x20},
{0x35, 0xa6},
{0x3b, 0x00},
{0x3e, 0x00},
{0x3f, 0xA8},
{0x40, 0x38},
{0x41, 0x32},
{0x42, 0x2b},
{0x43, 0x26},
{0x44, 0x1a},
{0x45, 0x16},
{0x46, 0x10},
{0x47, 0x0f},
{0x48, 0x0c},
{0x49, 0x0a},
{0x4b, 0x09},
{0x4c, 0x08},
{0x4d, 0x3c},
{0x4e, 0x06},
{0x4f, 0x05},
{0x50, 0x03},
{0x51, 0x25},
{0x52, 0x88},
{0x53, 0x03},
{0x63, 0x20},
{0x64, 0x02},
{0x65, 0xa6},
{0x66, 0xb6},
{0x69, 0x00},
{0x70, 0xFF},
{0x71, 0xa6},
{0x72, 0x2f},
{0x73, 0x2f},
{0x74, 0x2F},
{0x75, 0x0e},
{0x76, 0x1e},
{0x77, 0x00},
{0x78, 0x1e},
{0x79, 0x8a},
{0x7d, 0xe2},
{0x80, 0x44},
{0x81, 0x00},
{0x82, 0x18},
{0x83, 0x1b},
{0x84, 0x24},
{0x85, 0x2a},
{0x86, 0x4f},
{0x89, 0x82}, //0x82
{0x8b, 0x02},
{0x8e, 0x03},
{0x8f, 0xFC},
{0x9d, 0x4d},
{0x9e, 0x41},
{0xa1, 0x21},
{0xa2, 0x12},
{0xa3, 0x32},
{0xa4, 0x05},
{0xa5, 0x32},
{0xa6, 0x04},
{0xa7, 0x7f},
{0xa8, 0x7f},
{0xa9, 0x21},
{0xaa, 0x21},
{0xab, 0x21},
{0xac, 0x0a},
{0xad, 0xf0},
{0xae, 0xff},
{0xaf, 0x1d},
{0xb0, 0x94},
{0xb1, 0xc0},
{0xb2, 0xc0},
{0xd2, 0x30},
{0xe0, 0x0d},
{0xe1, 0x44},
{0xe7, 0x7c},
{0xe8, 0x89},
{0xe9, 0x01},
{0xea, 0x01},
{0xf0, 0x01},
{0xf3, 0x49},
{0xf4, 0xff},
{0xf5, 0x01},
{0xf6, 0xf2},
{0xf7, 0x6f},
{0x1b, 0x80},
{0x00, 0x00},
};
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
return (ret & mask) >> offset;
}
static int reset(sensor_t *sensor)
{
int i=0;
const uint8_t (*regs)[2];
// Write default regsiters
for (i=0, regs = default_regs; regs[i][0]; i++) {
CAMERA_WRITE_REG8(sensor->slv_addr, regs[i][0], regs[i][1]);
}
// Delay
CAMERA_DELAY_MS(50);
return 0;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
set_reg_bits(sensor, 0x12, 2, 1, 1);
break;
case PIXFORMAT_RAW:
set_reg_bits(sensor, 0x12, 0, 3, 0x4);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
set_reg_bits(sensor, 0x12, 2, 1, 0);
break;
default:
return -1;
}
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret=0;
if (framesize > FRAMESIZE_VGA) {
return -1;
}
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
// uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM7);
sensor->status.framesize = framesize;
// Write MSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, 0);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, w>>2);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, 0);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, h>>2);
// Write LSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x03, 0);
printf("%s %d\r\n", __func__, __LINE__);
if((w<=320)&&(h<=240))
{
printf("%s %d\r\n", __func__, __LINE__);
// Enable auto-scaling/zooming factors
//ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x12, 0x50);
set_reg_bits(sensor, 0x12, 4, 1, 1);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, (80-w/4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, (80+w/4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, (60-h/4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, (60+h/4));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x03, 0);
} else if((w<=640)&&(h<=480))
{
// Enable auto-scaling/zooming factors
//ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x12, 0x40);
set_reg_bits(sensor, 0x12, 4, 1, 0);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, (80-w/8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, (80+w/8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, (60-h/8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, (60+h/8));
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x03, 0);
}
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_colorbar(sensor_t *sensor, int value)
{
int ret=0;
sensor->status.colorbar = value;
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xb9, value);
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
sensor->status.awb = !!enable;
}
return sensor->status.awb;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
sensor->status.agc = !!enable;
}
return sensor->status.agc;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
sensor->status.aec = !!enable;
}
return sensor->status.aec;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
sensor->status.hmirror = !!enable;
}
return sensor->status.hmirror;
}
static int set_vflip(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
sensor->status.vflip = !!enable;
}
return sensor->status.vflip;
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set raw_gma to: %d", !enable);
sensor->status.raw_gma = !enable;
}
return ret;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set lenc to: %d", !enable);
sensor->status.lenc = !enable;
}
return ret;
}
static int set_agc_gain(sensor_t *sensor, int option)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
if (ret == 0) {
CAMERA_LOGD(TAG"Set gain to: %d", !!option);
sensor->status.agc_gain = !!option;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int value)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0xa6, value);
if (ret == 0) {
CAMERA_LOGD(TAG"Set awb gain threthold to: %d", value);
sensor->status.awb_gain = value;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x55, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x56, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set contrast to: %d", level);
sensor->status.contrast = level;
}
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x70, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set sharpness to: %d", level);
sensor->status.sharpness = level;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = CAMERA_READ_REG8(sensor->slv_addr, 0x55);
sensor->status.contrast = CAMERA_READ_REG8(sensor->slv_addr, 0x56);
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = CAMERA_READ_REG8(sensor->slv_addr, 0x87);
sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
sensor->status.awb_gain = CAMERA_READ_REG8(sensor->slv_addr, 0xa6);
sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
sensor->status.colorbar = CAMERA_READ_REG8(sensor->slv_addr, 0xb9);
sensor->status.sharpness = CAMERA_READ_REG8(sensor->slv_addr, 0x70);
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int bf3005_detect(int slv_addr, sensor_id_t *id)
{
if (BF3005_SCCB_ADDR == slv_addr) {
uint16_t PID = CAMERA_READ_REG8(slv_addr, 0xFC);
if (BF3005_PID == PID) {
id->PID = PID;
id->VER = CAMERA_READ_REG8(slv_addr, 0xFD);
id->MIDL = CAMERA_READ_REG8(slv_addr, 0xFC);
id->MIDH = CAMERA_READ_REG8(slv_addr, 0xFD);
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int bf3005_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_brightness = set_brightness;
sensor->set_contrast = set_contrast;
sensor->set_colorbar = set_colorbar;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_whitebal = set_whitebal;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
sensor->set_sharpness = set_sharpness;
//not supported
sensor->set_saturation= set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
CAMERA_LOGD(TAG"BF3005 Attached");
return 0;
}

View File

@@ -0,0 +1,745 @@
#include <stdint.h>
#include <stdio.h>
#include "sensor.h"
#define TAG "bf3901"
#include "lisa_log.h"
#define BF3901_CHIP_ID 0x3901
#define BF3A03_IIC_CLK_FREQ 10000
#define CAMERA_X_MAX (248 - 1)
#define CAMERA_Y_MAX 328
#define BACK_X_LEN (CAM_FRAME_WIDTH)
#define BACK_Y_LEN (CAM_FRAME_HEIGHT + 4)
#define CAMERA_LS001_NO_FRAME_FORMAT 1
#define TWI_MAX_COMBINE_REGS 99
struct regval_list {
uint8_t reg_num;
uint8_t value;
};
const struct regval_list sensor_default_regs[] = {
{0x15,0x10}, //BSD: Bit[1]: VSYNC = Active low
{0x62,0x81}, // bit[1:0] 01:spi mode 10:2bit mode 11:4bit mode
{0x11,0xb0},
{0x1b,0x80},
{0x6b,0x01}, //BSD: no frame HEAD, no frame END, no line HEAD bit6:CCIR656
{0x08,0xa0},
// {0xb9,0x80}, // test pattern
{0x12,0x01}, //BSD: use LSB, bit[5]: 0=MSB, 1=LSB bit4: 0=MTK 1=zhan xun
{0x0c,0x40},
{0x06,0x68},
{0x27,0x97},
{0x2b,0x20},
{0x13,0x00},
{0x01,0x0d},
{0x02,0x0d},
// {0x87,0x7f},
// {0x8d,0xff},
{0x20,0x09},
{0x09,0x03}, //BSD: Standby mode, bit[4]: 0=Disable, 1=Enable
{0x33,0x10},
{0x34,0x1d},
{0x35,0x46},
{0x36,0x40},
{0x37,0xa4},
{0x38,0x7c},
{0x65,0x46},
{0x66,0x46},
{0x6e,0x20},
{0x9b,0xa4},
{0x9c,0x7c},
{0xbc,0x0c},
{0xbd,0xa4},
{0xbe,0x7c},
{0x70,0x0f},
{0x71,0x46},
{0x72,0x2f},
{0x73,0x2f},
{0x74,0xa7},
{0x75,0x12},
{0x76,0x90},
{0x77,0xdd},
{0x78,0x4e},
{0x79,0x85},
{0x7a,0x00},
{0x7b,0x55},
{0x7e,0xfa},
{0x7c,0x88},
{0x7d,0xba},
{0x60,0xe7},
{0x61,0xc8},
{0x6d,0x70},
{0x8a,0x11},
{0x8b,0x21},
{0x8e,0x24},
{0x8f,0x31},
{0x94,0x38},
{0x95,0x6e},
{0x96,0x7f},
{0x97,0xf3},
{0x13,0x00},
{0x24,0x50},
{0x97,0x48},
{0x25,0x88},
{0x94,0x42},
{0x95,0xb0},
{0x80,0xd6},
{0x81,0xff},
{0x82,0x18},
{0x83,0x30},
{0x84,0x30},
{0x85,0x40},
{0x86,0x77},
{0x89,0x2d},
{0x8a,0x5e},
{0x8b,0x4c},
{0x98,0x1a},
// {0x39,0x98},
// {0x3f,0x98},
{0x90,0xa0},
{0x91,0xe0},
// {0x40,0x3b},
// {0x41,0x36},
// {0x42,0x2b},
// {0x43,0x1d},
// {0x44,0x1a},
// {0x45,0x14},
// {0x46,0x11},
// {0x47,0x0e},
// {0x48,0x0d},
// {0x49,0x0c},
// {0x4b,0x0b},
// {0x4c,0x09},
// {0x4e,0x09},
// {0x4f,0x08},
// {0x50,0x07},
// { 0x39, 0xc0 },
// { 0x3f, 0x40 },
{0x5a,0x56},
{0x51,0x12},
{0x52,0x0d},
{0x53,0x92},
{0x54,0x7d},
{0x57,0x97},
{0x58,0x43},
{0x5a,0xd6},
{0x51,0x1f},
{0x52,0x0f},
{0x53,0x47},
{0x54,0x20},
{0x57,0x2f},
{0x58,0x24},
{0x5b,0xc2},
{0x5c,0x28},
{0xb0,0xe0},
{0xb3,0x4f},
{0xb4,0xe3},
{0xb1,0xf0},
{0xb2,0xa0},
{0xb4,0x63},
{0xb1,0xd0},
{0xb2,0xc0},
{0x55,0x00},
{0x56,0x60},
{0xa0,0xd0},
{0xa1,0x31},
{0xa6,0x04},
{0xa2,0x0f},
{0xa3,0x2b},
{0xa4,0x0f},
{0xa5,0x2b},
{0xa7,0x9a},
{0xa8,0x1c},
{0xd0,0xb4},
{0xd1,0x00},
{0xd2,0x78},
{0xa9,0x11},
{0xaa,0x16},
{0xab,0x16},
{0xac,0x3c},
{0xad,0xf0},
{0xae,0x57},
{0xc6,0xaa},
{0xc8,0x0d},
{0xc9,0x10},
{0xd3,0x09},
{0xd4,0x24},
{0x6a,0x81},
{0x23,0x33},
{0x69,0x00},
{0x1e,0x39},
{0xee,0x4c},
{0xf1,0x00},
{0x4a,0x0e},
{0xda,0x00},
{0xdb,0xf8}, // 248
{0xdc,0x00},
{0xdd,0x48},
{0xde,0x10}, // 328
{0x17,0x00},
{0x18,0x78}, // 240
{0x19,0x00},
{0x1a,0xa0}, // 320
{0x2b,0x00},
{0x2F,0x04},
{0x16,0xA7},
{0xbb,0x23},
{ 0x13, 0x00 },
{ 0x24, 0xd8 },
{ 0x01, 0x0d },
{ 0x02, 0x0d },
// { 0x87, 0x12 },
{ 0x8c, 0x00 },
{ 0x8d, 0x20 },
{ 0x3e, 0x06 },
{ 0x89, 0x0d },
{ 0x86, 0x40 },
{ 0x8a, 0x18 },
{ 0x01, 0x0d },
{ 0x02, 0x0d },
{ 0x01, 0x0d },
{ 0x02, 0x0d },
{0x0b,0x03},
////////前端////////////////
{0x4a, 0x0e},
{0xda, 0x00},
{0xdb, 96},
{0xdc, 72},
{0xdd, 0x38},
{0xde, 0x10},
///////////////后端/////////////
{0x17, 0x00},
{0x18, 96 >> 1},
{0x19, 0x00},
{0x1a, 240 >> 1},
/* 0x92[L]/0x93[H] 0xe3[L]/0xe4[H]通过插入dummy line改变帧率 */
// {0x92, 0x8},
// {0xe3, 0x8},
// {0x0b,0x01},
// {0xb9,0x00},
//gamma
{0x39,0x98},
{0x3f,0x98},
{0X40,0x18},
{0X41,0x25},
{0X42,0x22},
{0X43,0x1f},
{0X44,0x1c},
{0X45,0x1a},
{0X46,0x17},
{0X47,0x15},
{0X48,0x11},
{0X49,0x0e},
{0X4b,0x0b},
{0X4c,0x0a},
{0X4e,0x09},
{0X4f,0x08},
{0X50,0x06},
//praw ISP:Denoise LSC gamma
{0x12,0x05},
{0x0c,0xc0},
//denoise
// {0x70,0x80},
// {0x72,0x0f},
// {0x73,0x0f},
{0x87,0x0f},
{0x35,0x66},
{0x65,0x66},
{0x66,0x66},
{0x28,0x00},
{0xd5,0x00},
{0xd6,0x00},
{0xd7,0x00},
{0xd8,0x02},
{0x61,0xc8},
{0x0d,0x1d},
{0x00,0x1c},
{0x61,0x88},
{0x0d,0x2a},
{0x00,0x2a},
{0xff, 0xff},
};
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, int len)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if (ret < 0) {
return ret;
}
uint8_t mask = ((1 << len) - 1) << offset;
return (ret & mask) >> offset;
}
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int bf3901_write_array(sensor_t *sensor, const struct regval_list *vals)
{
int ret = 0;
#if 0
while ( ((vals->reg_num != 0xff) || (vals->value != 0xff)) && (0 == ret) ) {
ret = CAMERA_WRITE_REG8(sensor->slv_addr, vals->reg_num, vals->value);
vals++;
}
#else
uint8_t regbuf[1 + TWI_MAX_COMBINE_REGS] = { 0 }; // first one for register, left for values
uint8_t regcnt = 0;
while (((vals->reg_num != 0xff) || (vals->value != 0xff)) && (0 == ret)) {
if (0 < regcnt && regcnt < TWI_MAX_COMBINE_REGS && regbuf[0] + regcnt == vals->reg_num)
regbuf[++regcnt] = vals->value;
else {
if (regcnt > 0) ret = CAMERA_WRITE_RAW8(sensor->slv_addr, regbuf, 1 + regcnt);
regbuf[0] = vals->reg_num;
regbuf[1] = vals->value;
regcnt = 1;
}
vals++;
}
if (regcnt > 0)
ret = CAMERA_WRITE_RAW8(sensor->slv_addr, regbuf, 1 + regcnt);
#endif
return ret;
}
static pixformat_t get_pixformat(sensor_t *sensor)
{
int reg_val = CAMERA_READ_REG8(sensor->slv_addr, 0x12);
if (reg_val < 0) {
return PIXFORMAT_INVALID;
}
uint8_t format_bits = ((reg_val >> 2) & 0x01) << 1 | (reg_val & 0x01);
switch (format_bits) {
case 0b00: // YUV422
return PIXFORMAT_YUV422;
case 0b01: // RAW
case 0b11: // Processed RAW
return PIXFORMAT_GRAYSCALE;
case 0b10: // RGB565
return PIXFORMAT_RGB565;
default:
return PIXFORMAT_INVALID;
}
}
static int get_window(sensor_t *sensor, uint16_t *w, uint16_t *h)
{
*w = CAMERA_READ_REG8(sensor->slv_addr, 0x18) << 1;
*h = CAMERA_READ_REG8(sensor->slv_addr, 0x1a) << 1;
return 0;
}
static int reset(sensor_t *sensor)
{
bf3901_write_array(sensor, sensor_default_regs);
CAMERA_DELAY_MS(5);
return 0;
}
static int start(sensor_t *sensor)
{
set_reg_bits(sensor, 0x09, 4, 1, 0x00);
return 0;
}
static int stop(sensor_t *sensor)
{
set_reg_bits(sensor, 0x09, 4, 1, 0x01);
return 0;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
uint8_t reg_val = CAMERA_READ_REG8(sensor->slv_addr, 0x12);
// mask for bit0 and bit2
uint8_t mask = (1 << 2) | (1 << 0);
uint8_t value = 0;
switch (pixformat) {
case PIXFORMAT_YUV422:
value = 0x00; // bit2=0, bit0=0
break;
case PIXFORMAT_GRAYSCALE:
value = 0x01; // bit2=0, bit0=1
break;
case PIXFORMAT_RGB565:
value = 0x04; // bit2=1, bit0=0
break;
case PIXFORMAT_RAW:
value = 0x05; // bit2=1, bit0=1
break;
default:
return -1;
}
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x12, (reg_val & ~mask) | value);
if (ret == 0) {
sensor->pixformat = pixformat;
}
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_window(sensor_t *sensor, int16_t x, int16_t y, uint16_t w, uint16_t h)
{
uint8_t reg_da = 0; /* x_win_start */
uint8_t reg_db = 0; /* x_win_end */
uint8_t reg_dc = 0; /* y_win_start, low 8 bit */
uint8_t reg_dd = 0; /* y_win_end, low 8 bit */
uint8_t reg_de = 0; /* bit[0]: y start high 1 bit, bit[5]:y end high 1 bit, */
int ret=0;
int16_t x_end = 0;
int16_t y_end = 0;
x = x < 0 ? 0 : x;
x_end = x + w;
x_end = x_end > CAMERA_X_MAX ? CAMERA_X_MAX : x_end;
x = x_end - w;
y = y < 0 ? 0 : y;
y_end = y + h;
y_end = y_end > CAMERA_Y_MAX ? CAMERA_Y_MAX : y_end;
y = y_end - h;
reg_da = x;
reg_db = x_end;
reg_dc = y & 0xff;
reg_dd = y_end & 0xff;
/**
* Note that the high bit of y_end
* is register 0xde bit4 instead of bit5.
* There may be an error in the data sheet.
*/
reg_de = ((y_end > (int16_t)255) << 4) | ((y) > (int16_t)255);
LOGI("x:%d, y:%d, w:%d, h:%d\r\n", x, y, w, h);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xda, reg_da);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xdb, reg_db);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xdc, reg_dc);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xdd, reg_dd);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xde, reg_de);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x17, 0x00);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x18, w>>1);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x19, 0x00);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0x1a, h>>1);
return ret;
}
static int set_colorbar(sensor_t *sensor, int value)
{
int ret=0;
sensor->status.colorbar = value;
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, 0xb9, value);
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 1, 1, enable) >= 0){
sensor->status.awb = !!enable;
}
return sensor->status.awb;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 2, 1, enable) >= 0){
sensor->status.agc = !!enable;
}
return sensor->status.agc;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x13, 0, 1, enable) >= 0){
sensor->status.aec = !!enable;
}
return sensor->status.aec;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 5, 1, enable) >= 0){
sensor->status.hmirror = !!enable;
}
return sensor->status.hmirror;
}
static int set_vflip(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, 0x1e, 4, 1, enable) >= 0){
sensor->status.vflip = !!enable;
}
return sensor->status.vflip;
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 1, 1, !enable);
if (ret == 0) {
CLOGD("Set raw_gma to: %d", !enable);
sensor->status.raw_gma = !enable;
}
return ret;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0xf1, 0, 1, !enable);
if (ret == 0) {
CLOGD("Set lenc to: %d", !enable);
sensor->status.lenc = !enable;
}
return ret;
}
static int set_agc_gain(sensor_t *sensor, int option)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x13, 4, 1, !!option);
if (ret == 0) {
CLOGD("Set gain to: %d", !!option);
sensor->status.agc_gain = !!option;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int value)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0xa6, value);
if (ret == 0) {
CLOGD("Set awb gain threthold to: %d", value);
sensor->status.awb_gain = value;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x55, level);
if (ret == 0) {
CLOGD("Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x56, level);
if (ret == 0) {
CLOGD("Set contrast to: %d", level);
sensor->status.contrast = level;
}
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x70, level);
if (ret == 0) {
CLOGD("Set sharpness to: %d", level);
sensor->status.sharpness = level;
}
return ret;
}
static int set_gainceiling(sensor_t *sensor, gainceiling_t val)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x87, val);
if (ret == 0) {
CLOGD("Set gain ceiling to: %d", val);
sensor->status.gainceiling = val;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = CAMERA_READ_REG8(sensor->slv_addr, 0x55);
sensor->status.contrast = CAMERA_READ_REG8(sensor->slv_addr, 0x56);
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = CAMERA_READ_REG8(sensor->slv_addr, 0x87);
sensor->status.awb = get_reg_bits(sensor, 0x13, 1, 1);
sensor->status.awb_gain = CAMERA_READ_REG8(sensor->slv_addr, 0xa6);
sensor->status.aec = get_reg_bits(sensor, 0x13, 0, 1);
sensor->status.agc = get_reg_bits(sensor, 0x13, 2, 1);
sensor->status.raw_gma = get_reg_bits(sensor, 0xf1, 1, 1);
sensor->status.lenc = get_reg_bits(sensor, 0xf1, 0, 1);
sensor->status.hmirror = get_reg_bits(sensor, 0x1e, 5, 1);
sensor->status.vflip = get_reg_bits(sensor, 0x1e, 4, 1);
sensor->status.colorbar = CAMERA_READ_REG8(sensor->slv_addr, 0xb9);
sensor->status.sharpness = CAMERA_READ_REG8(sensor->slv_addr, 0x70);
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int bf3901_detect(int slv_addr, sensor_id_t *id)
{
if (BF3901_SCCB_ADDR == slv_addr) {
uint16_t chip_id = 0;
unsigned char val = 0;
val = CAMERA_READ_REG8(slv_addr, 0xFC);
chip_id |= (val << 8);
val = CAMERA_READ_REG8(slv_addr, 0xfd);
chip_id |= (val);
if (BF3901_PID == chip_id) {
id->PID = chip_id;
return chip_id;
}
CLOGI("Mismatch PID=0x%x", chip_id);
}
return 0;
}
int bf3901_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->start = start;
sensor->stop = stop;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_brightness = set_brightness;
sensor->set_contrast = set_contrast;
sensor->set_colorbar = set_colorbar;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_window = set_window;
sensor->get_window = get_window;
sensor->set_whitebal = set_whitebal;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
sensor->set_sharpness = set_sharpness;
sensor->get_pixformat = get_pixformat;
//not supported
sensor->set_saturation= set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_gainceiling = set_gainceiling;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
CLOGD("BF3901 Attached\r\n");
return 0;
}

View File

@@ -0,0 +1,467 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sensor.h"
#include "gc0308.h"
#include "gc0308_regs.h"
#include "gc0308_settings.h"
#define TAG "gc0308: "
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
{
int i = 0, ret = 0;
while (!ret && (i < regs_size)) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0xa2; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(80);
ret = write_regs(sensor->slv_addr, gc0308_sensor_default_regs, sizeof(gc0308_sensor_default_regs)/(sizeof(uint8_t) * 2));
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(80);
write_reg(sensor->slv_addr, 0xfe, 0x00);
//set_reg_bits(sensor->slv_addr, 0x28, 4, 0x07, 1); //ensure pclk <= 15MHz
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x24, 0, 0x0f, 2); //yuv422 Y Cb Y Cr
break;
case PIXFORMAT_GRAYSCALE:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = write_reg(sensor->slv_addr, 0x24, 0xb1);
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
if (framesize > FRAMESIZE_VGA) {
CAMERA_LOGW(TAG"Invalid framesize: %u", framesize);
framesize = FRAMESIZE_VGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
(void)row_s;
(void)col_s;
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
struct subsample_cfg {
uint16_t ratio_numerator;
uint16_t ratio_denominator;
uint8_t reg0x54;
uint8_t reg0x56;
uint8_t reg0x57;
uint8_t reg0x58;
uint8_t reg0x59;
};
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
{84, 420, 0x55, 0x00, 0x00, 0x00, 0x00}, //1/5
{105, 420, 0x44, 0x00, 0x00, 0x00, 0x00},//1/4
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00},//1/3
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00},//1/2
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46},//4/7
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04},//3/5
{280, 420, 0x33, 0x02, 0x00, 0x02, 0x00},//2/3
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00},//1/1
};
uint16_t win_w = 640;
uint16_t win_h = 480;
/* 默认使用最后一个配置 (1:1) */
const struct subsample_cfg *cfg = &subsample_cfgs[sizeof(subsample_cfgs) / sizeof(struct subsample_cfg) - 1];
/**
* Strategy: try to keep the maximum perspective
*/
for (size_t i = 0; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
const struct subsample_cfg *cur = &subsample_cfgs[i];
if ((win_w * cur->ratio_numerator / cur->ratio_denominator >= w) && (win_h * cur->ratio_numerator / cur->ratio_denominator >= h)) {
cfg = cur;
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
row_s = (resolution[FRAMESIZE_VGA].height - win_h) / 2;
col_s = (resolution[FRAMESIZE_VGA].width - win_w) / 2;
CAMERA_LOGI(TAG"subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
break;
}
}
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0x05, H8(row_s));
write_reg(sensor->slv_addr, 0x06, L8(row_s));
write_reg(sensor->slv_addr, 0x07, H8(col_s));
write_reg(sensor->slv_addr, 0x08, L8(col_s));
write_reg(sensor->slv_addr, 0x09, H8(win_h + 8));
write_reg(sensor->slv_addr, 0x0a, L8(win_h + 8));
write_reg(sensor->slv_addr, 0x0b, H8(win_w + 8));
write_reg(sensor->slv_addr, 0x0c, L8(win_w + 8));
write_reg(sensor->slv_addr, 0xfe, 0x01);
set_reg_bits(sensor->slv_addr, 0x53, 7, 0x01, 1);
set_reg_bits(sensor->slv_addr, 0x55, 0, 0x01, 1);
write_reg(sensor->slv_addr, 0x54, cfg->reg0x54);
write_reg(sensor->slv_addr, 0x56, cfg->reg0x56);
write_reg(sensor->slv_addr, 0x57, cfg->reg0x57);
write_reg(sensor->slv_addr, 0x58, cfg->reg0x58);
write_reg(sensor->slv_addr, 0x59, cfg->reg0x59);
write_reg(sensor->slv_addr, 0xfe, 0x00);
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0xf7, col_s / 4);
write_reg(sensor->slv_addr, 0xf8, row_s / 4);
write_reg(sensor->slv_addr, 0xf9, (col_s + w) / 4);
write_reg(sensor->slv_addr, 0xfa, (row_s + h) / 4);
write_reg(sensor->slv_addr, 0x05, H8(row_s));
write_reg(sensor->slv_addr, 0x06, L8(row_s));
write_reg(sensor->slv_addr, 0x07, H8(col_s));
write_reg(sensor->slv_addr, 0x08, L8(col_s));
write_reg(sensor->slv_addr, 0x09, H8(h + 8));
write_reg(sensor->slv_addr, 0x0a, L8(h + 8));
write_reg(sensor->slv_addr, 0x0b, H8(w + 8));
write_reg(sensor->slv_addr, 0x0c, L8(w + 8));
#endif
CAMERA_LOGD(TAG"Set framesize to: %ux%u", w, h);
return ret;
}
static int set_contrast(sensor_t *sensor, int contrast)
{
if (contrast != 0) {
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0xb3, contrast);
}
return 0;
}
static int set_global_gain(sensor_t *sensor, int gain_level)
{
if (gain_level != 0) {
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, 0x50, gain_level);
}
return 0;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, 0x14, 0, 0x01, enable != 0);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, 0x14, 1, 0x01, enable != 0);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, 0x2e, 0, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
//CAMERA_LOGD(TAG"colorbar: P0 *0x2E=0x%x", read_reg(sensor->slv_addr, 0x2e));
}
return ret;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
write_reg(sensor->slv_addr, 0xfe, 0x00);
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, 0x14, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, 0x14, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
print_regs(sensor->slv_addr);
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int gc0308_detect(int slv_addr, sensor_id_t *id)
{
if (GC0308_SCCB_ADDR == slv_addr) {
write_reg(slv_addr, 0xfe, 0x00);
uint8_t PID = CAMERA_READ_REG8(slv_addr, 0x00);
if (GC0308_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc0308_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_contrast;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_global_gain;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC0308 Attached");
return 0;
}

View File

@@ -0,0 +1,402 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "gc0310.h"
#include "gc0310_regs.h"
#include "gc0310_settings.h"
#define TAG "gc0310: "
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x40; i <= 0x95; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
{
int i = 0, ret = 0;
while (!ret && (i < regs_size)) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int reset(sensor_t *sensor)
{
int ret;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, gc0310_default_regs, sizeof(gc0310_default_regs)/(sizeof(uint8_t) * 2));
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat)
{
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 0x06); // 0x06:RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 0x03); // 0x00:CbYCrY 0x01:CrYCbY 0x02:YCbYCr 0x03:YCrYCb
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
CAMERA_LOGI(TAG"set_framesize");
if (framesize > FRAMESIZE_VGA) {
CAMERA_LOGW(TAG"Invalid framesize: %u", framesize);
framesize = FRAMESIZE_VGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_VGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_VGA].width - w) / 2;
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s)); // Row_start[8]
write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s)); // Row_start[7:0]
write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s)); // Column_start[9:8]
write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s)); // Column_start[7:0]
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h)); //window_height [8]
write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h)); //window_height [7:0]
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w)); //window_width [9:8]
write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w)); //window_width [7:0]
CAMERA_LOGD(TAG"Set framesize to: %ux%u", w, h);
print_regs(sensor->slv_addr);
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_MIRROR_FLIP, 0, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_MIRROR_FLIP, 1, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 0, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
}
return ret;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_MIRROR_FLIP, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_MIRROR_FLIP, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int gc0310_detect(int slv_addr, sensor_id_t *id)
{
if (GC0310_SCCB_ADDR == slv_addr) {
uint8_t h = CAMERA_READ_REG8(slv_addr, REG_CHIP_ID_H);
uint8_t l = CAMERA_READ_REG8(slv_addr, REG_CHIP_ID_L);
uint16_t PID = (h<<8) | l;
if (GC0310_PID == PID) {
id->PID = PID;
CAMERA_LOGI(TAG"detect PID=0x%x", PID);
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc0310_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC0310 Attached");
return 0;
}

View File

@@ -0,0 +1,428 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "systick.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define TAG "gc0328: "
#include "sensor.h"
#include "gc0328.h"
#include "gc0328_regs.h"
#include "gc0328_settings.h"
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x40; i <= 0x95; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
SysTick_Delay_Ms(60);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
{
int i = 0, ret = 0;
while (!ret && (i < regs_size)) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int reset(sensor_t *sensor)
{
int ret;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, gc0328_default_regs, sizeof(gc0328_default_regs)/(sizeof(uint8_t) * 2));
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat)
{
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 0x06); // 0x06:RGB565
ret = set_reg_bits(sensor->slv_addr, 0x49, 0, 0x20, 0x20); // little endian
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 0x02); // 0x00:CbYCrY 0x01:CrYCbY 0x02:YCbYCr 0x03:YCrYCb
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_window(sensor_t *sensor, int16_t x, int16_t y, uint16_t w, uint16_t h)
{
int ret = 0;
uint16_t row_s = (uint16_t)y;
uint16_t col_s = (uint16_t)x;
ret |= write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s)); // Row_start[8]
ret |= write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s)); // Row_start[7:0]
ret |= write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s)); // Column_start[9:8]
ret |= write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s)); // Column_start[7:0]
ret |= write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h)); //window_height [8]
ret |= write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h)); //window_height [7:0]
ret |= write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w)); //window_width [9:8]
ret |= write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w)); //window_width [7:0]
if (ret == 0) {
CAMERA_LOGD(TAG"Set framesize to: %ux%u", w, h);
}
print_regs(sensor->slv_addr);
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_MIRROR_FLIP, 0, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_MIRROR_FLIP, 1, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int get_window(sensor_t *sensor, uint16_t *w, uint16_t *h)
{
int ret = 0;
uint8_t h_high, h_low, w_high, w_low;
ret |= write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
h_high = read_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH);
h_low = read_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW);
w_high = read_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH);
w_low = read_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW);
*w = (w_high << 8) | w_low;
*h = (h_high << 8) | h_low;
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 0, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
}
return ret;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
write_reg(sensor->slv_addr, RESET_RELATED, 0x00);
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_MIRROR_FLIP, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_MIRROR_FLIP, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
return 0;
}
static pixformat_t get_pixformat(sensor_t *sensor)
{
uint8_t reg_val = read_reg(sensor->slv_addr, 0x44) & 0x1f;
switch (reg_val) {
case 0x06:
return PIXFORMAT_RGB565;
case 0x00:
case 0x01:
case 0x02:
case 0x03:
return PIXFORMAT_YUV422;
default:
return PIXFORMAT_INVALID;
}
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int gc0328_detect(int slv_addr, sensor_id_t *id)
{
if (GC0328_SCCB_ADDR == slv_addr) {
//write_reg(slv_addr, RESET_RELATED, 0x00);
uint8_t PID = read_reg(slv_addr, REG_CHIP_ID);
if (GC0328_PID == PID) {
id->PID = PID;
return PID;
}
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
return 0;
}
int gc0328_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_window = set_window;
sensor->get_window = get_window;
sensor->get_pixformat = get_pixformat;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC032A Attached");
return 0;
}

View File

@@ -0,0 +1,423 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdio.h>
#define TAG "gc032a: "
#include "sensor.h"
#include "gc032a.h"
#include "gc032a_regs.h"
#include "gc032a_settings.h"
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x40; i <= 0x95; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
{
int i = 0, ret = 0;
while (!ret && regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int reset(sensor_t *sensor)
{
int ret;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xf0);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, gc032a_init_reg_tb);
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
write_reg(sensor->slv_addr, 0xfe, 0x00);
set_reg_bits(sensor->slv_addr, 0xf7, 1, 0x01, 1); // PLL_mode1:div2en
set_reg_bits(sensor->slv_addr, 0xf7, 7, 0x01, 1); // PLL_mode1:dvp mode
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 8); //PLL_mode2 :divx4
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); //vlk div mode :divide_by
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, 0x44, 0, 0x1f, 3);
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static pixformat_t get_pixformat(sensor_t *sensor)
{
int reg_val = read_reg(sensor->slv_addr, 0x44);
printf("reg_val: %d\n", reg_val);
if (reg_val < 0) {
return PIXFORMAT_INVALID;
}
switch (reg_val & 0x1F) {
case 6:
return PIXFORMAT_RGB565;
case 3:
return PIXFORMAT_YUV422;
default:
return PIXFORMAT_INVALID;
}
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_CISCTL_MODE1, 0, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_CISCTL_MODE1, 1, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE2, 3, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
}
return ret;
}
static int set_window(sensor_t *sensor, int16_t x, int16_t y, uint16_t w, uint16_t h)
{
int ret = 0;
uint16_t row_s = (uint16_t)y;
uint16_t col_s = (uint16_t)x;
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= write_reg(sensor->slv_addr, P0_ROW_START_HIGH, H8(row_s));
ret |= write_reg(sensor->slv_addr, P0_ROW_START_LOW, L8(row_s));
ret |= write_reg(sensor->slv_addr, P0_COLUMN_START_HIGH, H8(col_s));
ret |= write_reg(sensor->slv_addr, P0_COLUMN_START_LOW, L8(col_s));
ret |= write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_HIGH, H8(h + 8));
ret |= write_reg(sensor->slv_addr, P0_WINDOW_HEIGHT_LOW, L8(h + 8));
ret |= write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_HIGH, H8(w + 8));
ret |= write_reg(sensor->slv_addr, P0_WINDOW_WIDTH_LOW, L8(w + 8));
ret |= write_reg(sensor->slv_addr, P0_WIN_MODE, 0x01);
ret |= write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_HIGH, H8(h));
ret |= write_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_LOW, L8(h));
ret |= write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_HIGH, H8(w));
ret |= write_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_LOW, L8(w));
if (ret == 0) {
CAMERA_LOGD(TAG"Set window to: x=%d, y=%d, w=%u, h=%u", x, y, w, h);
}
return ret;
}
static int get_window(sensor_t *sensor, uint16_t *w, uint16_t *h)
{
int ret = 0;
uint8_t h_high, h_low, w_high, w_low;
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = read_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_HIGH);
if (ret < 0) return ret;
h_high = (uint8_t)ret;
ret = read_reg(sensor->slv_addr, P0_OUT_WIN_HEIGHT_LOW);
if (ret < 0) return ret;
h_low = (uint8_t)ret;
ret = read_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_HIGH);
if (ret < 0) return ret;
w_high = (uint8_t)ret;
ret = read_reg(sensor->slv_addr, P0_OUT_WIN_WIDTH_LOW);
if (ret < 0) return ret;
w_low = (uint8_t)ret;
*h = (h_high << 8) | h_low;
*w = (w_high << 8) | w_low;
// CAMERA_LOGD(TAG"Get window: w=%u, h=%u", *w, *h);
printf("Get window: w=%u, h=%u\n", *w, *h);
return 0;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
write_reg(sensor->slv_addr, 0xfe, 0x00);
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_CISCTL_MODE1, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int gc032a_detect(int slv_addr, sensor_id_t *id)
{
if (GC032A_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG8(slv_addr, SENSOR_ID_LOW);
uint8_t MIDH = CAMERA_READ_REG8(slv_addr, SENSOR_ID_HIGH);
uint16_t PID = MIDH << 8 | MIDL;
if (GC032A_PID == PID) {
id->PID = PID;
return PID;
}
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
return 0;
}
int gc032a_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
// sensor->set_framesize = set_framesize;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->set_window = set_window;
sensor->get_window = get_window;
sensor->get_pixformat = get_pixformat;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC032A Attached");
return 0;
}

View File

@@ -0,0 +1,470 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sensor.h"
#include "gc2145.h"
#include "gc2145_regs.h"
#include "gc2145_settings.h"
#define TAG "gc2145: "
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
{
int i = 0, ret = 0;
while (!ret && regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x80; i <= 0xa2; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, RESET_RELATED, 0xe0);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, gc2145_default_init_regs);
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
#if 0
write_reg(sensor->slv_addr, 0xfe, 0x00);
//ensure pclk <= 15MHz
set_reg_bits(sensor->slv_addr, 0xf8, 0, 0x3f, 2); // divx4
set_reg_bits(sensor->slv_addr, 0xfa, 4, 0x0f, 2); // divide_by
#endif
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat) {
case PIXFORMAT_RGB565:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 6); //RGB565
break;
case PIXFORMAT_YUV422:
write_reg(sensor->slv_addr, 0xfe, 0x00);
ret = set_reg_bits(sensor->slv_addr, P0_OUTPUT_FORMAT, 0, 0x1f, 2); //yuv422
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
if (framesize > FRAMESIZE_UXGA) {
CAMERA_LOGW(TAG"Invalid framesize: %u", framesize);
framesize = FRAMESIZE_UXGA;
}
sensor->status.framesize = framesize;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint16_t row_s = (resolution[FRAMESIZE_UXGA].height - h) / 2;
uint16_t col_s = (resolution[FRAMESIZE_UXGA].width - w) / 2;
(void)row_s;
(void)col_s;
#if CONFIG_GC_SENSOR_SUBSAMPLE_MODE
struct subsample_cfg {
uint16_t ratio_numerator;
uint16_t ratio_denominator;
uint8_t reg0x99;
uint8_t reg0x9b;
uint8_t reg0x9c;
uint8_t reg0x9d;
uint8_t reg0x9e;
uint8_t reg0x9f;
uint8_t reg0xa0;
uint8_t reg0xa1;
uint8_t reg0xa2;
};
const struct subsample_cfg subsample_cfgs[] = { // define some subsample ratio
// {60, 420, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/7 // A smaller ratio brings a larger view, but it reduces the frame rate
// {84, 420, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //1/5
// {105, 420, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/4
{140, 420, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/3
{210, 420, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/2
{240, 420, 0x77, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46, 0x02, 0x46},//4/7
{252, 420, 0x55, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04, 0x02, 0x04},//3/5
{280, 420, 0x33, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00},//2/3
{420, 420, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},//1/1
};
uint16_t win_w = resolution[FRAMESIZE_UXGA].width;
uint16_t win_h = resolution[FRAMESIZE_UXGA].height;
/* 默认使用最后一个配置 (1:1) */
const struct subsample_cfg *cfg = &subsample_cfgs[sizeof(subsample_cfgs) / sizeof(struct subsample_cfg) - 1];
/**
* Strategy: try to keep the maximum perspective
*/
uint8_t i = 0;
if (framesize >= FRAMESIZE_QVGA) {
i = 1;
}
for (; i < sizeof(subsample_cfgs) / sizeof(struct subsample_cfg); i++) {
const struct subsample_cfg *cur = &subsample_cfgs[i];
if ((win_w * cur->ratio_numerator / cur->ratio_denominator >= w) && (win_h * cur->ratio_numerator / cur->ratio_denominator >= h)) {
cfg = cur;
win_w = w * cfg->ratio_denominator / cfg->ratio_numerator;
win_h = h * cfg->ratio_denominator / cfg->ratio_numerator;
row_s = (resolution[FRAMESIZE_UXGA].height - win_h) / 2;
col_s = (resolution[FRAMESIZE_UXGA].width - win_w) / 2;
CAMERA_LOGI(TAG"subsample win:%dx%d, ratio:%f", win_w, win_h, (float)cfg->ratio_numerator / (float)cfg->ratio_denominator);
break;
}
}
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
write_reg(sensor->slv_addr, 0x09, H8(row_s));
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
write_reg(sensor->slv_addr, 0x0d, H8(win_h + 8));
write_reg(sensor->slv_addr, 0x0e, L8(win_h + 8));
write_reg(sensor->slv_addr, 0x0f, H8(win_w + 16));
write_reg(sensor->slv_addr, 0x10, L8(win_w + 16));
write_reg(sensor->slv_addr, 0x99, cfg->reg0x99);
write_reg(sensor->slv_addr, 0x9b, cfg->reg0x9b);
write_reg(sensor->slv_addr, 0x9c, cfg->reg0x9c);
write_reg(sensor->slv_addr, 0x9d, cfg->reg0x9d);
write_reg(sensor->slv_addr, 0x9e, cfg->reg0x9e);
write_reg(sensor->slv_addr, 0x9f, cfg->reg0x9f);
write_reg(sensor->slv_addr, 0xa0, cfg->reg0xa0);
write_reg(sensor->slv_addr, 0xa1, cfg->reg0xa1);
write_reg(sensor->slv_addr, 0xa2, cfg->reg0xa2);
write_reg(sensor->slv_addr, 0x95, H8(h));
write_reg(sensor->slv_addr, 0x96, L8(h));
write_reg(sensor->slv_addr, 0x97, H8(w));
write_reg(sensor->slv_addr, 0x98, L8(w));
#elif CONFIG_GC_SENSOR_WINDOWING_MODE
write_reg(sensor->slv_addr, 0xfe, 0x00);
write_reg(sensor->slv_addr, P0_CROP_ENABLE, 0x01);
// write_reg(sensor->slv_addr, 0xec, col_s / 8); //measure window
// write_reg(sensor->slv_addr, 0xed, row_s / 8);
// write_reg(sensor->slv_addr, 0xee, (col_s + h) / 8);
// write_reg(sensor->slv_addr, 0xef, (row_s + w) / 8);
write_reg(sensor->slv_addr, 0x09, H8(row_s));
write_reg(sensor->slv_addr, 0x0a, L8(row_s));
write_reg(sensor->slv_addr, 0x0b, H8(col_s));
write_reg(sensor->slv_addr, 0x0c, L8(col_s));
write_reg(sensor->slv_addr, 0x0d, H8(h + 8));
write_reg(sensor->slv_addr, 0x0e, L8(h + 8));
write_reg(sensor->slv_addr, 0x0f, H8(w + 8));
write_reg(sensor->slv_addr, 0x10, L8(w + 8));
write_reg(sensor->slv_addr, 0x95, H8(h));
write_reg(sensor->slv_addr, 0x96, L8(h));
write_reg(sensor->slv_addr, 0x97, H8(w));
write_reg(sensor->slv_addr, 0x98, L8(w));
#endif
CAMERA_LOGD(TAG"Set framesize to: %ux%u", w, h);
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_ANALOG_MODE1, 0, 0x01, enable != 0);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
ret |= set_reg_bits(sensor->slv_addr, P0_ANALOG_MODE1, 1, 0x01, enable != 0);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
// int ret = 0;
// ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
// ret |= set_reg_bits(sensor->slv_addr, P0_DEBUG_MODE3, 3, 0x01, enable);
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
return 0;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
write_reg(sensor->slv_addr, 0xfe, 0x00);
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x01);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, P0_ANALOG_MODE1, 0x02);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
print_regs(sensor->slv_addr);
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int gc2145_detect(int slv_addr, sensor_id_t *id)
{
if (GC2145_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG8(slv_addr, CHIP_ID_LOW);
uint8_t MIDH = CAMERA_READ_REG8(slv_addr, CHIP_ID_HIGH);
uint16_t PID = MIDH << 8 | MIDL;
if (GC2145_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int gc2145_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC2145 Attached");
return 0;
}

View File

@@ -0,0 +1,976 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* NT99141 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sensor.h"
#include "nt99141.h"
#include "nt99141_regs.h"
#include "nt99141_settings.h"
#define TAG "NT99141: "
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG16(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
static int read_reg16(uint8_t slv_addr, const uint16_t reg)
{
int ret = read_reg(slv_addr, reg);
if (ret >= 0) {
ret = (ret & 0xFF) << 8;
int ret2 = read_reg(slv_addr, reg + 1);
if (ret2 < 0) {
ret = ret2;
} else {
ret |= ret2 & 0xFF;
}
}
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG16(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGD(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG16(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG16(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint16_t (*regs)[2])
{
int i = 0, ret = 0;
while (!ret && regs[i][0] != REGLIST_TAIL) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int write_reg16(uint8_t slv_addr, const uint16_t reg, uint16_t value)
{
if (write_reg(slv_addr, reg, value >> 8) || write_reg(slv_addr, reg + 1, value)) {
return -1;
}
return 0;
}
static int write_addr_reg(uint8_t slv_addr, const uint16_t reg, uint16_t x_value, uint16_t y_value)
{
if (write_reg16(slv_addr, reg, x_value) || write_reg16(slv_addr, reg + 2, y_value)) {
return -1;
}
return 0;
}
#define write_reg_bits(slv_addr, reg, mask, enable) set_reg_bits(slv_addr, reg, 0, mask, enable?mask:0)
static int set_pll(sensor_t *sensor, bool bypass, uint8_t multiplier, uint8_t sys_div, uint8_t pre_div, bool root_2x, uint8_t seld5, bool pclk_manual, uint8_t pclk_div)
{
return -1;
}
static int set_ae_level(sensor_t *sensor, int level);
static int reset(sensor_t *sensor)
{
int ret = 0;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, SYSTEM_CTROL0, 0x01);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, sensor_default_regs); //re-initial
if (ret == 0) {
CAMERA_LOGD(TAG"Camera defaults loaded");
ret = set_ae_level(sensor, 0);
CAMERA_DELAY_MS(100);
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
const uint16_t (*regs)[2];
switch (pixformat) {
case PIXFORMAT_YUV422:
regs = sensor_fmt_yuv422;
break;
case PIXFORMAT_GRAYSCALE:
regs = sensor_fmt_grayscale;
break;
case PIXFORMAT_RGB565:
case PIXFORMAT_RGB888:
regs = sensor_fmt_rgb565;
break;
case PIXFORMAT_JPEG:
regs = sensor_fmt_jpeg;
break;
case PIXFORMAT_RAW:
regs = sensor_fmt_raw;
break;
default:
CAMERA_LOGE(TAG"Unsupported pixformat: %u", pixformat);
return -1;
}
ret = write_regs(sensor->slv_addr, regs);
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_image_options(sensor_t *sensor)
{
int ret = 0;
uint8_t reg20 = 0;
uint8_t reg21 = 0;
uint8_t reg4514 = 0;
uint8_t reg4514_test = 0;
// V-Flip
if (sensor->status.vflip) {
reg20 |= 0x01;
reg4514_test |= 1;
}
// H-Mirror
if (sensor->status.hmirror) {
reg21 |= 0x02;
reg4514_test |= 2;
}
switch (reg4514_test) {
}
if (write_reg(sensor->slv_addr, TIMING_TC_REG20, reg20 | reg21)) {
CAMERA_LOGE(TAG"Setting Image Options Failed");
ret = -1;
}
CAMERA_LOGD(TAG"Set Image Options: Compression: %u, Binning: %u, V-Flip: %u, H-Mirror: %u, Reg-4514: 0x%02x",
sensor->pixformat == PIXFORMAT_JPEG, sensor->status.binning, sensor->status.vflip, sensor->status.hmirror, reg4514);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
sensor->status.framesize = framesize;
ret = write_regs(sensor->slv_addr, sensor_default_regs);
if (ret) {
return ret;
}
if (framesize == FRAMESIZE_QVGA) {
CAMERA_LOGD(TAG"Set FRAMESIZE_QVGA");
ret = write_regs(sensor->slv_addr, sensor_framesize_QVGA);
#if CONFIG_NT99141_SUPPORT_XSKIP
CAMERA_LOGD(TAG"Set FRAMESIZE_QVGA: xskip mode");
ret |= write_regs(sensor->slv_addr, sensor_framesize_QVGA_xskip);
#elif CONFIG_NT99141_SUPPORT_CROP
CAMERA_LOGD(TAG"Set FRAMESIZE_QVGA: crop mode");
ret |= write_regs(sensor->slv_addr, sensor_framesize_QVGA_crop);
#endif
} else if (framesize == FRAMESIZE_VGA) {
CAMERA_LOGD(TAG"Set FRAMESIZE_VGA");
// ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
ret = write_regs(sensor->slv_addr, sensor_framesize_VGA_xyskip);// Resolution:640*360 This configuration is equally-scaled without deforming
#ifdef CONFIG_NT99141_SUPPORT_XSKIP
CAMERA_LOGD(TAG"Set FRAMESIZE_QVGA: xskip mode");
ret |= write_regs(sensor->slv_addr, sensor_framesize_VGA_xskip);
#elif CONFIG_NT99141_SUPPORT_CROP
CAMERA_LOGD(TAG"Set FRAMESIZE_QVGA: crop mode");
ret |= write_regs(sensor->slv_addr, sensor_framesize_VGA_crop);
#endif
} else if (framesize >= FRAMESIZE_HD) {
CAMERA_LOGD(TAG"Set FRAMESIZE_HD");
ret = write_regs(sensor->slv_addr, sensor_framesize_HD);
} else {
CAMERA_LOGD(TAG"Dont suppost this size, Set FRAMESIZE_VGA");
ret = write_regs(sensor->slv_addr, sensor_framesize_VGA);
}
return ret;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret = set_image_options(sensor);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = set_image_options(sensor);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_quality(sensor_t *sensor, int qs)
{
int ret = 0;
ret = write_reg(sensor->slv_addr, COMPRESSION_CTRL07, qs & 0x3f);
if (ret == 0) {
sensor->status.quality = qs;
CAMERA_LOGD(TAG"Set quality to: %d", qs);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
}
return ret;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
int ret = 0;
ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set gain_ctrl to: %d", enable);
sensor->status.agc = enable;
}
return ret;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
int ret = 0;
int data = 0;
// ret = write_reg_bits(sensor->slv_addr, 0x32bb, 0x87, enable);
data = read_reg(sensor->slv_addr, 0x3201);
CAMERA_LOGD(TAG"set_exposure_ctrl:enable");
if (enable) {
CAMERA_LOGD(TAG"set_exposure_ctrl:enable");
ret = write_reg(sensor->slv_addr, 0x3201, (1 << 5) | data);
} else {
CAMERA_LOGD(TAG"set_exposure_ctrl:disable");
ret = write_reg(sensor->slv_addr, 0x3201, (~(1 << 5)) & data);
}
if (ret == 0) {
CAMERA_LOGD(TAG"Set exposure_ctrl to: %d", enable);
sensor->status.aec = enable;
}
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set awb to: %d", enable);
sensor->status.awb = enable;
return 0;
}
//Advanced AWB
static int set_dcw_dsp(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set dcw to: %d", enable);
sensor->status.dcw = enable;
return 0;
}
//night mode enable
static int set_aec2(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set aec2 to: %d", enable);
sensor->status.aec2 = enable;
return 0;
}
static int set_bpc_dsp(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set bpc to: %d", enable);
sensor->status.bpc = enable;
return 0;
}
static int set_wpc_dsp(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set wpc to: %d", enable);
sensor->status.wpc = enable;
return 0;
}
//Gamma enable
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set raw_gma to: %d", enable);
sensor->status.raw_gma = enable;
return 0;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
CAMERA_LOGD(TAG"Set lenc to: %d", enable);
sensor->status.lenc = enable;
return 0;
}
static int get_agc_gain(sensor_t *sensor)
{
CAMERA_LOGD(TAG"get_agc_gain can not be configured at present");
return 0;
}
//real gain
static int set_agc_gain(sensor_t *sensor, int gain)
{
CAMERA_LOGD(TAG"set_agc_gain can not be configured at present");
// CAMERA_LOGD(TAG"GAIN = %d\n", gain);
int cnt = gain / 2;
switch (cnt) {
case 0:
CAMERA_LOGD(TAG"set_agc_gain: 1x");
write_reg(sensor->slv_addr, 0X301D, 0X00);
break;
case 1:
CAMERA_LOGD(TAG,"set_agc_gain: 2x");
write_reg(sensor->slv_addr, 0X301D, 0X0F);
break;
case 2:
CAMERA_LOGD(TAG,"set_agc_gain: 4x");
write_reg(sensor->slv_addr, 0X301D, 0X2F);
break;
case 3:
CAMERA_LOGD(TAG,"set_agc_gain: 6x");
write_reg(sensor->slv_addr, 0X301D, 0X37);
break;
case 4:
CAMERA_LOGD(TAG,"set_agc_gain: 8x");
write_reg(sensor->slv_addr, 0X301D, 0X3F);
break;
default:
CAMERA_LOGD(TAG,"fail set_agc_gain");
break;
}
return 0;
}
static int get_aec_value(sensor_t *sensor)
{
CAMERA_LOGD(TAG"get_aec_value can not be configured at present");
return 0;
}
static int set_aec_value(sensor_t *sensor, int value)
{
CAMERA_LOGD(TAG"set_aec_value can not be configured at present");
int ret = 0;
// CAMERA_LOGD(TAG" set_aec_value to: %d", value);
ret = write_reg_bits(sensor->slv_addr, 0x3012, 0xFF, (value >> 8) & 0xff);
ret |= write_reg_bits(sensor->slv_addr, 0x3013, 0xFF, value & 0xff);
if (ret == 0) {
CAMERA_LOGD(TAG" set_aec_value to: %d", value);
// sensor->status.aec = enable;
}
return ret;
}
static int set_ae_level(sensor_t *sensor, int level)
{
CAMERA_LOGD(TAG"set_ae_level can not be configured at present");
int ret = 0;
if (level < 0) {
level = 0;
} else if (level > 9) {
level = 9;
}
for (int i = 0; i < 5; i++) {
ret += write_reg(sensor->slv_addr, sensor_ae_level[ 5 * level + i ][0], sensor_ae_level[5 * level + i ][1]);
}
if (ret) {
CAMERA_LOGE(TAG" fail to set ae level: %d", ret);
}
return 0;
}
static int set_wb_mode(sensor_t *sensor, int mode)
{
int ret = 0;
if (mode < 0 || mode > 4) {
return -1;
}
ret = write_reg(sensor->slv_addr, 0x3201, (mode != 0));
if (ret) {
return ret;
}
switch (mode) {
case 1://Sunny
ret = write_reg16(sensor->slv_addr, 0x3290, 0x01)
|| write_reg16(sensor->slv_addr, 0x3291, 0x38)
|| write_reg16(sensor->slv_addr, 0x3296, 0x01)
|| write_reg16(sensor->slv_addr, 0x3297, 0x68)
|| write_reg16(sensor->slv_addr, 0x3060, 0x01);
break;
case 2://Cloudy
ret = write_reg16(sensor->slv_addr, 0x3290, 0x01)
|| write_reg16(sensor->slv_addr, 0x3291, 0x51)
|| write_reg16(sensor->slv_addr, 0x3296, 0x01)
|| write_reg16(sensor->slv_addr, 0x3297, 0x00)
|| write_reg16(sensor->slv_addr, 0x3060, 0x01);
break;
case 3://INCANDESCENCE]
ret = write_reg16(sensor->slv_addr, 0x3290, 0x01)
|| write_reg16(sensor->slv_addr, 0x3291, 0x30)
|| write_reg16(sensor->slv_addr, 0x3296, 0x01)
|| write_reg16(sensor->slv_addr, 0x3297, 0xCB)
|| write_reg16(sensor->slv_addr, 0x3060, 0x01);
break;
case 4://FLUORESCENT
ret = write_reg16(sensor->slv_addr, 0x3290, 0x01)
|| write_reg16(sensor->slv_addr, 0x3291, 0x70)
|| write_reg16(sensor->slv_addr, 0x3296, 0x01)
|| write_reg16(sensor->slv_addr, 0x3297, 0xFF)
|| write_reg16(sensor->slv_addr, 0x3060, 0x01);
break;
default://AUTO
break;
}
if (ret == 0) {
CAMERA_LOGD(TAG"Set wb_mode to: %d", mode);
sensor->status.wb_mode = mode;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
int old_mode = sensor->status.wb_mode;
int mode = enable ? old_mode : 0;
ret = set_wb_mode(sensor, mode);
if (ret == 0) {
sensor->status.wb_mode = old_mode;
CAMERA_LOGD(TAG"Set awb_gain to: %d", enable);
sensor->status.awb_gain = enable;
}
return ret;
}
static int set_special_effect(sensor_t *sensor, int effect)
{
int ret = 0;
if (effect < 0 || effect > 6) {
return -1;
}
uint8_t *regs = (uint8_t *)sensor_special_effects[effect];
ret = write_reg(sensor->slv_addr, 0x32F1, regs[0])
|| write_reg(sensor->slv_addr, 0x32F4, regs[1])
|| write_reg(sensor->slv_addr, 0x32F5, regs[2])
|| write_reg(sensor->slv_addr, 0x3060, regs[3]);
if (ret == 0) {
CAMERA_LOGD(TAG"Set special_effect to: %d", effect);
sensor->status.special_effect = effect;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
uint8_t value = 0;
switch (level) {
case 3:
value = 0xA0;
break;
case 2:
value = 0x90;
break;
case 1:
value = 0x88;
break;
case -1:
value = 0x78;
break;
case -2:
value = 0x70;
break;
case -3:
value = 0x60;
break;
default: // 0
break;
}
ret = write_reg(sensor->slv_addr, 0x32F2, value);
if (ret == 0) {
CAMERA_LOGD(TAG"Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
uint8_t value1 = 0, value2 = 0 ;
switch (level) {
case 3:
value1 = 0xD0;
value2 = 0xB0;
break;
case 2:
value1 = 0xE0;
value2 = 0xA0;
break;
case 1:
value1 = 0xF0;
value2 = 0x90;
break;
case 0:
value1 = 0x00;
value2 = 0x80;
break;
case -1:
value1 = 0x10;
value2 = 0x70;
break;
case -2:
value1 = 0x20;
value2 = 0x60;
break;
case -3:
value1 = 0x30;
value2 = 0x50;
break;
default: // 0
break;
}
ret = write_reg(sensor->slv_addr, 0x32FC, value1);
ret |= write_reg(sensor->slv_addr, 0x32F2, value2);
ret |= write_reg(sensor->slv_addr, 0x3060, 0x01);
if (ret == 0) {
CAMERA_LOGD(TAG"Set contrast to: %d", level);
sensor->status.contrast = level;
}
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret = 0;
if (level > 4 || level < -4) {
return -1;
}
uint8_t *regs = (uint8_t *)sensor_saturation_levels[level + 4];
ret = write_reg(sensor->slv_addr, 0x32F3, regs[0]);
if (ret == 0) {
CAMERA_LOGD(TAG"Set saturation to: %d", level);
sensor->status.saturation = level;
}
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
if (level > 3 || level < -3) {
return -1;
}
uint8_t mt_offset_2 = (level + 3) * 8;
uint8_t mt_offset_1 = mt_offset_2 + 1;
ret = write_reg_bits(sensor->slv_addr, 0x5308, 0x40, false)//0x40 means auto
|| write_reg(sensor->slv_addr, 0x5300, 0x10)
|| write_reg(sensor->slv_addr, 0x5301, 0x10)
|| write_reg(sensor->slv_addr, 0x5302, mt_offset_1)
|| write_reg(sensor->slv_addr, 0x5303, mt_offset_2)
|| write_reg(sensor->slv_addr, 0x5309, 0x10)
|| write_reg(sensor->slv_addr, 0x530a, 0x10)
|| write_reg(sensor->slv_addr, 0x530b, 0x04)
|| write_reg(sensor->slv_addr, 0x530c, 0x06);
if (ret == 0) {
CAMERA_LOGD(TAG"Set sharpness to: %d", level);
sensor->status.sharpness = level;
}
return ret;
}
static int set_gainceiling(sensor_t *sensor, gainceiling_t level)
{
CAMERA_LOGD(TAG"set_gainceiling can not be configured at present");
return 0;
}
static int get_denoise(sensor_t *sensor)
{
return (read_reg(sensor->slv_addr, 0x5306) / 4) + 1;
}
static int set_denoise(sensor_t *sensor, int level)
{
CAMERA_LOGD(TAG"set_denoise can not be configured at present");
return 0;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
ret = read_reg16(sensor->slv_addr, reg);
if (ret >= 0 && mask > 0xFFFF) {
int ret2 = read_reg(sensor->slv_addr, reg + 2);
if (ret2 >= 0) {
ret = (ret << 8) | ret2 ;
} else {
ret = ret2;
}
}
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
ret = read_reg16(sensor->slv_addr, reg);
if (ret >= 0 && mask > 0xFFFF) {
int ret2 = read_reg(sensor->slv_addr, reg + 2);
if (ret2 >= 0) {
ret = (ret << 8) | ret2 ;
} else {
ret = ret2;
}
}
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFFFF) {
ret = write_reg16(sensor->slv_addr, reg, value >> 8);
if (ret >= 0) {
ret = write_reg(sensor->slv_addr, reg + 2, value & 0xFF);
}
} else if (mask > 0xFF) {
ret = write_reg16(sensor->slv_addr, reg, value);
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
{
int ret = 0;
ret = write_addr_reg(sensor->slv_addr, X_ADDR_ST_H, startX, startY)
|| write_addr_reg(sensor->slv_addr, X_ADDR_END_H, endX, endY)
|| write_addr_reg(sensor->slv_addr, X_OFFSET_H, offsetX, offsetY)
|| write_addr_reg(sensor->slv_addr, X_TOTAL_SIZE_H, totalX, totalY)
|| write_addr_reg(sensor->slv_addr, X_OUTPUT_SIZE_H, outputX, outputY);
if (!ret) {
sensor->status.scale = scale;
sensor->status.binning = binning;
ret = set_image_options(sensor);
}
return ret;
}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
{
return set_pll(sensor, bypass > 0, multiplier, sys_div, pre_div, root_2x > 0, seld5, pclk_manual > 0, pclk_div);
}
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
if (xclk > 10)
{
CAMERA_LOGE(TAG"only XCLK under 10MHz is supported, and XCLK is now set to 10M");
xclk = 10;
}
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int nt99141_detect(int slv_addr, sensor_id_t *id)
{
if (NT99141_SCCB_ADDR == slv_addr) {
CAMERA_WRITE_REG16(slv_addr, 0x3008, 0x01);//bank sensor
uint16_t h = CAMERA_READ_REG16(slv_addr, 0x3000);
uint16_t l = CAMERA_READ_REG16(slv_addr, 0x3001);
uint16_t PID = (h<<8) | l;
if (NT99141_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = (read_reg(sensor->slv_addr, 0x3301));
sensor->status.denoise = get_denoise(sensor);
sensor->status.ae_level = 0;
sensor->status.gainceiling = read_reg16(sensor->slv_addr, 0x32F0) & 0xFF;
sensor->status.awb = check_reg_mask(sensor->slv_addr, ISP_CONTROL_01, 0x10);
sensor->status.dcw = !check_reg_mask(sensor->slv_addr, 0x5183, 0x80);
sensor->status.agc = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AGC_MANUALEN);
sensor->status.aec = !check_reg_mask(sensor->slv_addr, AEC_PK_MANUAL, AEC_PK_MANUAL_AEC_MANUALEN);
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, TIMING_TC_REG21, TIMING_TC_REG21_HMIRROR);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, TIMING_TC_REG20, TIMING_TC_REG20_VFLIP);
sensor->status.colorbar = check_reg_mask(sensor->slv_addr, PRE_ISP_TEST_SETTING_1, TEST_COLOR_BAR);
sensor->status.bpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x04);
sensor->status.wpc = check_reg_mask(sensor->slv_addr, 0x5000, 0x02);
sensor->status.raw_gma = check_reg_mask(sensor->slv_addr, 0x5000, 0x20);
sensor->status.lenc = check_reg_mask(sensor->slv_addr, 0x5000, 0x80);
sensor->status.quality = read_reg(sensor->slv_addr, COMPRESSION_CTRL07) & 0x3f;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = check_reg_mask(sensor->slv_addr, 0x3000, 0x01);
sensor->status.agc_gain = get_agc_gain(sensor);
sensor->status.aec_value = get_aec_value(sensor);
sensor->status.aec2 = check_reg_mask(sensor->slv_addr, 0x3000, 0x04);
return 0;
}
int nt99141_init(sensor_t *sensor)
{
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_contrast;
sensor->set_brightness = set_brightness;
sensor->set_saturation = set_saturation;
sensor->set_sharpness = set_sharpness;
sensor->set_gainceiling = set_gainceiling;
sensor->set_quality = set_quality;
sensor->set_colorbar = set_colorbar;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_whitebal = set_whitebal;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->init_status = init_status;
sensor->set_aec2 = set_aec2;
sensor->set_aec_value = set_aec_value;
sensor->set_special_effect = set_special_effect;
sensor->set_wb_mode = set_wb_mode;
sensor->set_ae_level = set_ae_level;
sensor->set_dcw = set_dcw_dsp;
sensor->set_bpc = set_bpc_dsp;
sensor->set_wpc = set_wpc_dsp;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
sensor->set_denoise = set_denoise;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
return 0;
}

View File

@@ -0,0 +1,605 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV2640 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "sensor.h"
#include "ov2640.h"
#include "ov2640_regs.h"
#include "ov2640_settings.h"
#define TAG "ov2640: "
static volatile ov2640_bank_t reg_bank = BANK_MAX;
static int set_bank(sensor_t *sensor, ov2640_bank_t bank)
{
int res = 0;
if (bank != reg_bank) {
reg_bank = bank;
res = CAMERA_WRITE_REG8(sensor->slv_addr, BANK_SEL, bank);
}
return res;
}
static int write_regs(sensor_t *sensor, const uint8_t (*regs)[2])
{
int i=0, res = 0;
while (regs[i][0]) {
if (regs[i][0] == BANK_SEL) {
res = set_bank(sensor, regs[i][1]);
} else {
res = CAMERA_WRITE_REG8(sensor->slv_addr, regs[i][0], regs[i][1]);
}
if (res) {
return res;
}
i++;
}
return res;
}
static int write_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg, uint8_t value)
{
int ret = set_bank(sensor, bank);
if(!ret) {
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg, value);
}
return ret;
}
static int set_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = set_bank(sensor, bank);
if(ret) {
return ret;
}
c_value = CAMERA_READ_REG8(sensor->slv_addr, reg);
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg, new_value);
return ret;
}
static int read_reg(sensor_t *sensor, ov2640_bank_t bank, uint8_t reg)
{
if(set_bank(sensor, bank)){
return 0;
}
return CAMERA_READ_REG8(sensor->slv_addr, reg);
}
static uint8_t get_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t offset, uint8_t mask)
{
return (read_reg(sensor, bank, reg) >> offset) & mask;
}
static int write_reg_bits(sensor_t *sensor, uint8_t bank, uint8_t reg, uint8_t mask, int enable)
{
return set_reg_bits(sensor, bank, reg, 0, mask, enable?mask:0);
}
#define WRITE_REGS_OR_RETURN(regs) ret = write_regs(sensor, regs); if(ret){return ret;}
#define WRITE_REG_OR_RETURN(bank, reg, val) ret = write_reg(sensor, bank, reg, val); if(ret){return ret;}
#define SET_REG_BITS_OR_RETURN(bank, reg, offset, mask, val) ret = set_reg_bits(sensor, bank, reg, offset, mask, val); if(ret){return ret;}
static int reset(sensor_t *sensor)
{
int ret = 0;
WRITE_REG_OR_RETURN(BANK_SENSOR, COM7, COM7_SRST);
CAMERA_DELAY_MS(10);
WRITE_REGS_OR_RETURN(ov2640_settings_cif);
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_RGB888:
WRITE_REGS_OR_RETURN(ov2640_settings_rgb565);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
WRITE_REGS_OR_RETURN(ov2640_settings_yuv422);
break;
case PIXFORMAT_JPEG:
WRITE_REGS_OR_RETURN(ov2640_settings_jpeg3);
break;
default:
ret = -1;
break;
}
if(!ret) {
CAMERA_DELAY_MS(10);
}
return ret;
}
static int set_window(sensor_t *sensor, ov2640_sensor_mode_t mode, int offset_x, int offset_y, int max_x, int max_y, int w, int h){
int ret = 0;
const uint8_t (*regs)[2];
ov2640_clk_t c = {0};
max_x /= 4;
max_y /= 4;
w /= 4;
h /= 4;
uint8_t win_regs[][2] = {
{BANK_SEL, BANK_DSP},
{HSIZE, max_x & 0xFF},
{VSIZE, max_y & 0xFF},
{XOFFL, offset_x & 0xFF},
{YOFFL, offset_y & 0xFF},
{VHYX, ((max_y >> 1) & 0X80) | ((offset_y >> 4) & 0X70) | ((max_x >> 5) & 0X08) | ((offset_x >> 8) & 0X07)},
{TEST, (max_x >> 2) & 0X80},
{ZMOW, (w)&0xFF},
{ZMOH, (h)&0xFF},
{ZMHH, ((h>>6)&0x04)|((w>>8)&0x03)},
{0, 0}
};
if (sensor->pixformat == PIXFORMAT_JPEG) {
c.clk_2x = 0;
c.clk_div = 0;
c.pclk_auto = 0;
c.pclk_div = 8;
if(mode == OV2640_MODE_UXGA) {
c.pclk_div = 12;
}
// if (sensor->xclk_freq_hz == 16000000) {
// c.pclk_div = c.pclk_div / 2;
// }
} else {
#if 0
c.clk_2x = 0;
#else
c.clk_2x = 1;
#endif
c.clk_div = 7;
c.pclk_auto = 1;
c.pclk_div = 8;
if (mode == OV2640_MODE_CIF) {
c.clk_div = 3;
} else if(mode == OV2640_MODE_UXGA) {
c.pclk_div = 12;
}
}
CAMERA_LOGI(TAG"Set PLL: clk_2x: %u, clk_div: %u, pclk_auto: %u, pclk_div: %u", c.clk_2x, c.clk_div, c.pclk_auto, c.pclk_div);
if (mode == OV2640_MODE_CIF) {
regs = ov2640_settings_to_cif;
} else if (mode == OV2640_MODE_SVGA) {
regs = ov2640_settings_to_svga;
} else {
regs = ov2640_settings_to_uxga;
}
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_BYPAS);
WRITE_REGS_OR_RETURN(regs);
WRITE_REGS_OR_RETURN(win_regs);
WRITE_REG_OR_RETURN(BANK_SENSOR, CLKRC, c.clk);
WRITE_REG_OR_RETURN(BANK_DSP, R_DVP_SP, c.pclk);
WRITE_REG_OR_RETURN(BANK_DSP, R_BYPASS, R_BYPASS_DSP_EN);
CAMERA_DELAY_MS(10);
//required when changing resolution
set_pixformat(sensor, sensor->pixformat);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret = 0;
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
aspect_ratio_t ratio = resolution[framesize].aspect_ratio;
uint16_t max_x = ratio_table[ratio].max_x;
uint16_t max_y = ratio_table[ratio].max_y;
uint16_t offset_x = ratio_table[ratio].offset_x;
uint16_t offset_y = ratio_table[ratio].offset_y;
ov2640_sensor_mode_t mode = OV2640_MODE_UXGA;
sensor->status.framesize = framesize;
if (framesize <= FRAMESIZE_CIF) {
mode = OV2640_MODE_CIF;
max_x /= 4;
max_y /= 4;
offset_x /= 4;
offset_y /= 4;
if(max_y > 296){
max_y = 296;
}
} else if (framesize <= FRAMESIZE_SVGA) {
mode = OV2640_MODE_SVGA;
max_x /= 2;
max_y /= 2;
offset_x /= 2;
offset_y /= 2;
}
ret = set_window(sensor, mode, offset_x, offset_y, max_x, max_y, w, h);
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_CONTRAST_LEVELS) {
return -1;
}
sensor->status.contrast = level-3;
for (int i=0; i<7; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, contrast_regs[0][i], contrast_regs[level][i]);
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_BRIGHTNESS_LEVELS) {
return -1;
}
sensor->status.brightness = level-3;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, brightness_regs[0][i], brightness_regs[level][i]);
}
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_SATURATION_LEVELS) {
return -1;
}
sensor->status.saturation = level-3;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, saturation_regs[0][i], saturation_regs[level][i]);
}
return ret;
}
static int set_special_effect(sensor_t *sensor, int effect)
{
int ret=0;
effect++;
if (effect <= 0 || effect > NUM_SPECIAL_EFFECTS) {
return -1;
}
sensor->status.special_effect = effect-1;
for (int i=0; i<5; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, special_effects_regs[0][i], special_effects_regs[effect][i]);
}
return ret;
}
static int set_wb_mode(sensor_t *sensor, int mode)
{
int ret=0;
if (mode < 0 || mode > NUM_WB_MODES) {
return -1;
}
sensor->status.wb_mode = mode;
SET_REG_BITS_OR_RETURN(BANK_DSP, 0XC7, 6, 1, mode?1:0);
if(mode) {
for (int i=0; i<3; i++) {
WRITE_REG_OR_RETURN(BANK_DSP, wb_modes_regs[0][i], wb_modes_regs[mode][i]);
}
}
return ret;
}
static int set_ae_level(sensor_t *sensor, int level)
{
int ret=0;
level += 3;
if (level <= 0 || level > NUM_AE_LEVELS) {
return -1;
}
sensor->status.ae_level = level-3;
for (int i=0; i<3; i++) {
WRITE_REG_OR_RETURN(BANK_SENSOR, ae_levels_regs[0][i], ae_levels_regs[level][i]);
}
return ret;
}
static int set_quality(sensor_t *sensor, int quality)
{
if(quality < 0) {
quality = 0;
} else if(quality > 63) {
quality = 63;
}
sensor->status.quality = quality;
return write_reg(sensor, BANK_DSP, QS, quality);
}
static int set_agc_gain(sensor_t *sensor, int gain)
{
if(gain < 0) {
gain = 0;
} else if(gain > 30) {
gain = 30;
}
sensor->status.agc_gain = gain;
return write_reg(sensor, BANK_SENSOR, GAIN, agc_gain_tbl[gain]);
}
static int set_gainceiling_sensor(sensor_t *sensor, gainceiling_t gainceiling)
{
sensor->status.gainceiling = gainceiling;
//return write_reg(sensor, BANK_SENSOR, COM9, COM9_AGC_SET(gainceiling));
return set_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7, gainceiling);
}
static int set_aec_value(sensor_t *sensor, int value)
{
if(value < 0) {
value = 0;
} else if(value > 1200) {
value = 1200;
}
sensor->status.aec_value = value;
return set_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3, value & 0x3)
|| write_reg(sensor, BANK_SENSOR, AEC, (value >> 2) & 0xFF)
|| set_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F, value >> 10);
}
static int set_aec2(sensor_t *sensor, int enable)
{
sensor->status.aec2 = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1, enable?0:1);
}
static int set_colorbar(sensor_t *sensor, int enable)
{
sensor->status.colorbar = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM7, COM7_COLOR_BAR, enable?1:0);
}
static int set_agc_sensor(sensor_t *sensor, int enable)
{
sensor->status.agc = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AGC_EN, enable?1:0);
}
static int set_aec_sensor(sensor_t *sensor, int enable)
{
sensor->status.aec = enable;
return write_reg_bits(sensor, BANK_SENSOR, COM8, COM8_AEC_EN, enable?1:0);
}
static int set_hmirror_sensor(sensor_t *sensor, int enable)
{
sensor->status.hmirror = enable;
return write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_HFLIP_IMG, enable?1:0);
}
static int set_vflip_sensor(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret = write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VREF_EN, enable?1:0);
return ret & write_reg_bits(sensor, BANK_SENSOR, REG04, REG04_VFLIP_IMG, enable?1:0);
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
sensor->status.raw_gma = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1, enable?1:0);
}
static int set_awb_dsp(sensor_t *sensor, int enable)
{
sensor->status.awb = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1, enable?1:0);
}
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
{
sensor->status.awb_gain = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1, enable?1:0);
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
sensor->status.lenc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1, enable?1:0);
}
static int set_dcw_dsp(sensor_t *sensor, int enable)
{
sensor->status.dcw = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1, enable?1:0);
}
static int set_bpc_dsp(sensor_t *sensor, int enable)
{
sensor->status.bpc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1, enable?1:0);
}
static int set_wpc_dsp(sensor_t *sensor, int enable)
{
sensor->status.wpc = enable;
return set_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1, enable?1:0);
}
//unsupported
static int set_sharpness(sensor_t *sensor, int level)
{
return -1;
}
static int set_denoise(sensor_t *sensor, int level)
{
return -1;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = read_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = write_reg(sensor, (reg >> 8) & 0x01, reg & 0xFF, value);
return ret;
}
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning)
{
return set_window(sensor, (ov2640_sensor_mode_t)startX, offsetX, offsetY, totalX, totalY, outputX, outputY);
}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div)
{
return -1;
}
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
static int init_status(sensor_t *sensor){
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.agc_gain = 30;
int agc_gain = read_reg(sensor, BANK_SENSOR, GAIN);
for (int i=0; i<30; i++){
if(agc_gain >= agc_gain_tbl[i] && agc_gain < agc_gain_tbl[i+1]){
sensor->status.agc_gain = i;
break;
}
}
sensor->status.aec_value = ((uint16_t)get_reg_bits(sensor, BANK_SENSOR, REG45, 0, 0x3F) << 10)
| ((uint16_t)read_reg(sensor, BANK_SENSOR, AEC) << 2)
| get_reg_bits(sensor, BANK_SENSOR, REG04, 0, 3);//0 - 1200
sensor->status.quality = read_reg(sensor, BANK_DSP, QS);
sensor->status.gainceiling = get_reg_bits(sensor, BANK_SENSOR, COM9, 5, 7);
sensor->status.awb = get_reg_bits(sensor, BANK_DSP, CTRL1, 3, 1);
sensor->status.awb_gain = get_reg_bits(sensor, BANK_DSP, CTRL1, 2, 1);
sensor->status.aec = get_reg_bits(sensor, BANK_SENSOR, COM8, 0, 1);
sensor->status.aec2 = get_reg_bits(sensor, BANK_DSP, CTRL0, 6, 1);
sensor->status.agc = get_reg_bits(sensor, BANK_SENSOR, COM8, 2, 1);
sensor->status.bpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 7, 1);
sensor->status.wpc = get_reg_bits(sensor, BANK_DSP, CTRL3, 6, 1);
sensor->status.raw_gma = get_reg_bits(sensor, BANK_DSP, CTRL1, 5, 1);
sensor->status.lenc = get_reg_bits(sensor, BANK_DSP, CTRL1, 1, 1);
sensor->status.hmirror = get_reg_bits(sensor, BANK_SENSOR, REG04, 7, 1);
sensor->status.vflip = get_reg_bits(sensor, BANK_SENSOR, REG04, 6, 1);
sensor->status.dcw = get_reg_bits(sensor, BANK_DSP, CTRL2, 5, 1);
sensor->status.colorbar = get_reg_bits(sensor, BANK_SENSOR, COM7, 1, 1);
sensor->status.sharpness = 0;//not supported
sensor->status.denoise = 0;
return 0;
}
int ov2640_detect(int slv_addr, sensor_id_t *id)
{
if (OV2640_SCCB_ADDR == slv_addr) {
CAMERA_WRITE_REG8(slv_addr, 0xFF, 0x01);//bank sensor
uint16_t PID = CAMERA_READ_REG8(slv_addr, 0x0A);
if (OV2640_PID == PID) {
id->PID = PID;
id->VER = CAMERA_READ_REG8(slv_addr, REG_VER);
id->MIDL = CAMERA_READ_REG8(slv_addr, REG_MIDL);
id->MIDH = CAMERA_READ_REG8(slv_addr, REG_MIDH);
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int ov2640_init(sensor_t *sensor)
{
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_contrast;
sensor->set_brightness= set_brightness;
sensor->set_saturation= set_saturation;
sensor->set_quality = set_quality;
sensor->set_colorbar = set_colorbar;
sensor->set_gainceiling = set_gainceiling_sensor;
sensor->set_gain_ctrl = set_agc_sensor;
sensor->set_exposure_ctrl = set_aec_sensor;
sensor->set_hmirror = set_hmirror_sensor;
sensor->set_vflip = set_vflip_sensor;
sensor->set_whitebal = set_awb_dsp;
sensor->set_aec2 = set_aec2;
sensor->set_aec_value = set_aec_value;
sensor->set_special_effect = set_special_effect;
sensor->set_wb_mode = set_wb_mode;
sensor->set_ae_level = set_ae_level;
sensor->set_dcw = set_dcw_dsp;
sensor->set_bpc = set_bpc_dsp;
sensor->set_wpc = set_wpc_dsp;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
//not supported
sensor->set_sharpness = set_sharpness;
sensor->set_denoise = set_denoise;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
CAMERA_LOGD(TAG"OV2640 Attached");
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,452 @@
/*
* This file is part of the OpenMV project.
* author: Juan Schiavoni <juanjoseschiavoni@hotmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV7725 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "ov7670.h"
#include "ov7670_regs.h"
#define TAG "ov7760: "
static int ov7670_clkrc = 0x01;
/*
* The default register settings, as obtained from OmniVision. There
* is really no making sense of most of these - lots of "reserved" values
* and such.
*
* These settings give VGA YUYV.
*/
struct regval_list {
uint8_t reg_num;
uint8_t value;
};
static struct regval_list ov7670_default_regs[] = {
/* Sensor automatically sets output window when resolution changes. */
{TSLB, 0x04},
/* Frame rate 30 fps at 12 Mhz clock */
{CLKRC, 0x00},
{DBLV, 0x4A},
{COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE},
/* Improve white balance */
{COM4, 0x40},
/* Improve color */
{RSVD_B0, 0x84},
/* Enable 50/60 Hz auto detection */
{COM11, COM11_EXP|COM11_HZAUTO},
/* Disable some delays */
{HSYST, 0},
{HSYEN, 0},
{MVFP, MVFP_SUN},
/* More reserved magic, some of which tweaks white balance */
{AWBC1, 0x0a},
{AWBC2, 0xf0},
{AWBC3, 0x34},
{AWBC4, 0x58},
{AWBC5, 0x28},
{AWBC6, 0x3a},
{AWBCTR3, 0x0a},
{AWBCTR2, 0x55},
{AWBCTR1, 0x11},
{AWBCTR0, 0x9e},
{COM8, COM8_FAST_AUTO|COM8_STEP_UNLIMIT|COM8_AGC_EN|COM8_AEC_EN|COM8_AWB_EN},
/* End marker is FF because in ov7670 the address of GAIN 0 and default value too. */
{0xFF, 0xFF},
};
static struct regval_list ov7670_fmt_yuv422[] = {
{ COM7, 0x0 }, /* Selects YUV mode */
{ RGB444, 0 }, /* No RGB444 please */
{ COM1, 0 }, /* CCIR601 */
{ COM15, COM15_R00FF },
{ MVFP, MVFP_SUN },
{ COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */
{ MTX1, 0x80 }, /* "matrix coefficient 1" */
{ MTX2, 0x80 }, /* "matrix coefficient 2" */
{ MTX3, 0 }, /* vb */
{ MTX4, 0x22 }, /* "matrix coefficient 4" */
{ MTX5, 0x5e }, /* "matrix coefficient 5" */
{ MTX6, 0x80 }, /* "matrix coefficient 6" */
{ COM13, COM13_UVSAT },
{ 0xff, 0xff }, /* END MARKER */
};
static struct regval_list ov7670_fmt_rgb565[] = {
{ COM7, COM7_FMT_RGB565 }, /* Selects RGB mode */
{ RGB444, 0 }, /* No RGB444 please */
{ COM1, 0x0 }, /* CCIR601 */
{ COM15, COM15_RGB565 |COM15_R00FF },
{ MVFP, MVFP_SUN },
{ COM9, 0x6A }, /* 128x gain ceiling; 0x8 is reserved bit */
{ MTX1, 0xb3 }, /* "matrix coefficient 1" */
{ MTX2, 0xb3 }, /* "matrix coefficient 2" */
{ MTX3, 0 }, /* vb */
{ MTX4, 0x3d }, /* "matrix coefficient 4" */
{ MTX5, 0xa7 }, /* "matrix coefficient 5" */
{ MTX6, 0xe4 }, /* "matrix coefficient 6" */
{ COM13, COM13_UVSAT },
{ 0xff, 0xff }, /* END MARKER */
};
static struct regval_list ov7670_vga[] = {
{ COM3, 0x00 },
{ COM14, 0x00 },
{ SCALING_XSC, 0x3A },
{ SCALING_YSC, 0x35 },
{ SCALING_DCWCTR, 0x11 },
{ SCALING_PCLK_DIV, 0xF0 },
{ SCALING_PCLK_DELAY, 0x02 },
{ 0xff, 0xff },
};
static struct regval_list ov7670_qvga[] = {
{ COM3, 0x04 },
{ COM14, 0x19 },
{ SCALING_XSC, 0x3A },
{ SCALING_YSC, 0x35 },
{ SCALING_DCWCTR, 0x11 },
{ SCALING_PCLK_DIV, 0xF1 },
{ SCALING_PCLK_DELAY, 0x02 },
{ 0xff, 0xff },
};
static struct regval_list ov7670_qqvga[] = {
{ COM3, 0x04 }, //DCW enable
{ COM14, 0x1a }, //pixel clock divided by 4, manual scaling enable, DCW and PCLK controlled by register
{ SCALING_XSC, 0x3a },
{ SCALING_YSC, 0x35 },
{ SCALING_DCWCTR, 0x22 }, //downsample by 4
{ SCALING_PCLK_DIV, 0xf2 }, //pixel clock divided by 4
{ SCALING_PCLK_DELAY, 0x02 },
{ 0xff, 0xff },
};
/*
* Write a list of register settings; ff/ff stops the process.
*/
static int ov7670_write_array(sensor_t *sensor, struct regval_list *vals)
{
int ret = 0;
while ( (vals->reg_num != 0xff || vals->value != 0xff) && (ret == 0) ) {
ret = CAMERA_WRITE_REG8(sensor->slv_addr, vals->reg_num, vals->value);
CAMERA_LOGD(TAG"reset reg %02X, W(%02X) R(%02X)", vals->reg_num,
vals->value, CAMERA_READ_REG8(sensor->slv_addr, vals->reg_num) );
vals++;
}
return ret;
}
/*
* Calculate the frame control registers.
*/
static int ov7670_frame_control(sensor_t *sensor, int hstart, int hstop, int vstart, int vstop)
{
struct regval_list frame[7];
frame[0].reg_num = HSTART;
frame[0].value = (hstart >> 3);
frame[1].reg_num = HSTOP;
frame[1].value = (hstop >> 3);
frame[2].reg_num = HREF;
frame[2].value = (((hstop & 0x07) << 3) | (hstart & 0x07));
frame[3].reg_num = VSTART;
frame[3].value = (vstart >> 2);
frame[4].reg_num = VSTOP;
frame[4].value = (vstop >> 2);
frame[5].reg_num = VREF;
frame[5].value = (((vstop & 0x02) << 2) | (vstart & 0x02));
/* End mark */
frame[6].reg_num = 0xFF;
frame[6].value = 0xFF;
return ov7670_write_array(sensor, frame);
}
static int reset(sensor_t *sensor)
{
int ret;
// Reset all registers
CAMERA_WRITE_REG8(sensor->slv_addr, COM7, COM7_RESET);
// Delay 10 ms
CAMERA_DELAY_MS(10);
ret = ov7670_write_array(sensor, ov7670_default_regs);
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret;
switch (pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_RGB888:
ret = ov7670_write_array(sensor, ov7670_fmt_rgb565);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
default:
ret = ov7670_write_array(sensor, ov7670_fmt_yuv422);
break;
}
CAMERA_DELAY_MS(30);
/*
* If we're running RGB565, we must rewrite clkrc after setting
* the other parameters or the image looks poor. If we're *not*
* doing RGB565, we must not rewrite clkrc or the image looks
* *really* poor.
*
* (Update) Now that we retain clkrc state, we should be able
* to write it unconditionally, and that will make the frame
* rate persistent too.
*/
if (pixformat == PIXFORMAT_RGB565) {
ret = CAMERA_WRITE_REG8(sensor->slv_addr, CLKRC, ov7670_clkrc);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret;
// store clkrc before changing window settings...
ov7670_clkrc = CAMERA_READ_REG8(sensor->slv_addr, CLKRC);
switch (framesize){
case FRAMESIZE_VGA:
if( (ret = ov7670_write_array(sensor, ov7670_vga)) == 0 ) {
/* These values from Omnivision */
ret = ov7670_frame_control(sensor, 158, 14, 10, 490);
}
break;
case FRAMESIZE_QVGA:
if( (ret = ov7670_write_array(sensor, ov7670_qvga)) == 0 ) {
/* These values from Omnivision */
ret = ov7670_frame_control(sensor, 158, 14, 10, 490);
}
break;
case FRAMESIZE_QQVGA:
if( (ret = ov7670_write_array(sensor, ov7670_qqvga)) == 0 ) {
/* These values from Omnivision */
ret = ov7670_frame_control(sensor, 158, 14, 12, 490);
}
break;
default:
ret = -1;
}
CAMERA_DELAY_MS(30);
if (ret == 0) {
sensor->status.framesize = framesize;
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
uint8_t ret = 0;
// Read register scaling_xsc
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, SCALING_XSC);
// Pattern to set color bar bit[0]=0 in every case
reg = SCALING_XSC_CBAR(reg);
// Write pattern to SCALING_XSC
ret = CAMERA_WRITE_REG8(sensor->slv_addr, SCALING_XSC, reg);
// Read register scaling_ysc
reg = CAMERA_READ_REG8(sensor->slv_addr, SCALING_YSC);
// Pattern to set color bar bit[0]=0 in every case
reg = SCALING_YSC_CBAR(reg, enable);
// Write pattern to SCALING_YSC
ret = ret | CAMERA_WRITE_REG8(sensor->slv_addr, SCALING_YSC, reg);
// return 0 or 0xFF
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
// Read register COM8
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM8);
// Set white bal on/off
reg = COM8_SET_AWB(reg, enable);
// Write back register COM8
return CAMERA_WRITE_REG8(sensor->slv_addr, COM8, reg);
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
// Read register COM8
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM8);
// Set white bal on/off
reg = COM8_SET_AGC(reg, enable);
// Write back register COM8
return CAMERA_WRITE_REG8(sensor->slv_addr, COM8, reg);
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
// Read register COM8
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM8);
// Set white bal on/off
reg = COM8_SET_AEC(reg, enable);
// Write back register COM8
return CAMERA_WRITE_REG8(sensor->slv_addr, COM8, reg);
}
static int set_hmirror(sensor_t *sensor, int enable)
{
// Read register MVFP
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, MVFP);
// Set mirror on/off
reg = MVFP_SET_MIRROR(reg, enable);
// Write back register MVFP
return CAMERA_WRITE_REG8(sensor->slv_addr, MVFP, reg);
}
static int set_vflip(sensor_t *sensor, int enable)
{
// Read register MVFP
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, MVFP);
// Set mirror on/off
reg = MVFP_SET_FLIP(reg, enable);
// Write back register MVFP
return CAMERA_WRITE_REG8(sensor->slv_addr, MVFP, reg);
}
static int init_status(sensor_t *sensor)
{
sensor->status.awb = 0;
sensor->status.aec = 0;
sensor->status.agc = 0;
sensor->status.hmirror = 0;
sensor->status.vflip = 0;
sensor->status.colorbar = 0;
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
int ov7670_detect(int slv_addr, sensor_id_t *id)
{
if (OV7670_SCCB_ADDR == slv_addr) {
CAMERA_WRITE_REG8(slv_addr, 0xFF, 0x01);//bank sensor
uint16_t PID = CAMERA_READ_REG8(slv_addr, 0x0A);
if (OV7670_PID == PID) {
id->PID = PID;
id->VER = CAMERA_READ_REG8(slv_addr, REG_VER);
id->MIDL = CAMERA_READ_REG8(slv_addr, REG_MIDL);
id->MIDH = CAMERA_READ_REG8(slv_addr, REG_MIDH);
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int ov7670_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_whitebal;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
//not supported
sensor->set_brightness= set_dummy;
sensor->set_saturation= set_dummy;
sensor->set_quality = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_aec2 = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
// Retrieve sensor's signature
sensor->id.MIDH = CAMERA_READ_REG8(sensor->slv_addr, REG_MIDH);
sensor->id.MIDL = CAMERA_READ_REG8(sensor->slv_addr, REG_MIDL);
sensor->id.PID = CAMERA_READ_REG8(sensor->slv_addr, REG_PID);
sensor->id.VER = CAMERA_READ_REG8(sensor->slv_addr, REG_VER);
CAMERA_LOGD(TAG"OV7670 Attached");
return 0;
}

View File

@@ -0,0 +1,568 @@
/*
* This file is part of the OpenMV project.
* Copyright (c) 2013/2014 Ibrahim Abdelkader <i.abdalkader@gmail.com>
* This work is licensed under the MIT license, see the file LICENSE for details.
*
* OV7725 driver.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "ov7725.h"
#include "ov7725_regs.h"
#define TAG "ov7725: "
static const uint8_t default_regs[][2] = {
{COM3, COM3_SWAP_YUV},
{COM7, COM7_RES_QVGA | COM7_FMT_YUV},
{COM4, 0x01 | 0x00}, /* bypass PLL (0x00:off, 0x40:4x, 0x80:6x, 0xC0:8x) */
{CLKRC, 0x80 | 0x03}, /* Res/Bypass pre-scalar (0x40:bypass, 0x00-0x3F:prescaler PCLK=XCLK/(prescaler + 1)/2 ) */
// QVGA Window Size
{HSTART, 0x3F},
{HSIZE, 0x50},
{VSTART, 0x03},
{VSIZE, 0x78},
{HREF, 0x00},
// Scale down to QVGA Resolution
{HOUTSIZE, 0x50},
{VOUTSIZE, 0x78},
{EXHCH, 0x00},
{COM12, 0x03},
{TGT_B, 0x7F},
{FIXGAIN, 0x09},
{AWB_CTRL0, 0xE0},
{DSP_CTRL1, 0xFF},
{DSP_CTRL2, DSP_CTRL2_VDCW_EN | DSP_CTRL2_HDCW_EN | DSP_CTRL2_HZOOM_EN | DSP_CTRL2_VZOOM_EN},
{DSP_CTRL3, 0x00},
{DSP_CTRL4, 0x00},
{DSPAUTO, 0xFF},
{COM8, 0xF0},
{COM6, 0xC5},
{COM9, 0x11},
{COM10, COM10_VSYNC_NEG | COM10_PCLK_FREE}, //Invert VSYNC and MASK PCLK
{BDBASE, 0x7F},
{DBSTEP, 0x03},
{AEW, 0x75},
{AEB, 0x64},
{VPT, 0xA1},
{EXHCL, 0x00},
{AWB_CTRL3, 0xAA},
{COM8, 0xFF},
//Gamma
{GAM1, 0x0C},
{GAM2, 0x16},
{GAM3, 0x2A},
{GAM4, 0x4E},
{GAM5, 0x61},
{GAM6, 0x6F},
{GAM7, 0x7B},
{GAM8, 0x86},
{GAM9, 0x8E},
{GAM10, 0x97},
{GAM11, 0xA4},
{GAM12, 0xAF},
{GAM13, 0xC5},
{GAM14, 0xD7},
{GAM15, 0xE8},
{SLOP, 0x20},
{EDGE1, 0x05},
{EDGE2, 0x03},
{EDGE3, 0x00},
{DNSOFF, 0x01},
{MTX1, 0xB0},
{MTX2, 0x9D},
{MTX3, 0x13},
{MTX4, 0x16},
{MTX5, 0x7B},
{MTX6, 0x91},
{MTX_CTRL, 0x1E},
{BRIGHTNESS, 0x08},
{CONTRAST, 0x30},
{UVADJ0, 0x81},
{SDE, (SDE_CONT_BRIGHT_EN | SDE_SATURATION_EN)},
// For 30 fps/60Hz
{DM_LNL, 0x00},
{DM_LNH, 0x00},
{BDBASE, 0x7F},
{DBSTEP, 0x03},
// Lens Correction, should be tuned with real camera module
{LC_RADI, 0x10},
{LC_COEF, 0x10},
{LC_COEFB, 0x14},
{LC_COEFR, 0x17},
{LC_CTR, 0x05},
{COM5, 0xF5}, //0x65
{0x00, 0x00},
};
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg & 0xFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int set_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg & 0xFF, value);
return ret;
}
static int get_reg_bits(sensor_t *sensor, uint8_t reg, uint8_t offset, uint8_t length)
{
int ret = 0;
ret = CAMERA_READ_REG8(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
return (ret & mask) >> offset;
}
static int reset(sensor_t *sensor)
{
int i=0;
const uint8_t (*regs)[2];
// Reset all registers
CAMERA_WRITE_REG8(sensor->slv_addr, COM7, COM7_RESET);
// Delay 10 ms
CAMERA_DELAY_MS(10);
// Write default regsiters
for (i=0, regs = default_regs; regs[i][0]; i++) {
CAMERA_WRITE_REG8(sensor->slv_addr, regs[i][0], regs[i][1]);
}
// Delay
CAMERA_DELAY_MS(30);
return 0;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
// Read register COM7
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM7);
switch (pixformat) {
case PIXFORMAT_RGB565:
reg = COM7_SET_RGB(reg, COM7_FMT_RGB565);
break;
case PIXFORMAT_YUV422:
case PIXFORMAT_GRAYSCALE:
reg = COM7_SET_FMT(reg, COM7_FMT_YUV);
break;
default:
return -1;
}
// Write back register COM7
ret = CAMERA_WRITE_REG8(sensor->slv_addr, COM7, reg);
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
int ret=0;
if (framesize > FRAMESIZE_VGA) {
return -1;
}
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
uint8_t reg = CAMERA_READ_REG8(sensor->slv_addr, COM7);
sensor->status.framesize = framesize;
// Write MSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, HOUTSIZE, w>>2);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, VOUTSIZE, h>>1);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, HSIZE, w>>2);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, VSIZE, h>>1);
// Write LSBs
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, HREF, ((w&0x3) | ((h&0x1) << 2)));
if (framesize < FRAMESIZE_VGA) {
// Enable auto-scaling/zooming factors
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, DSPAUTO, 0xFF);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, HSTART, 0x3F);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, VSTART, 0x03);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, COM7, reg | COM7_RES_QVGA);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, CLKRC, 0x80 | 0x01);
} else {
// Disable auto-scaling/zooming factors
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, DSPAUTO, 0xF3);
// Clear auto-scaling/zooming factors
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, SCAL0, 0x00);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, SCAL1, 0x00);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, SCAL2, 0x00);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, HSTART, 0x23);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, VSTART, 0x07);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, COM7, reg & ~COM7_RES_QVGA);
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, CLKRC, 0x80 | 0x03);
}
// Delay
CAMERA_DELAY_MS(30);
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret=0;
uint8_t reg;
sensor->status.colorbar = enable;
// Read reg COM3
reg = CAMERA_READ_REG8(sensor->slv_addr, COM3);
// Enable colorbar test pattern output
reg = COM3_SET_CBAR(reg, enable);
// Write back COM3
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, COM3, reg);
// Read reg DSP_CTRL3
reg = CAMERA_READ_REG8(sensor->slv_addr, DSP_CTRL3);
// Enable DSP colorbar output
reg = DSP_CTRL3_SET_CBAR(reg, enable);
// Write back DSP_CTRL3
ret |= CAMERA_WRITE_REG8(sensor->slv_addr, DSP_CTRL3, reg);
return ret;
}
static int set_whitebal(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, COM8, 1, 1, enable) >= 0){
sensor->status.awb = !!enable;
}
return sensor->status.awb;
}
static int set_gain_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, COM8, 2, 1, enable) >= 0){
sensor->status.agc = !!enable;
}
return sensor->status.agc;
}
static int set_exposure_ctrl(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, COM8, 0, 1, enable) >= 0){
sensor->status.aec = !!enable;
}
return sensor->status.aec;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, COM3, 6, 1, enable) >= 0){
sensor->status.hmirror = !!enable;
}
return sensor->status.hmirror;
}
static int set_vflip(sensor_t *sensor, int enable)
{
if(set_reg_bits(sensor, COM3, 7, 1, enable) >= 0){
sensor->status.vflip = !!enable;
}
return sensor->status.vflip;
}
static int set_dcw_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x65, 2, 1, !enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set dcw to: %d", enable);
sensor->status.dcw = enable;
}
return ret;
}
static int set_aec2(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, COM8, 7, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set aec2 to: %d", enable);
sensor->status.aec2 = enable;
}
return ret;
}
static int set_bpc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x64, 1, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set bpc to: %d", enable);
sensor->status.bpc = enable;
}
return ret;
}
static int set_wpc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x64, 0, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set wpc to: %d", enable);
sensor->status.wpc = enable;
}
return ret;
}
static int set_raw_gma_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x64, 2, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set raw_gma to: %d", enable);
sensor->status.raw_gma = enable;
}
return ret;
}
static int set_lenc_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, LC_CTR, 0, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set lenc to: %d", enable);
sensor->status.lenc = enable;
}
return ret;
}
//real gain
static int set_agc_gain(sensor_t *sensor, int gain)
{
int ret = 0;
ret = set_reg_bits(sensor, COM9, 4, 3, gain % 5);
if (ret == 0) {
CAMERA_LOGD(TAG"Set gain to: %d", gain);
sensor->status.agc_gain = gain;
}
return ret;
}
static int set_aec_value(sensor_t *sensor, int value)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, AEC, value & 0xff) | CAMERA_WRITE_REG8(sensor->slv_addr, AECH, value >> 8);
if (ret == 0) {
CAMERA_LOGD(TAG"Set aec_value to: %d", value);
sensor->status.aec_value = value;
}
return ret;
}
static int set_awb_gain_dsp(sensor_t *sensor, int enable)
{
int ret = 0;
ret = set_reg_bits(sensor, 0x63, 7, 1, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set awb_gain to: %d", enable);
sensor->status.awb_gain = enable;
}
return ret;
}
static int set_brightness(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x9B, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set brightness to: %d", level);
sensor->status.brightness = level;
}
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
ret = CAMERA_WRITE_REG8(sensor->slv_addr, 0x9C, level);
if (ret == 0) {
CAMERA_LOGD(TAG"Set contrast to: %d", level);
sensor->status.contrast = level;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = CAMERA_READ_REG8(sensor->slv_addr, 0x9B);
sensor->status.contrast = CAMERA_READ_REG8(sensor->slv_addr, 0x9C);
sensor->status.saturation = 0;
sensor->status.ae_level = 0;
sensor->status.special_effect = get_reg_bits(sensor, 0x64, 5, 1);
sensor->status.wb_mode = get_reg_bits(sensor, 0x6B, 7, 1);
sensor->status.agc_gain = get_reg_bits(sensor, COM9, 4, 3);
sensor->status.aec_value = CAMERA_READ_REG8(sensor->slv_addr, AEC) | (CAMERA_READ_REG8(sensor->slv_addr, AECH) << 8);
sensor->status.gainceiling = CAMERA_READ_REG8(sensor->slv_addr, 0x00);
sensor->status.awb = get_reg_bits(sensor, COM8, 1, 1);
sensor->status.awb_gain = get_reg_bits(sensor, 0x63, 7, 1);
sensor->status.aec = get_reg_bits(sensor, COM8, 0, 1);
sensor->status.aec2 = get_reg_bits(sensor, COM8, 7, 1);
sensor->status.agc = get_reg_bits(sensor, COM8, 2, 1);
sensor->status.bpc = get_reg_bits(sensor, 0x64, 1, 1);
sensor->status.wpc = get_reg_bits(sensor, 0x64, 0, 1);
sensor->status.raw_gma = get_reg_bits(sensor, 0x64, 2, 1);
sensor->status.lenc = get_reg_bits(sensor, LC_CTR, 0, 1);
sensor->status.hmirror = get_reg_bits(sensor, COM3, 6, 1);
sensor->status.vflip = get_reg_bits(sensor, COM3, 7, 1);
sensor->status.dcw = get_reg_bits(sensor, 0x65, 2, 1);
sensor->status.colorbar = get_reg_bits(sensor, COM3, 0, 1);
sensor->status.sharpness = get_reg_bits(sensor, EDGE0, 0, 5);
sensor->status.denoise = CAMERA_READ_REG8(sensor->slv_addr, 0x8E);
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val){ return -1; }
static int set_res_raw(sensor_t *sensor, int startX, int startY, int endX, int endY, int offsetX, int offsetY, int totalX, int totalY, int outputX, int outputY, bool scale, bool binning){return -1;}
static int _set_pll(sensor_t *sensor, int bypass, int multiplier, int sys_div, int root_2x, int pre_div, int seld5, int pclk_manual, int pclk_div){return -1;}
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int ov7725_detect(int slv_addr, sensor_id_t *id)
{
if (OV7725_SCCB_ADDR == slv_addr) {
CAMERA_WRITE_REG8(slv_addr, 0xFF, 0x01);//bank sensor
uint16_t PID = CAMERA_READ_REG8(slv_addr, 0x0A);
if (OV7725_PID == PID) {
id->PID = PID;
id->VER = CAMERA_READ_REG8(slv_addr, REG_VER);
id->MIDL = CAMERA_READ_REG8(slv_addr, REG_MIDL);
id->MIDH = CAMERA_READ_REG8(slv_addr, REG_MIDH);
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int ov7725_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_whitebal;
sensor->set_gain_ctrl = set_gain_ctrl;
sensor->set_exposure_ctrl = set_exposure_ctrl;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_brightness = set_brightness;
sensor->set_contrast = set_contrast;
sensor->set_aec2 = set_aec2;
sensor->set_aec_value = set_aec_value;
sensor->set_awb_gain = set_awb_gain_dsp;
sensor->set_agc_gain = set_agc_gain;
sensor->set_dcw = set_dcw_dsp;
sensor->set_bpc = set_bpc_dsp;
sensor->set_wpc = set_wpc_dsp;
sensor->set_raw_gma = set_raw_gma_dsp;
sensor->set_lenc = set_lenc_dsp;
//not supported
sensor->set_saturation= set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = set_res_raw;
sensor->set_pll = _set_pll;
sensor->set_xclk = set_xclk;
// Retrieve sensor's signature
sensor->id.MIDH = CAMERA_READ_REG8(sensor->slv_addr, REG_MIDH);
sensor->id.MIDL = CAMERA_READ_REG8(sensor->slv_addr, REG_MIDL);
sensor->id.PID = CAMERA_READ_REG8(sensor->slv_addr, REG_PID);
sensor->id.VER = CAMERA_READ_REG8(sensor->slv_addr, REG_VER);
CAMERA_LOGD(TAG"OV7725 Attached");
return 0;
}

View File

@@ -0,0 +1,397 @@
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "ov9655.h"
#include "ov9655_regs.h"
#include "ov9655_settings.h"
#define TAG "ov9655: "
#define H8(v) ((v)>>8)
#define L8(v) ((v)&0xff)
//#define REG_DEBUG_ON
static int read_reg(uint8_t slv_addr, const uint16_t reg)
{
int ret = CAMERA_READ_REG8(slv_addr, reg);
#ifdef REG_DEBUG_ON
if (ret < 0) {
CAMERA_LOGE(TAG"READ REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int write_reg(uint8_t slv_addr, const uint16_t reg, uint8_t value)
{
int ret = 0;
#ifndef REG_DEBUG_ON
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
#else
int old_value = read_reg(slv_addr, reg);
if (old_value < 0) {
return old_value;
}
if ((uint8_t)old_value != value) {
CAMERA_LOGI(TAG"NEW REG 0x%04x: 0x%02x to 0x%02x", reg, (uint8_t)old_value, value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);
} else {
CAMERA_LOGD(TAG"OLD REG 0x%04x: 0x%02x", reg, (uint8_t)old_value);
ret = CAMERA_WRITE_REG8(slv_addr, reg, value);//maybe not?
}
if (ret < 0) {
CAMERA_LOGE(TAG"WRITE REG 0x%04x FAILED: %d", reg, ret);
}
#endif
return ret;
}
static int check_reg_mask(uint8_t slv_addr, uint16_t reg, uint8_t mask)
{
return (read_reg(slv_addr, reg) & mask) == mask;
}
#if 0
static void print_regs(uint8_t slv_addr)
{
#ifdef DEBUG_PRINT_REG
CAMERA_DELAY_MS(100);
CAMERA_LOGI(TAG"REG list look ======================");
for (size_t i = 0xf0; i <= 0xfe; i++) {
CAMERA_LOGI(TAG"reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 0 ===");
write_reg(slv_addr, 0xfe, 0x00); // page 0
for (size_t i = 0x03; i <= 0x24; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
for (size_t i = 0x40; i <= 0x95; i++) {
CAMERA_LOGI(TAG"p0 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
CAMERA_LOGI(TAG"\npage 3 ===");
write_reg(slv_addr, 0xfe, 0x03); // page 3
for (size_t i = 0x01; i <= 0x43; i++) {
CAMERA_LOGI(TAG"p3 reg[0x%02x] = 0x%02x", i, read_reg(slv_addr, i));
}
#endif
}
#endif
static int set_reg_bits(uint8_t slv_addr, uint16_t reg, uint8_t offset, uint8_t mask, uint8_t value)
{
int ret = 0;
uint8_t c_value, new_value;
ret = read_reg(slv_addr, reg);
if (ret < 0) {
return ret;
}
c_value = ret;
new_value = (c_value & ~(mask << offset)) | ((value & mask) << offset);
ret = write_reg(slv_addr, reg, new_value);
return ret;
}
static int write_regs(uint8_t slv_addr, const uint8_t (*regs)[2], size_t regs_size)
{
int i = 0, ret = 0;
while (!ret && (i < regs_size)) {
if (regs[i][0] == REG_DLY) {
CAMERA_DELAY_MS(regs[i][1]);
} else {
ret = write_reg(slv_addr, regs[i][0], regs[i][1]);
}
i++;
}
return ret;
}
static int reset(sensor_t *sensor)
{
int ret;
// Software Reset: clear all registers and reset them to their default values
ret = write_reg(sensor->slv_addr, OV9655_REG_COM7, SCCB_REG_RESET);
if (ret) {
CAMERA_LOGE(TAG"Software Reset FAILED!");
return ret;
}
CAMERA_DELAY_MS(100);
ret = write_regs(sensor->slv_addr, ov9655_default_regs, sizeof(ov9655_default_regs)/(sizeof(uint8_t) * 2));
if (ret == 0) {
/* Set the RGB565 mode */
write_reg(sensor->slv_addr, OV9655_REG_COM7, FORMAT_CTRL_30fpsVGA_VArioPixel | OUTPUT_FORMAT_RGB);
write_reg(sensor->slv_addr, OV9655_REG_COM15, RGB_565);
/* Invert the HRef signal*/
write_reg(sensor->slv_addr, OV9655_REG_COM10, 0x08);
CAMERA_LOGD(TAG"Camera defaults loaded");
CAMERA_DELAY_MS(100);
write_reg(sensor->slv_addr, OV9655_REG_COM7, 0x00);
}
return ret;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret = 0;
switch (pixformat)
{
case PIXFORMAT_RGB565:
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM7, 0, 0x3, 0x3);
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM15, 4, 0x3, 0x1);
break;
case PIXFORMAT_RGB555:
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM7, 0, 0x3, 0x3);
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM15, 4, 0x3, 0x3);
break;
case PIXFORMAT_YUV422:
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM7, 0, 0x3, 0x2);
break;
default:
CAMERA_LOGW(TAG"unsupport format");
ret = -1;
break;
}
if (ret == 0) {
sensor->pixformat = pixformat;
CAMERA_LOGD(TAG"Set pixformat to: %u", pixformat);
}
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.hmirror = enable;
ret |= set_reg_bits(sensor->slv_addr, OV9655_REG_MVFP, 5, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set h-mirror to: %d", enable);
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
sensor->status.vflip = enable;
ret |= set_reg_bits(sensor->slv_addr, OV9655_REG_MVFP, 4, 0x01, enable);
if (ret == 0) {
CAMERA_LOGD(TAG"Set v-flip to: %d", enable);
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM3, 7, 0x01, enable);
ret += set_reg_bits(sensor->slv_addr, OV9655_REG_COM20, 4, 0x01, enable);
if (ret == 0) {
sensor->status.colorbar = enable;
CAMERA_LOGD(TAG"Set colorbar to: %d", enable);
}
return ret;
}
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret > 0) {
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
if (mask > 0xFF) {
CAMERA_LOGE(TAG"mask should not more than 0xff");
} else {
ret = read_reg(sensor->slv_addr, reg);
}
if (ret < 0) {
return ret;
}
value = (ret & ~mask) | (value & mask);
if (mask > 0xFF) {
} else {
ret = write_reg(sensor->slv_addr, reg, value);
}
return ret;
}
static int init_status(sensor_t *sensor)
{
sensor->status.brightness = 0;
sensor->status.contrast = 0;
sensor->status.saturation = 0;
sensor->status.sharpness = 0;
sensor->status.denoise = 0;
sensor->status.ae_level = 0;
sensor->status.gainceiling = 0;
sensor->status.awb = 0;
sensor->status.dcw = 0;
sensor->status.agc = 0;
sensor->status.aec = 0;
sensor->status.hmirror = check_reg_mask(sensor->slv_addr, OV9655_REG_MVFP, 0x20);
sensor->status.vflip = check_reg_mask(sensor->slv_addr, OV9655_REG_MVFP, 0x10);
sensor->status.colorbar = 0;
sensor->status.bpc = 0;
sensor->status.wpc = 0;
sensor->status.raw_gma = 0;
sensor->status.lenc = 0;
sensor->status.quality = 0;
sensor->status.special_effect = 0;
sensor->status.wb_mode = 0;
sensor->status.awb_gain = 0;
sensor->status.agc_gain = 0;
sensor->status.aec_value = 0;
sensor->status.aec2 = 0;
return 0;
}
static int set_dummy(sensor_t *sensor, int val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
static int set_gainceiling_dummy(sensor_t *sensor, gainceiling_t val)
{
CAMERA_LOGW(TAG"Unsupported");
return -1;
}
int ov9655_detect(int slv_addr, sensor_id_t *id)
{
#if 1
if (OV9655_SCCB_ADDR == slv_addr) {
uint8_t PID = read_reg(slv_addr, OV9655_REG_PID);
if (OV9655_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
#else
for(slv_addr = 0; slv_addr <= 0x7F; slv_addr++)
{
CAMERA_LOGI(TAG"slv_addr=0x%x", slv_addr);
uint8_t PID = read_reg(slv_addr, OV9655_REG_PID);
if (OV9655_PID == PID) {
id->PID = PID;
CAMERA_LOGI(TAG"ov9655_detect");
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
#endif
return 0;
}
int ov9655_init(sensor_t *sensor)
{
sensor->init_status = init_status;
sensor->reset = reset;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_contrast = set_dummy;
sensor->set_brightness = set_dummy;
sensor->set_saturation = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_gainceiling = set_gainceiling_dummy;
sensor->set_quality = set_dummy;
sensor->set_colorbar = set_colorbar;
sensor->set_whitebal = set_dummy;
sensor->set_gain_ctrl = set_dummy;
sensor->set_exposure_ctrl = set_dummy;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_aec2 = set_dummy;
sensor->set_awb_gain = set_dummy;
sensor->set_agc_gain = set_dummy;
sensor->set_aec_value = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->set_dcw = set_dummy;
sensor->set_bpc = set_dummy;
sensor->set_wpc = set_dummy;
sensor->set_raw_gma = set_dummy;
sensor->set_lenc = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_res_raw = NULL;
sensor->set_pll = NULL;
sensor->set_xclk = NULL;
CAMERA_LOGD(TAG"GC032A Attached");
return 0;
}

View File

@@ -0,0 +1,328 @@
/*
* SC030IOT driver.
*
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "sc030iot.h"
#include "sc030iot_settings.h"
#define TAG "sc030: "
#define SC030_SENSOR_ID_HIGH_REG 0XF7
#define SC030_SENSOR_ID_LOW_REG 0XF8
#define SC030_MAX_FRAME_WIDTH (640)
#define SC030_MAX_FRAME_HIGH (480)
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
// For more information please refer to the Technical Reference Manual.
static int get_reg(sensor_t *sensor, int reg, int reg_value_mask)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
uint8_t reg_low = reg & 0xFF;
if(CAMERA_WRITE_REG8(sensor->slv_addr, 0xf0, reg_high)) {
return -1;
}
ret = CAMERA_READ_REG8(sensor->slv_addr, reg_low);
if(ret > 0){
ret &= reg_value_mask;
}
return ret;
}
// sc030 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
// For more information please refer to the Technical Reference Manual.
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
uint8_t reg_low = reg & 0xFF;
if(CAMERA_WRITE_REG8(sensor->slv_addr, 0xf0, reg_high)) {
return -1;
}
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg_low, value & 0xFF);
return ret;
}
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
{
int i=0, res = 0;
while (i<regs_entry_len) {
res = CAMERA_WRITE_REG8(sensor->slv_addr, regs[i][0], regs[i][1]);
if (res) {
return res;
}
i++;
}
return res;
}
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = get_reg(sensor, reg, 0xff);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
return ret;
}
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
} else {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
} else {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable test pattern mode
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
return ret;
}
static int set_agc_gain(sensor_t *sensor, int gain)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
return ret;
}
static int set_aec_value(sensor_t *sensor, int value)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
return ret;
}
static int set_awb_gain(sensor_t *sensor, int value)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
return ret;
}
static int reset(sensor_t *sensor)
{
int ret = set_regs(sensor, sc030iot_default_init_regs, sizeof(sc030iot_default_init_regs)/(sizeof(uint8_t) * 2));
// Delay
CAMERA_DELAY_MS(50);
// CAMERA_LOGI(TAG"set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
// CAMERA_LOGI(TAG"0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
if (ret) {
CAMERA_LOGE(TAG"reset fail");
}
return ret;
}
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
{
int ret = 0;
//sc:H_start={0x0172[1:0],0x0170},H_end={0x0172[5:4],0x0171},
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x03) | (((offset_x+w)>>4)&0x30));
//sc:V_start={0x0175[1:0],0x0173},H_end={0x0175[5:4],0x0174},
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x03) | (((offset_y+h)>>4)&0x30));
CAMERA_DELAY_MS(10);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
if(w>SC030_MAX_FRAME_WIDTH || h > SC030_MAX_FRAME_HIGH) {
goto err;
}
uint16_t offset_x = (640-w) /2;
uint16_t offset_y = (480-h) /2;
if(set_window(sensor, offset_x, offset_y, w, h)) {
goto err;
}
sensor->status.framesize = framesize;
return 0;
err:
CAMERA_LOGE(TAG"frame size err");
return -1;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_RAW:
case PIXFORMAT_GRAYSCALE:
CAMERA_LOGE(TAG"Not support");
break;
case PIXFORMAT_YUV422: // For now, sc030/sc031 sensor only support YUV422.
break;
default:
return -1;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int sc030iot_detect(int slv_addr, sensor_id_t *id)
{
if (SC030IOT_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG8(slv_addr, SC030_SENSOR_ID_LOW_REG);
uint8_t MIDH = CAMERA_READ_REG8(slv_addr, SC030_SENSOR_ID_HIGH_REG);
uint16_t PID = MIDH << 8 | MIDL;
if (SC030IOT_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int sc030iot_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_saturation= set_saturation;
sensor->set_colorbar = set_colorbar;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_sharpness = set_sharpness;
sensor->set_agc_gain = set_agc_gain;
sensor->set_aec_value = set_aec_value;
sensor->set_awb_gain = set_awb_gain;
sensor->set_contrast = set_contrast;
//not supported
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_xclk = set_xclk;
CAMERA_LOGD(TAG"sc030iot Attached");
return 0;
}

View File

@@ -0,0 +1,336 @@
/*
* SC031GS driver.
*
* Copyright 2022-2023 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "sc031gs.h"
#include "sc031gs_settings.h"
#define TAG "sc031gs: "
#define SC031GS_PID_LOW_REG 0x3107
#define SC031GS_PID_HIGH_REG 0x3108
#define SC031GS_MAX_FRAME_WIDTH (640)
#define SC031GS_MAX_FRAME_HIGH (480)
#define SC031GS_GAIN_CTRL_COARSE_REG 0x3e08
#define SC031GS_GAIN_CTRL_FINE_REG 0x3e09
#define SC031GS_PIDH_MAGIC 0x00 // High byte of sensor ID
#define SC031GS_PIDL_MAGIC 0x31 // Low byte of sensor ID
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = CAMERA_READ_REG16(sensor->slv_addr, reg & 0xFFFF);
if(ret > 0){
ret &= mask;
}
return ret;
}
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
ret = CAMERA_READ_REG16(sensor->slv_addr, reg & 0xFFFF);
if(ret < 0){
return ret;
}
value = (ret & ~mask) | (value & mask);
ret = CAMERA_WRITE_REG16(sensor->slv_addr, reg & 0xFFFF, value);
return ret;
}
static int set_reg_bits(sensor_t *sensor, uint16_t reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = CAMERA_READ_REG16(sensor->slv_addr, reg);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = CAMERA_WRITE_REG16(sensor->slv_addr, reg, value);
return ret;
}
static int write_regs(uint8_t slv_addr, const struct sc031gs_regval *regs)
{
int i = 0, ret = 0;
while (!ret && regs[i].addr != REG_NULL) {
if (regs[i].addr == REG_DELAY) {
CAMERA_DELAY_MS(regs[i].val);
} else {
ret = CAMERA_WRITE_REG16(slv_addr, regs[i].addr, regs[i].val);
}
i++;
}
return ret;
}
#define WRITE_REGS_OR_RETURN(regs) ret = write_regs(slv_addr, regs); if(ret){return ret;}
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // mirror on
} else {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // mirror off
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
} else {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x4501, 3, 1, enable & 0x01); // enable test pattern mode
SET_REG_BITS_OR_RETURN(0x3902, 6, 1, 1); // enable auto BLC, disable auto BLC if set to 0
SET_REG_BITS_OR_RETURN(0x3e06, 0, 2, 3); // digital gain: 00->1x, 01->2x, 03->4x.
return ret;
}
static int set_special_effect(sensor_t *sensor, int sleep_mode_enable) // For sc03ags sensor, This API used for sensor sleep mode control.
{
// Add some others special control in this API, use switch to control different funcs, such as ctrl_id.
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0100, 0, 1, !(sleep_mode_enable & 0x01)); // 0: enable sleep mode. In sleep mode, the registers can be accessed.
return ret;
}
int set_bpc(sensor_t *sensor, int enable) // // For sc03ags sensor, This API used to control BLC
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x3900, 0, 1, enable & 0x01);
SET_REG_BITS_OR_RETURN(0x3902, 6, 1, enable & 0x01);
return ret;
}
static int set_agc_gain(sensor_t *sensor, int gain)
{
// sc031gs doesn't support AGC, use this func to control.
int ret = 0;
uint32_t coarse_gain, fine_gain, fine_again_reg_v, coarse_gain_reg_v;
if (gain < 0x20) {
WRITE_REG_OR_RETURN(0x3314, 0x3a);
WRITE_REG_OR_RETURN(0x3317, 0x20);
} else {
WRITE_REG_OR_RETURN(0x3314, 0x44);
WRITE_REG_OR_RETURN(0x3317, 0x0f);
}
if (gain < 0x20) { /*1x ~ 2x*/
fine_gain = gain - 16;
coarse_gain = 0x03;
fine_again_reg_v = ((0x01 << 4) & 0x10) |
(fine_gain & 0x0f);
coarse_gain_reg_v = coarse_gain & 0x1F;
} else if (gain < 0x40) { /*2x ~ 4x*/
fine_gain = (gain >> 1) - 16;
coarse_gain = 0x7;
fine_again_reg_v = ((0x01 << 4) & 0x10) |
(fine_gain & 0x0f);
coarse_gain_reg_v = coarse_gain & 0x1F;
} else if (gain < 0x80) { /*4x ~ 8x*/
fine_gain = (gain >> 2) - 16;
coarse_gain = 0xf;
fine_again_reg_v = ((0x01 << 4) & 0x10) |
(fine_gain & 0x0f);
coarse_gain_reg_v = coarse_gain & 0x1F;
} else { /*8x ~ 16x*/
fine_gain = (gain >> 3) - 16;
coarse_gain = 0x1f;
fine_again_reg_v = ((0x01 << 4) & 0x10) |
(fine_gain & 0x0f);
coarse_gain_reg_v = coarse_gain & 0x1F;
}
WRITE_REG_OR_RETURN(SC031GS_GAIN_CTRL_COARSE_REG, coarse_gain_reg_v);
WRITE_REG_OR_RETURN(SC031GS_GAIN_CTRL_FINE_REG, fine_again_reg_v);
return ret;
}
static int set_aec_value(sensor_t *sensor, int value)
{
// For now, HDR is disabled, the sensor work in normal mode.
int ret = 0;
WRITE_REG_OR_RETURN(0x3e01, value & 0xFF); // AE target high
WRITE_REG_OR_RETURN(0x3e02, (value >> 8) & 0xFF); // AE target low
return ret;
}
static int reset(sensor_t *sensor)
{
int ret = write_regs(sensor->slv_addr, sc031gs_reset_regs);
if (ret) {
CAMERA_LOGE(TAG"reset fail");
}
// printf("reg 0x3d04=%02x\r\n", get_reg(sensor, 0x3d04, 0xff));
// set_colorbar(sensor, 1);
return ret;
}
static int set_output_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
{
int ret = 0;
//sc:H_start={0x3212[1:0],0x3213},H_length={0x3208[1:0],0x3209},
WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_H_REG, ((w>>8) & 0x03));
WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_WIDTH_L_REG, w & 0xff);
//sc:V_start={0x3210[1:0],0x3211},V_length={0x320a[1:0],0x320b},
WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_H_REG, ((h>>8) & 0x03));
WRITE_REG_OR_RETURN(SC031GS_OUTPUT_WINDOW_HIGH_L_REG, h & 0xff);
CAMERA_DELAY_MS(10);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
struct sc031gs_regval const *framesize_regs = sc031gs_200x200_init_regs;
if(framesize > FRAMESIZE_VGA) {
goto err;
} else if(framesize > FRAMESIZE_QVGA) {
framesize_regs = sc031gs_640x480_50fps_init_regs;
}
uint16_t offset_x = (640-w) /2 + 4;
uint16_t offset_y = (480-h) /2 + 4;
int ret = write_regs(sensor->slv_addr, framesize_regs);
if (ret) {
CAMERA_LOGE(TAG"reset fail");
}
if(set_output_window(sensor, offset_x, offset_y, w, h)) {
goto err;
}
sensor->status.framesize = framesize;
return 0;
err:
CAMERA_LOGE(TAG"frame size err");
return -1;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_GRAYSCALE:
break;
default:
CAMERA_LOGE(TAG"Only support GRAYSCALE(Y8)");
return -1;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int sc031gs_detect(int slv_addr, sensor_id_t *id)
{
if (SC031GS_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG16(slv_addr, SC031GS_PID_HIGH_REG);
uint8_t MIDH = CAMERA_READ_REG16(slv_addr, SC031GS_PID_LOW_REG);
uint16_t PID = MIDH << 8 | MIDL;
if (SC031GS_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int sc031gs_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_colorbar = set_colorbar;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_agc_gain = set_agc_gain;
sensor->set_aec_value = set_aec_value;
//not supported
sensor->set_awb_gain = set_dummy;
sensor->set_contrast = set_dummy;
sensor->set_sharpness = set_dummy;
sensor->set_saturation= set_dummy;
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_xclk = set_xclk;
CAMERA_LOGD(TAG"sc031gs Attached");
return 0;
}

View File

@@ -0,0 +1,335 @@
/*
* SC101IOT driver.
*
* Copyright 2020-2022 Espressif Systems (Shanghai) PTE LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sensor.h"
#include "sc101iot.h"
#include "sc101iot_settings.h"
#define TAG "sc101: "
#define SC101_SENSOR_ID_HIGH_REG 0XF7
#define SC101_SENSOR_ID_LOW_REG 0XF8
#define SC101_MAX_FRAME_WIDTH (1280)
#define SC101_MAX_FRAME_HIGH (720)
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
// For more information please refer to the Technical Reference Manual.
static int get_reg(sensor_t *sensor, int reg, int mask)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
uint8_t reg_low = reg & 0xFF;
if(CAMERA_WRITE_REG8(sensor->slv_addr, 0xf0, reg_high)) {
return -1;
}
ret = CAMERA_READ_REG8(sensor->slv_addr, reg_low);
if(ret > 0){
ret &= mask;
}
return ret;
}
// sc101 use "i2c paging mode", so the high byte of the register needs to be written to the 0xf0 reg.
// For more information please refer to the Technical Reference Manual.
static int set_reg(sensor_t *sensor, int reg, int mask, int value)
{
int ret = 0;
uint8_t reg_high = (reg>>8) & 0xFF;
uint8_t reg_low = reg & 0xFF;
if(CAMERA_WRITE_REG8(sensor->slv_addr, 0xf0, reg_high)) {
return -1;
}
ret = CAMERA_WRITE_REG8(sensor->slv_addr, reg_low, value & 0xFF);
return ret;
}
static int set_regs(sensor_t *sensor, const uint8_t (*regs)[2], uint32_t regs_entry_len)
{
int i=0, res = 0;
while (i<regs_entry_len) {
res = CAMERA_WRITE_REG8(sensor->slv_addr, regs[i][0], regs[i][1]);
if (res) {
return res;
}
i++;
}
return res;
}
static int set_reg_bits(sensor_t *sensor, int reg, uint8_t offset, uint8_t length, uint8_t value)
{
int ret = 0;
ret = get_reg(sensor, reg, 0xff);
if(ret < 0){
return ret;
}
uint8_t mask = ((1 << length) - 1) << offset;
value = (ret & ~mask) | ((value << offset) & mask);
ret = set_reg(sensor, reg & 0xFFFF, 0xFFFF, value);
return ret;
}
#define WRITE_REGS_OR_RETURN(regs, regs_entry_len) ret = set_regs(sensor, regs, regs_entry_len); if(ret){return ret;}
#define WRITE_REG_OR_RETURN(reg, val) ret = set_reg(sensor, reg, 0xFF, val); if(ret){return ret;}
#define SET_REG_BITS_OR_RETURN(reg, offset, length, val) ret = set_reg_bits(sensor, reg, offset, length, val); if(ret){return ret;}
static int set_hmirror(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x3); // enable mirror
} else {
SET_REG_BITS_OR_RETURN(0x3221, 1, 2, 0x0); // disable mirror
}
return ret;
}
static int set_vflip(sensor_t *sensor, int enable)
{
int ret = 0;
if(enable) {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x3); // flip on
} else {
SET_REG_BITS_OR_RETURN(0x3221, 5, 2, 0x0); // flip off
}
return ret;
}
static int set_colorbar(sensor_t *sensor, int enable)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0100, 7, 1, enable & 0xff); // enable colorbar mode
return ret;
}
static int set_raw_gma(sensor_t *sensor, int enable)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00f5, 1, 1, enable & 0xff); // enable gamma compensation
return ret;
}
static int set_sharpness(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00e0, 1, 1, 1); // enable edge enhancement
WRITE_REG_OR_RETURN(0x00d0, level & 0xFF); // base value
WRITE_REG_OR_RETURN(0x00d2, (level >> 8) & 0xFF); // limit
return ret;
}
static int set_agc_gain(sensor_t *sensor, int gain)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0070, 1, 1, 1); // enable auto agc control
WRITE_REG_OR_RETURN(0x0068, gain & 0xFF); // Window weight setting1
WRITE_REG_OR_RETURN(0x0069, (gain >> 8) & 0xFF); // Window weight setting2
WRITE_REG_OR_RETURN(0x006a, (gain >> 16) & 0xFF); // Window weight setting3
WRITE_REG_OR_RETURN(0x006b, (gain >> 24) & 0xFF); // Window weight setting4
return ret;
}
static int set_aec_value(sensor_t *sensor, int value)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x0070, 0, 1, 1); // enable auto aec control
WRITE_REG_OR_RETURN(0x0072, value & 0xFF); // AE target
return ret;
}
static int set_awb_gain(sensor_t *sensor, int value)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00b0, 0, 1, 1); // enable awb control
WRITE_REG_OR_RETURN(0x00c8, value & 0xFF); // blue gain
WRITE_REG_OR_RETURN(0x00c9, (value>>8) & 0XFF); // red gain
return ret;
}
static int set_saturation(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00f5, 5, 1, 0); // enable saturation control
WRITE_REG_OR_RETURN(0x0149, level & 0xFF); // blue saturation gain (/128)
WRITE_REG_OR_RETURN(0x014a, (level>>8) & 0XFF); // red saturation gain (/128)
return ret;
}
static int set_contrast(sensor_t *sensor, int level)
{
int ret = 0;
SET_REG_BITS_OR_RETURN(0x00f5, 6, 1, 0); // enable contrast control
WRITE_REG_OR_RETURN(0x014b, level); // contrast coefficient(/64)
return ret;
}
static int reset(sensor_t *sensor)
{
int ret = set_regs(sensor, sc101iot_default_init_regs, sizeof(sc101iot_default_init_regs)/(sizeof(uint8_t) * 2));
// Delay
CAMERA_DELAY_MS(50);
// CAMERA_LOGI(TAG"set_reg=%0x", set_reg(sensor, 0x0100, 0xffff, 0x00)); // write 0x80 to enter test mode if you want to test the sensor
// CAMERA_LOGI(TAG"0x0100=%0x", get_reg(sensor, 0x0100, 0xffff));
if (ret) {
CAMERA_LOGE(TAG"reset fail");
}
return ret;
}
static int set_window(sensor_t *sensor, int offset_x, int offset_y, int w, int h)
{
int ret = 0;
//sc:H_start={0x0172[3:0],0x0170},H_end={0x0172[7:4],0x0171},
WRITE_REG_OR_RETURN(0x0170, offset_x & 0xff);
WRITE_REG_OR_RETURN(0x0171, (offset_x+w) & 0xff);
WRITE_REG_OR_RETURN(0x0172, ((offset_x>>8) & 0x0f) | (((offset_x+w)>>4)&0xf0));
//sc:V_start={0x0175[3:0],0x0173},H_end={0x0175[7:4],0x0174},
WRITE_REG_OR_RETURN(0x0173, offset_y & 0xff);
WRITE_REG_OR_RETURN(0x0174, (offset_y+h) & 0xff);
WRITE_REG_OR_RETURN(0x0175, ((offset_y>>8) & 0x0f) | (((offset_y+h)>>4)&0xf0));
CAMERA_DELAY_MS(10);
return ret;
}
static int set_framesize(sensor_t *sensor, framesize_t framesize)
{
uint16_t w = resolution[framesize].width;
uint16_t h = resolution[framesize].height;
if(w>SC101_MAX_FRAME_WIDTH || h > SC101_MAX_FRAME_HIGH) {
goto err;
}
uint16_t offset_x = (SC101_MAX_FRAME_WIDTH-w) /2;
uint16_t offset_y = (SC101_MAX_FRAME_HIGH-h) /2;
if(set_window(sensor, offset_x, offset_y, w, h)) {
goto err;
}
sensor->status.framesize = framesize;
return 0;
err:
CAMERA_LOGE(TAG"frame size err");
return -1;
}
static int set_pixformat(sensor_t *sensor, pixformat_t pixformat)
{
int ret=0;
sensor->pixformat = pixformat;
switch (pixformat) {
case PIXFORMAT_RGB565:
case PIXFORMAT_RAW:
case PIXFORMAT_GRAYSCALE:
CAMERA_LOGE(TAG"Not support");
break;
case PIXFORMAT_YUV422: // For now, sc101 sensor only support YUV422.
break;
default:
ret = -1;
}
return ret;
}
static int init_status(sensor_t *sensor)
{
return 0;
}
static int set_dummy(sensor_t *sensor, int val){ return -1; }
static int set_xclk(sensor_t *sensor, int timer, int xclk)
{
int ret = 0;
sensor->xclk_freq_hz = xclk * 1000000U;
//ret = xclk_timer_conf(timer, sensor->xclk_freq_hz);
return ret;
}
int sc101iot_detect(int slv_addr, sensor_id_t *id)
{
if (SC101IOT_SCCB_ADDR == slv_addr) {
uint8_t MIDL = CAMERA_READ_REG8(slv_addr, SC101_SENSOR_ID_LOW_REG);
uint8_t MIDH = CAMERA_READ_REG8(slv_addr, SC101_SENSOR_ID_HIGH_REG);
uint16_t PID = MIDH << 8 | MIDL;
if (SC101IOT_PID == PID) {
id->PID = PID;
return PID;
} else {
CAMERA_LOGI(TAG"Mismatch PID=0x%x", PID);
}
}
return 0;
}
int sc101iot_init(sensor_t *sensor)
{
// Set function pointers
sensor->reset = reset;
sensor->init_status = init_status;
sensor->set_pixformat = set_pixformat;
sensor->set_framesize = set_framesize;
sensor->set_hmirror = set_hmirror;
sensor->set_vflip = set_vflip;
sensor->set_colorbar = set_colorbar;
sensor->set_raw_gma = set_raw_gma;
sensor->set_sharpness = set_sharpness;
sensor->set_agc_gain = set_agc_gain;
sensor->set_aec_value = set_aec_value;
sensor->set_awb_gain = set_awb_gain;
sensor->set_saturation= set_saturation;
sensor->set_contrast = set_contrast;
sensor->set_denoise = set_dummy;
sensor->set_quality = set_dummy;
sensor->set_special_effect = set_dummy;
sensor->set_wb_mode = set_dummy;
sensor->set_ae_level = set_dummy;
sensor->get_reg = get_reg;
sensor->set_reg = set_reg;
sensor->set_xclk = set_xclk;
CAMERA_LOGD(TAG"sc101iot Attached");
return 0;
}

View File

@@ -0,0 +1,18 @@
# LISA Device Framework
#
# Copyright (c) 2025, LISTENAI
# SPDX-License-Identifier: Apache-2.0
if(CONFIG_LISA_DEVICE)
listenai_library_named(lisa_device)
listenai_include_directories(.)
listenai_library_sources(
lisa_device.c
)
listenai_library_sources_ifdef(CONFIG_LISA_DEVICE_DEBUG lisa_device_debug.c)
endif() # CONFIG_LISA_DEVICE

Some files were not shown because too many files have changed in this diff Show More