应答2-发送文件和其它数据(send_data)

本文介绍了Rails框架中send_data与send_file方法的使用细节,包括如何发送二进制数据和文件内容给客户端,并设置了不同的选项来控制客户端的行为。
我们已经看了在“控制器”内提交模板(render)和发送字符串(render_to_string)。应答的第三个部分是发送数据(典型地,但不是必须的,是文件的内容)给客户端。

A、send_data 发送包含二进制数据的字符串给客户端。
send_data(data, options...)
发送一个数据流给客户端。典型地浏览器将结合使用内容类型与部署,两者在选项内设
置,来决定这个用数据做什么。
def sales_graph
png_data = Sales.plot_for(Date.today.month)
send_data(png_data, :type => "image/png", :disposition =>
"inline")
end
选项:
1、:filename 字符串, 在保存数据时,建议浏览器使用缺省的文件名。
2、:type 字符串, 内容类型,缺省为application/octet-stream。
3、:disposition 字符串, 建议浏览器应该使用内联的(选项inline)或者是下载的文
件并保存(缺省选项attachment)。

B、send_file 发送文件的内容给客户端。
send_file(path, options...)
发送指定文件给客户端。方法设置Content-Length,Content-Type,Content-
Disposition,和Content-Transfer-Encodeing 头。
选项:
1、:filename 字符串, 当保存文件时,建议浏览器使用缺省文件名。如果没有设置,
缺省是文件名路径。
2、:type 字符串, 内容类型,缺省应用application/octet-stream。
3、:disposition 字符串,建议浏览器应该使用内联的(选项inline)或者是下载的文件
并保存(缺省选项attachment)。
4、:streaming true 或false ,如果是false, 则整个文件被读入服务器内存并发送
给客户端。否则,文件以:buffer_size 块来读并写给客户端。
You can set additional headers for either send_ method using the headers
attribute in the controller.
def send_secret_file
send_file("/files/secret_list")
headers["Content-Description"] = "Top secret"
end
#ifndef __SOFT_I2C_H #define __SOFT_I2C_H #include "stm32f10x.h" // I2C引脚定义 #define I2C_SCL_PORT GPIOC #define I2C_SDA_PORT GPIOC #define I2C_SCL_PIN GPIO_Pin_11 #define I2C_SDA_PIN GPIO_Pin_10 // 帧长度定义 #define CMD_FRAME_LEN 4 // 命令帧长度:01 AA 功能码 校验 #define DATA_FRAME_LEN 11 // 数据帧长度:01 AA + 8字节数据 + 校验 // 函数返回值定义 #define I2C_OK 0 #define I2C_ERR_HEADER 1 #define I2C_ERR_CHECKSUM 2 // 功能码定义 #define FUNC_CODE_ADC 0x01 #define FUNC_CODE_UART 0x02 // 快速设置SDA方向的宏 #define I2C_SDA_OUT() do { GPIO_InitTypeDef t; t.GPIO_Pin=I2C_SDA_PIN; t.GPIO_Mode=GPIO_Mode_Out_OD; t.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(I2C_SDA_PORT, &t); } while(0) #define I2C_SDA_IN() do { GPIO_InitTypeDef t; t.GPIO_Pin=I2C_SDA_PIN; t.GPIO_Mode=GPIO_Mode_IPU; t.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(I2C_SDA_PORT, &t); } while(0) // 快速设置引脚电平的宏 #define I2C_SCL_HIGH() GPIO_SetBits(I2C_SCL_PORT, I2C_SCL_PIN); I2C_delay() #define I2C_SCL_LOW() GPIO_ResetBits(I2C_SCL_PORT, I2C_SCL_PIN); I2C_delay() #define I2C_SDA_HIGH() GPIO_SetBits(I2C_SDA_PORT, I2C_SDA_PIN); I2C_delay() #define I2C_SDA_LOW() GPIO_ResetBits(I2C_SDA_PORT, I2C_SDA_PIN); I2C_delay() #define I2C_SDA_READ() (GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_PIN) == Bit_SET) // 函数声明 void I2C_Init(void); void I2C_Start(void); void I2C_Stop(void); void I2C_SendByte(uint8_t data); uint8_t I2C_ReceiveByte(void); uint8_t Calculate_XOR_Checksum(uint8_t *data, uint8_t length); uint8_t Get_Function_Code(void); void Prepare_Command_Frame(uint8_t func_code); uint8_t I2C_Send_Command(void); uint8_t I2C_Receive_DataFrame(void); // 全局缓冲区 extern uint8_t i2c_cmd_buf[CMD_FRAME_LEN]; extern uint8_t i2c_data_buf[DATA_FRAME_LEN]; #endif #include "soft_i2c.h" #include "delay.h" #include <stdio.h> // 全局缓冲区 uint8_t i2c_cmd_buf[CMD_FRAME_LEN] = {0}; // 命令帧缓冲区 uint8_t i2c_data_buf[DATA_FRAME_LEN] = {0}; // 数据帧缓冲区 // 初始化I2C引脚 void I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能端口时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 配置SCL引脚为推挽输出 GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(I2C_SCL_PORT, &GPIO_InitStructure); // 配置SDA引脚为开漏输出 GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure); // 初始状态:SCLSDA都为高电平 I2C_SCL_HIGH(); I2C_SDA_HIGH(); } // 发送起始信号:SDA在SCL高电平时从高到低跳变 void I2C_Start(void) { I2C_SDA_OUT(); // 确保初始状态为高电平 I2C_SDA_HIGH(); I2C_SCL_HIGH(); I2C_delay(); // SDA下降沿产生起始信号 I2C_SDA_LOW(); I2C_delay(); // 拉低SCL,准备发送数据 I2C_SCL_LOW(); I2C_delay(); } // 发送停止信号:SDA在SCL高电平时从低到高跳变 void I2C_Stop(void) { I2C_SDA_OUT(); // 确保SCL为低电平 I2C_SCL_LOW(); I2C_delay(); // 确保SDA为低电平 I2C_SDA_LOW(); I2C_delay(); // 拉高SCL I2C_SCL_HIGH(); I2C_delay(); // SDA上升沿产生停止信号 I2C_SDA_HIGH(); I2C_delay(); } // 发送一个字节(从机在SCL下降沿采样数据) void I2C_SendByte(uint8_t data) { uint8_t i; I2C_SDA_OUT(); I2C_SCL_LOW(); // 确保SCL为低,准备发送数据 for(i = 0; i < 8; i++) { // 设置数据位(高位在前) if(data & 0x80) { I2C_SDA_HIGH(); } else I2C_SDA_LOW(); data <<= 1; I2C_delay(); // 拉高SCL,从机在下降沿采样,这里先保持高电平 I2C_SCL_HIGH(); I2C_delay(); // 拉低SCL,准备下一位数据(下降沿) I2C_SCL_LOW(); I2C_delay(); } } // 接收一个字节(从机在SCL上升沿输出数据) uint8_t I2C_ReceiveByte(void) { uint8_t i; uint8_t data = 0; I2C_SDA_IN(); // 设置SDA为输入 I2C_SCL_LOW(); // 确保SCL为低 for(i = 0; i < 8; i++) { data <<= 1; // 左移一位,准备接收新数据 // 拉高SCL,从机在上升沿输出数据 I2C_SCL_HIGH(); I2C_delay(); // 读取数据(高位在前) if(I2C_SDA_READ()) data |= 0x01; // 拉低SCL,准备下一位接收 I2C_SCL_LOW(); I2C_delay(); } return data; } // 计算XOR校验 uint8_t Calculate_XOR_Checksum(uint8_t *data, uint8_t length) { uint8_t checksum = 0; while(length--) checksum ^= *data++; return checksum; } // 读取PE9引脚电平获取功能码 uint8_t Get_Function_Code(void) { // 初始化PE9为输入(如果未在其他地方初始化) static uint8_t pe9_initialized = 0; if(!pe9_initialized) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOE, &GPIO_InitStructure); pe9_initialized = 1; } return (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_9) == Bit_SET) ? FUNC_CODE_ADC : FUNC_CODE_UART; } // 准备命令帧:[0x01, 0xAA, 功能码, 校验] void Prepare_Command_Frame(uint8_t func_code) { i2c_cmd_buf[0] = 0x01; // 帧头1 i2c_cmd_buf[1] = 0xAA; // 帧头2 i2c_cmd_buf[2] = func_code; // 功能码 // 计算前3字节的校验 i2c_cmd_buf[3] = Calculate_XOR_Checksum(i2c_cmd_buf, 3); } // 发送命令帧 uint8_t I2C_Send_Command(void) { uint8_t i; uint8_t func_code = Get_Function_Code(); // 准备命令帧 Prepare_Command_Frame(func_code); // 发送起始信号 I2C_Start(); // 发送命令帧所有字节(无应答) for(i = 0; i < CMD_FRAME_LEN; i++) { I2C_SendByte(i2c_cmd_buf[i]); } // 发送停止信号 I2C_Stop(); // 打印发送的命令帧 printf("发送命令帧: "); for(i = 0; i < CMD_FRAME_LEN; i++) { printf("0x%02X ", i2c_cmd_buf[i]); } printf("\n"); return I2C_OK; } // 接收数据帧 uint8_t I2C_Receive_DataFrame(void) { uint8_t i; uint8_t received_checksum, calculated_checksum; // 发送起始信号 I2C_Start(); // 接收数据帧所有字节(无应答) for(i = 0; i < DATA_FRAME_LEN; i++) { i2c_data_buf[i] = I2C_ReceiveByte(); } // 发送停止信号 I2C_Stop(); // 验证帧头 if(i2c_data_buf[0] != 0x01 || i2c_data_buf[1] != 0xAA) { printf("帧头错误: 0x%02X 0x%02X\n", i2c_data_buf[0], i2c_data_buf[1]); return I2C_ERR_HEADER; } // 验证校验(前10字节计算,第11字节是校验) received_checksum = i2c_data_buf[DATA_FRAME_LEN - 1]; calculated_checksum = Calculate_XOR_Checksum(i2c_data_buf, DATA_FRAME_LEN - 1); if(received_checksum != calculated_checksum) { printf("校验错误: 接收0x%02X, 计算0x%02X\n", received_checksum, calculated_checksum); return I2C_ERR_CHECKSUM; } // 打印接收的数据帧 printf("接收数据: "); for(i = 0; i < DATA_FRAME_LEN; i++) { printf("0x%02X ", i2c_data_buf[i]); } printf("\n"); return I2C_OK; } while(1) { switch(comm_state) { case I2C_STATE_SEND_CMD: // 发送命令帧 if(I2C_Send_Command() == I2C_OK) { printf("命令帧发送成功,等待从机准备数据...\n"); comm_state = I2C_STATE_WAIT_DATA; wait_counter = 0; } else { printf("命令帧发送失败,重试...\n"); Delay_Ms(50); // 短暂延时后重试 } break; case I2C_STATE_WAIT_DATA: // 等待从机准备数据,根据实际情况调整等待时间 if(wait_counter < WAIT_TICKS) { wait_counter++; Delay_Ms(1); // 1ms延时 } else { // 等待时间到,切换到接收状态 comm_state = I2C_STATE_RECEIVE_DATA; } break; case I2C_STATE_RECEIVE_DATA: // 接收数据帧 switch(I2C_Receive_DataFrame()) { case I2C_OK: printf("数据帧接收成功\n"); // 这里可以添加数据处理代码 // 例如:ProcessReceivedData(i2c_data_buf); break; case I2C_ERR_HEADER: printf("数据帧头错误\n"); break; case I2C_ERR_CHECKSUM: printf("数据帧校验错误\n"); break; default: printf("数据接收未知错误\n"); break; } // 完成一次通信,切换回发送命令状态,准备下一轮通信 // 可根据需要添加通信间隔延时 Delay_Ms(100); // 100ms通信间隔 comm_state = I2C_STATE_SEND_CMD; break; default: comm_state = I2C_STATE_SEND_CMD; break; } }检查代码逻辑,再将代码运行过程进行解释
最新发布
09-23
BLE Mesh 消息可靠传输实现指南 根据您提供的文档信息,我来详细解释如何实现消息不丢包如何发送应答消息。 1. 实现消息传输不丢包(有应答消息) 要实现可靠传输不丢包,需要使用有应答的消息机制。以下是具体实现方法: 1.1 配置超时时间 首先在 menuconfig 中配置默认超时时间: ``` Component config → Bluetooth Mesh → Client model message timeout (seconds) ``` 或者直接修改 sdkconfig: ```c CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT=10 # 设置为10秒超时 ``` 1.2 发送应答消息 ```c /** * @brief 发送应答的厂商消息(确保可靠传输) * * @param server_addr 目标地址 * @param data 发送数据 * @param length 数据长度 * @param opcode 操作码 * @param timeout_ms 自定义超时时间(毫秒),0表示使用默认值 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_send_reliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode, uint32_t timeout_ms) { esp_ble_mesh_msg_ctx_t ctx = {0}; esp_err_t err; // 设置消息上下文 ctx.net_idx = gStore.net_idx; ctx.app_idx = gStore.app_idx; ctx.addr = server_addr; ctx.send_ttl = MSG_SEND_TTL; ctx.send_rel = true; // 要求可靠传输 if (opcode == 0) { opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND; } // 转换超时时间(毫秒到毫秒,但API可能需要不同的单位) int msg_timeout = (timeout_ms > 0) ? (timeout_ms / 1000) : 0; ESP_LOGI(BLE_MESH, "Sending reliable message to 0x%04x, timeout: %d ms", server_addr, (timeout_ms > 0) ? timeout_ms : CONFIG_BLE_MESH_CLIENT_MSG_TIMEOUT * 1000); // 发送应答的消息(need_rsp = true) err = esp_ble_mesh_client_model_send_msg(vendor_client.model, &ctx, opcode, length, data, msg_timeout, // 超时时间 true, // need_rsp = true 要求应答 MSG_ROLE); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Failed to send reliable message: %s", esp_err_to_name(err)); } return err; } ``` 1.3 处理应答超时事件 需要在Mesh事件回调中处理应答超时: ```c static void mesh_event_cb(esp_ble_mesh_cb_event_t event, esp_ble_mesh_cb_param_t *param) { switch (event) { case ESP_BLE_MESH_CLIENT_MODEL_SEND_TIMEOUT_EVT: // 消息发送超时处理 ESP_LOGE(BLE_MESH, "Message timeout, opcode: 0x%06x, addr: 0x%04x", param->client_recv_timeout.opcode, param->client_recv_timeout.ctx->addr); // 这里可以触发重试机制 break; case ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT: // 收到应答处理 ESP_LOGI(BLE_MESH, "Received response, opcode: 0x%06x, addr: 0x%04x", param->client_recv_rsp.opcode, param->client_recv_rsp.ctx->addr); // 处理应答数据 ESP_LOG_BUFFER_HEX_LEVEL(BLE_MESH, param->client_recv_rsp.params, param->client_recv_rsp.length, ESP_LOG_DEBUG); break; // 其他事件处理... } } ``` 1.4 带自动重试的可靠发送函数 ```c /** * @brief 带自动重试的可靠消息发送 */ esp_err_t example_ble_mesh_send_with_retry(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode, uint8_t max_retries, uint32_t retry_delay_ms) { esp_err_t err; uint8_t retry_count = 0; do { err = example_ble_mesh_send_reliable_message(server_addr, data, length, opcode, 5000); if (err == ESP_OK) { // 发送成功,等待应答或超时事件 // 这里需要实现一个等待机制,比如使用信号量或消息队列 // 等待 ESP_BLE_MESH_CLIENT_MODEL_RECV_RSP_EVT 事件 ESP_LOGI(BLE_MESH, "Message sent successfully, waiting for response..."); return ESP_OK; } retry_count++; if (retry_count <= max_retries) { ESP_LOGW(BLE_MESH, "Send failed, retry %d/%d after %d ms", retry_count, max_retries, retry_delay_ms); vTaskDelay(pdMS_TO_TICKS(retry_delay_ms)); } } while (retry_count <= max_retries); ESP_LOGE(BLE_MESH, "All retries failed for message to 0x%04x", server_addr); return err; } ``` 1. 发送应答的消息 2.1 客户端模型发送应答消息 ```c /** * @brief 发送应答的厂商消息(快速,不保证可靠) * * @param server_addr 目标地址 * @param data 发送数据 * @param length 数据长度 * @param opcode 操作码 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_send_unreliable_message(uint16_t server_addr, uint8_t *data, uint16_t length, uint32_t opcode) { esp_ble_mesh_msg_ctx_t ctx = {0}; esp_err_t err; // 设置消息上下文 ctx.net_idx = gStore.net_idx; ctx.app_idx = gStore.app_idx; ctx.addr = server_addr; ctx.send_ttl = MSG_SEND_TTL; ctx.send_rel = false; // 不要求可靠传输 if (opcode == 0) { opcode = ESP_BLE_MESH_VND_MODEL_OP_SEND; } ESP_LOGI(BLE_MESH, "Sending unreliable message to 0x%04x", server_addr); // 发送应答的消息(need_rsp = false) err = esp_ble_mesh_client_model_send_msg(vendor_client.model, &ctx, opcode, length, data, 0, // 超时时间设为0 false, // need_rsp = false 不要求应答 MSG_ROLE); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Failed to send unreliable message: %s", esp_err_to_name(err)); } else { ESP_LOGD(BLE_MESH, "Unreliable message sent successfully"); } return err; } ``` 2.2 服务器模型发送消息(总是无应答) ```c /** * @brief 服务器模型发送消息(总是无应答) * * 服务器模型发送的消息总是无应答的,适用于状态通知等场景 * * @param model 服务器模型指针 * @param ctx 消息上下文 * @param opcode 操作码 * @param data 发送数据 * @param length 数据长度 * @return esp_err_t 发送结果 */ esp_err_t example_ble_mesh_server_send_message(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, uint32_t opcode, uint8_t *data, uint16_t length) { esp_err_t err; if (model == NULL || ctx == NULL || data == NULL || length == 0) { return ESP_ERR_INVALID_ARG; } ESP_LOGI(BLE_MESH, "Server sending message to 0x%04x", ctx->addr); // 服务器模型发送消息(总是无应答) err = esp_ble_mesh_server_model_send_msg(model, ctx, opcode, length, data); if (err != ESP_OK) { ESP_LOGE(BLE_MESH, "Server failed to send message: %s", esp_err_to_name(err)); } else { ESP_LOGD(BLE_MESH, "Server message sent successfully"); } return err; } ``` 1. 完整的使用示例 ```c // 初始化Mesh回调 esp_ble_mesh_register_prov_callback(mesh_event_cb); // 发送应答的可靠消息(不丢包) uint8_t important_data[] = {0x01, 0x02, 0x03}; esp_err_t err = example_ble_mesh_send_reliable_message(0x1001, important_data, sizeof(important_data), 0, 5000); if (err == ESP_OK) { ESP_LOGI(BLE_MESH, "Reliable message sent, waiting for response..."); } // 发送应答的快速消息 uint8_t status_data[] = {0xAA, 0xBB}; err = example_ble_mesh_send_unreliable_message(0x1002, status_data, sizeof(status_data), 0); if (err == ESP_OK) { ESP_LOGI(BLE_MESH, "Unreliable message sent successfully"); } // 服务器发送状态通知 esp_ble_mesh_msg_ctx_t ctx = { .net_idx = gStore.net_idx, .app_idx = gStore.app_idx, .addr = 0xFFFF, // 广播地址 .send_ttl = 3, .send_rel = false }; uint8_t notification_data[] = {0x55, 0x66}; err = example_ble_mesh_server_send_message(vendor_server_model, &ctx, 0x2001, notification_data, sizeof(notification_data)); ``` 1. 选择策略建议 2. 使用有应答消息的场景: · 关键控制指令(开关、调节等) · 需要确认执行结果的操作 · 数据传输完整性要求高的场景 3. 使用无应答消息的场景: · 状态通知心跳包 · 实时传感器数据(可以容忍偶尔丢失) · 广播消息多播消息 · 对延迟敏感的应用 4. 超时时间设置建议: · 局域网内:2-5秒 · 多跳网络:5-10秒 · 高延迟网络:10-30秒 这样的实现可以确保在需要可靠性的场景下不丢包,同时在追求效率的场景下快速发送消息。 能够解决节点“ignoring old seqauth" 指南中api貌似不支持序列号持久化管理
09-02
module SCCB_CTRL ( input clk , //100M input rst_n , output scl , output XCLK , inout sda // input initial_start , // output cam_rst, //摄像头复位信号 (低电平有效) // output cam_pwdn , //摄像头功耗控制 (0:正常工作, 1:休眠模式) // output work_done //SCCB读写完成信号 ); //100M `define SCL_POSEDGE (cnt == 16'd0) `define SCL_NEGEDGE (cnt == 16'd160) `define SCL_HIG_MID (cnt == 16'd80) `define SCL_LOW_MID (cnt == 16'd280) //50M // `define SCL_POSEDGE (cnt == 7'd0) // `define SCL_NEGEDGE (cnt == 7'd50) // `define SCL_HIG_MID (cnt == 7'd25) // `define SCL_LOW_MID (cnt == 7'd87) //时钟频率、寄存器地址的声明 parameter CLK_FRQ = 100_000_000 ; parameter CNT_3SEC=CLK_FRQ*3-1 ; parameter CNT_1SEC=CLK_FRQ-1 ; parameter slave_addr = 7'b0111_100; // assign cam_rst = 1; // assign cam_pwdn = 0; // wire wire initial_done; wire work_start; wire [23:0] initial_data; wire write_start_flag_wire; //使能信号 wire sda_in; //sda输入寄存器 //reg reg [7:0] data_tx ; //写入数据寄存器 reg [7:0] data_rx ; //读出数据寄存器S reg [7:0] w_slave_addr_buf; //从设备地址寄存器(地址存高7位,0位为写命令0) reg [7:0] r_slave_addr_buf; //从设备地址寄存器(地址存高7位,0位为读命令1) reg [7:0] H_byte_addr_buf; //8位存储器址位 reg [7:0] L_byte_addr_buf; //8位存储器址位 reg [5:0] state ; reg [16:0] cnt ; reg SCL_r ; reg XCLK_R ; reg sda_out ; reg SDA_en ; reg [3:0] write_byte_cnt ; reg [7:0] write_byte_reg ; reg config_done ; //完成信号 reg [31:0] delay_cnt ; reg [31:0] auto_read_write_cnt_reg; //计数器 reg wr_flag ; reg [25:0] wr_flag_cnt ; reg work_en ; //工作使能信号 assign XCLK = XCLK_R; assign scl = SCL_r; assign sda_in = sda; assign sda = SDA_en ? sda_out : 1'bz; // assign sda = SDA_en ? (sda_out ? 1'bz : 1'b0) : 1'bz; assign write_start_flag_wire=(auto_read_write_cnt_reg==CNT_1SEC)?1'b1:1'b0; // 寄存器表例化 // sccb_ov5640_table u_sccb_ov5640_table( // .clk(clk), // .rst_n(rst_n), // .initial_start(initial_start), // .work_done(work_done), // .work_start(work_start), // .initial_data(initial_data), // .initial_done(initial_done) // ); //数据复位、开始工作时寄存数据(避免传输中途数据不稳定) always @(posedge clk or negedge rst_n) begin if (!rst_n) begin w_slave_addr_buf <= 8'b0000_0000;//0位为写命令0 r_slave_addr_buf <= 8'b0000_0001;//0位为读命令1 H_byte_addr_buf <= 8'b0; L_byte_addr_buf <= 8'b0; data_tx <= 8'b0; end else if (wr_flag) begin w_slave_addr_buf [7:1] <= slave_addr; //地址存高7位 r_slave_addr_buf [7:1] <= slave_addr; //地址存高7位 data_tx <= 8'b0011_0001; H_byte_addr_buf <= 8'b0100_0011;//存储器地址 L_byte_addr_buf <= 8'b0000_0000;//存储器地址 // data_tx <= initial_data[7:0]; // H_byte_addr_buf <= initial_data[23:16];//存储器地址 // L_byte_addr_buf <= initial_data[15:8];//存储器地址 end end //状态机定时功能 always@(posedge clk or negedge rst_n)begin if(!rst_n)begin auto_read_write_cnt_reg<='d0; end else begin if(auto_read_write_cnt_reg<CNT_3SEC)begin auto_read_write_cnt_reg<=auto_read_write_cnt_reg+'d1; end else begin auto_read_write_cnt_reg<='b0; end end end reg [16:0] cnt_xclk; always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt_xclk <= 0; else begin if(cnt_xclk == 16'd3)//4000 / 20 cnt_xclk <= 0; else cnt_xclk <= cnt_xclk + 1'b1; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) XCLK_R <= 0; else begin if(cnt_xclk == 16'd0) XCLK_R <= 1'b1; else if(cnt == 16'd1) XCLK_R <= 1'b0; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 0; else begin if(cnt == 16'd399)//4000 / 20 cnt <= 0; else cnt <= cnt + 1'b1; end end always @(posedge clk or negedge rst_n) begin if(!rst_n) SCL_r <= 0; else begin if(cnt == 16'd0) SCL_r <= 1'b1; else if(cnt == 16'd160) SCL_r <= 1'b0; end end //定义的状态机状态 parameter IDLE = 6'd0 ; parameter STRAT_W = 6'd1 ; parameter WRITE_SLAVE_ADDR = 6'd2 ; parameter ACK_1 = 6'd3 ; parameter SEND_CTRL_BYTE_M = 6'd4 ; parameter ACK_2 = 6'd5 ; parameter SEND_CTRL_BYTE_L = 6'd6 ; // parameter ACK_3 = 6'd7 ; // parameter SEND_DATA = 6'd8 ; parameter ACK_4 = 6'd7 ; parameter STOP_W = 6'd8 ; // parameter STRAT_R1 = 6'd11 ; // parameter SEND_CTRL_BYTE_R1 = 6'd12 ; // parameter ACK_5 = 6'd13 ; // parameter CONTROL_BYTE_ADDR_M = 6'd14 ; // parameter ACK_6 = 6'd15 ; // parameter CONTROL_BYTE_ADDR_L = 6'd16 ; // parameter ACK_7 = 6'd17 ; // parameter STOP_R1 = 6'd18 ; parameter STRAT_R2 = 6'd9 ; parameter SEND_CTRL_BYTE_R2 = 6'd10 ; parameter ACK_8 = 6'd11 ; parameter RECV_DATA = 6'd12 ; parameter NACK = 6'd13 ; parameter WAIT = 6'd14 ; parameter STOP_R2 = 6'd15 ; //开始信号wr_flag always @(posedge clk or negedge rst_n) begin if(!rst_n) begin wr_flag_cnt <= 0; wr_flag <= 0; end else begin if(wr_flag_cnt < 26'd10_000_000) begin // 100ms延迟 wr_flag_cnt <= wr_flag_cnt + 1'b1; wr_flag <= 1'b0; end else begin wr_flag_cnt <= 26'd5_000_000; // 保持计数值 wr_flag <= 1'b1; // 持续触发,或仅触发一次 end end end // always @(posedge clk or negedge rst_n) begin // if(!rst_n) begin // wr_flag_cnt <= 0; // wr_flag <= 0; // end // else begin // if(wr_flag_cnt[25]) begin // wr_flag_cnt <= 0; // wr_flag <= 1'b1; // end // else if(wr_flag_cnt<='d167796) begin // wr_flag_cnt <= wr_flag_cnt + 1'b1; // wr_flag <= wr_flag; // end // else begin // wr_flag_cnt <= wr_flag_cnt + 1'b1; // wr_flag <= 1'b0; // end // end // end always @(posedge clk or negedge rst_n) begin if(!rst_n) begin state <= IDLE; write_byte_cnt <= 0; write_byte_reg <= 0; SDA_en <= 1'b0; sda_out <= 1'b1; work_en <= 1'b0; config_done <= 1'b0; //wr_reg <= 0; end // else if(wr_flag) begin // state <= IDLE; // write_byte_cnt <= 0; // write_byte_reg <= 0; // SDA_en <= 1'b0; // config_done <= 1'b0; // end else begin case(state) //---------------------空闲----------------------// IDLE: begin //0 config_done <= 1'b0; SDA_en <= 1'b0; sda_out <= 1'b1; if(wr_flag)begin work_en <= 1'b1; state <= STRAT_W; end // else // state <= IDLE; end //--------------------写起始位--------------------// STRAT_W : begin SDA_en <= 1'b1; //拉高sda if(`SCL_HIG_MID) begin sda_out <= 0; write_byte_cnt <= 0; state <= WRITE_SLAVE_ADDR; end // else begin // state <= STRAT_W; // end end //--------------------写器件地址--------------------// WRITE_SLAVE_ADDR: begin SDA_en <= 1'b1; if(`SCL_LOW_MID) begin if(write_byte_cnt != 4'd8) begin sda_out <= w_slave_addr_buf[7-write_byte_cnt];//sda输出设备地址 write_byte_cnt <= write_byte_cnt + 4'd1; end else begin write_byte_cnt <= 4'd0; state <= ACK_1; end end end //--------------------应答1--------------------// ACK_1: begin SDA_en <= 1'b0; if(`SCL_NEGEDGE) state <= SEND_CTRL_BYTE_M; // else // state <= ACK_1; end //--------------------存储器地址高8位--------------------// SEND_CTRL_BYTE_M: begin SDA_en <= 1'b1; if(`SCL_LOW_MID) begin if(write_byte_cnt != 4'd8)begin sda_out <= H_byte_addr_buf[7-write_byte_cnt];//sda输出字节地址(从高到低) write_byte_cnt <= write_byte_cnt + 4'd1; end else begin write_byte_cnt <= 4'd0; state <= ACK_2; end end end //--------------------应答2--------------------// ACK_2: begin SDA_en <= 1'b0; if(`SCL_NEGEDGE) begin state <= SEND_CTRL_BYTE_L; end // else // state <= ACK_2; end //--------------------存储器地址低8位--------------------// SEND_CTRL_BYTE_L: begin SDA_en <= 1'b1; if(`SCL_LOW_MID) begin if(write_byte_cnt != 4'd8)begin sda_out <= L_byte_addr_buf[7-write_byte_cnt];//sda输出字节地址(从高到低) write_byte_cnt <= write_byte_cnt + 1'b1; end else begin write_byte_cnt <= 4'd0; state <= ACK_4; end end end // //--------------------应答3--------------------// // ACK_3: begin // SDA_en <= 1'b0; // if(`SCL_NEGEDGE) begin // state <= SEND_DATA; // end // else // state <= ACK_3; // end // //--------------------数据--------------------// // SEND_DATA: begin // SDA_en <= 1'b1; // if(`SCL_LOW_MID) begin // if(write_byte_cnt != 4'd8)begin // sda_out <= data_tx[7-write_byte_cnt];//sda输出字节地址(从高到低) // write_byte_cnt <= write_byte_cnt + 1'b1; // end else begin // state <= ACK_4; // write_byte_cnt <= 4'd0; // end // end // end //--------------------应答4--------------------// ACK_4: begin SDA_en <= 1'b0; if(`SCL_NEGEDGE) begin state <= STOP_W; end else state <= ACK_4; end //--------------------写停止--------------------// STOP_W: begin SDA_en <= 1'b1; // sda_out <= 1'b1; // if(delay_cnt ==32'd250_000) begin // state <= STRAT_R1; // delay_cnt <= 0; // end if(`SCL_HIG_MID) begin sda_out <= 1'b1; state <= STRAT_R2; end // else // delay_cnt <= delay_cnt + 1'b1; end // //--------------------读开始--------------------// // STRAT_R1 : begin // SDA_en <= 1'b1; // if(`SCL_HIG_MID) begin // sda_out <= 0; // state <= SEND_CTRL_BYTE_R1; // end // else // state <= STRAT_R1; // end // //--------------------读器件地址--------------------// // SEND_CTRL_BYTE_R1: begin // SDA_en <= 1'b1; // if(`SCL_LOW_MID) begin // if(write_byte_cnt != 4'd8) begin // sda_out <= w_slave_addr_buf[7-write_byte_cnt];//sda输出设备地址 // write_byte_cnt <= write_byte_cnt + 1'b1; // end else begin // state <= ACK_5; // write_byte_cnt <= 4'd0; // end // end // end // //--------------------应答5--------------------// // ACK_5: begin // SDA_en <= 1'b0; // if(`SCL_NEGEDGE) begin // state <= CONTROL_BYTE_ADDR_M; // end // else // state <= ACK_5; // end // //--------------------读存储器高8--------------------// // CONTROL_BYTE_ADDR_M: begin // SDA_en <= 1'b1; // if(`SCL_LOW_MID) begin // if(write_byte_cnt != 4'd8)begin // sda_out <= H_byte_addr_buf[7-write_byte_cnt];//sda输出字节地址(从高到低) // write_byte_cnt <= write_byte_cnt + 1'b1; // end else begin // state <= ACK_6; // write_byte_cnt <= 4'd0; // end // end // end // //--------------------应答6--------------------// // ACK_6: begin // SDA_en <= 1'b0; // if(`SCL_NEGEDGE) begin // state <= CONTROL_BYTE_ADDR_L; // end // else // state <= ACK_6; // end // //--------------------读存储器低8--------------------// // CONTROL_BYTE_ADDR_L: begin // SDA_en <= 1'b1; // if(`SCL_LOW_MID) begin // if(write_byte_cnt != 4'd8)begin // sda_out <= L_byte_addr_buf[7-write_byte_cnt];//sda输出字节地址(从高到低) // write_byte_cnt <= write_byte_cnt + 1'b1; // end else begin // state <= ACK_7; // write_byte_cnt <= 4'd0; // end // end // end // //--------------------应答7--------------------// // ACK_7: begin // SDA_en <= 1'b0; // if(`SCL_NEGEDGE) begin // state <= STOP_R1; // end // else // state <= ACK_7; // end // //--------------------读停止1--------------------// // STOP_R1: begin //7 // SDA_en <= 1'b1; // if(`SCL_HIG_MID) begin // sda_out <= 1'b1; // state <= STRAT_R2; // end // else // state <= STOP_R1; // end //--------------------读开始2--------------------// STRAT_R2 : begin //1 SDA_en <= 1'b1; if(`SCL_HIG_MID) begin sda_out <= 1'b0; state <= SEND_CTRL_BYTE_R2; end // else // state <= STRAT_R2; end //--------------------读器件地址--------------------// SEND_CTRL_BYTE_R2: begin SDA_en <= 1'b1; if(`SCL_LOW_MID) begin if(write_byte_cnt != 4'd8) begin sda_out <= r_slave_addr_buf[7-write_byte_cnt];//sda输出设备地址 write_byte_cnt <= write_byte_cnt + 4'd1; end else begin write_byte_cnt <= 4'd0; state <= ACK_8; end end end //--------------------应答8--------------------// ACK_8: begin SDA_en <= 1'b0; if(`SCL_NEGEDGE) begin state <= RECV_DATA; end // else // state <= ACK_8; end //--------------------接收数据--------------------// RECV_DATA: begin SDA_en <= 1'b0; if(`SCL_HIG_MID)begin if(write_byte_cnt != 4'd8)begin data_rx[7-write_byte_cnt] <= sda_in; write_byte_cnt <= write_byte_cnt + 4'd1; end if(`SCL_HIG_MID && write_byte_cnt == 4'd8) begin state <= NACK; write_byte_cnt <= 4'd0; end end end //--------------------应答--------------------// NACK: begin SDA_en <= 1'b1; if(`SCL_LOW_MID) begin sda_out <= 1'b1; state <= WAIT; end // else // state <= NACK; end //--------------------等待--------------------// WAIT: begin SDA_en <= 1'b0; if(`SCL_LOW_MID) begin sda_out <= 1'b0; state <= STOP_R2; end else state <= WAIT; end //--------------------读结束--------------------// STOP_R2: begin SDA_en <= 1'b1; if(`SCL_HIG_MID) begin config_done <= 1'b1; sda_out <= 1'b1; state <= IDLE; end end endcase end end ila_0 ila_0_debuge ( .clk(clk), // input wire clk .probe0({clk,rst_n,sda,SCL_r,state,wr_flag_cnt,wr_flag,config_done,write_byte_cnt,cnt,sda_in,sda_out,SDA_en,data_tx,data_rx,delay_cnt,work_en}) // input wire [31:0] probe0 ); endmodule 检查一下这段代码,为什么无法接受来自摄像头里寄存器的数据
08-28
这段代码是一个 SCCB(Serial Camera Control Bus)控制器的 Verilog 实现,用于与摄像头(如 OV5640)进行通信。你提到的问题是“无法接受来自摄像头里寄存器的数据”,我们可以从以下几个方面来分析问题: --- ### 1. **读取流程是否完整** SCCB 协议中,读取寄存器的流程是: - 发送 Start 信号 - 发送写地址(写模式) - 发送寄存器地址(高 + 低字节) - 发送 Start 信号(重复起始) - 发送读地址(读模式) - 接收数据 - 发送 Nack - 发送 Stop 信号 你当前的代码中: - 有写地址寄存器地址的发送流程(`STRAT_W`, `WRITE_SLAVE_ADDR`, `SEND_CTRL_BYTE_M`, `SEND_CTRL_BYTE_L`) - 也有读地址发送流程(`STRAT_R2`, `SEND_CTRL_BYTE_R2`, `ACK_8`, `RECV_DATA`) - 但 Nack Stop 的处理(`NACK`, `WAIT`, `STOP_R2`)逻辑可能存在问题 **问题点:** - 在 `RECV_DATA` 状态中,接收完 8 位数据后进入 `NACK` 状态,但 Nack 的处理是否正确? - `NACK` 状态中是否拉高 SDA(发送 Nack)? - `WAIT` 状态是否有必要?是否可能导致状态机卡住? - `STOP_R2` 是否正确发送 Stop 信号并回到 `IDLE`? --- ### 2. **SDA 输入是否正确采样** 在 `RECV_DATA` 状态中: ```verilog RECV_DATA: begin SDA_en <= 1'b0; if(`SCL_HIG_MID)begin if(write_byte_cnt != 4'd8)begin data_rx[7-write_byte_cnt] <= sda_in; write_byte_cnt <= write_byte_cnt + 4'd1; end if(`SCL_HIG_MID && write_byte_cnt == 4'd8) begin state <= NACK; write_byte_cnt <= 4'd0; end end end ``` **问题点:** - `sda_in` 是否正确采样?应确保在 SCL 高电平中间采样(即 `SCL_HIG_MID`),但当前逻辑可能存在问题: - 多重 `if(`SCL_HIG_MID)` 判断可能导致采样时机不准确 - `write_byte_cnt` 是否在 SCL 高电平中间递增? --- ### 3. **主时钟与 SCCB 时钟匹配问题** SCCB 的 SCL 频率通常为 100kHz 或 400kHz,而你的主时钟为 100MHz。 ```verilog `define SCL_POSEDGE (cnt == 16'd0) `define SCL_NEGEDGE (cnt == 16'd160) `define SCL_HIG_MID (cnt == 16'd80) `define SCL_LOW_MID (cnt == 16'd280) ``` **问题点:** - `cnt` 的最大值为 399,一个完整的 SCL 周期为 400 个主时钟周期(即 100MHz / 400 = 250kHz),这已经高于标准 SCCB 的最高频率(400kHz),是否摄像头支持这么高的频率? - 如果摄像头不支持高频,可能导致通信失败 --- ### 4. **I2C SCCB 的差异** SCCB 是 I2C 的子集,但 SCCB 不支持连续读写、时序更严格。 **问题点:** - 是否摄像头要求某些固定延时或特定时序? - 是否在发送完寄存器地址后有足够延时才开始读操作? - 是否遗漏了某些必要的延时(如 `delay_cnt` 被注释掉了)? --- ### 5. **调试建议** 1. 使用 ILA(你已经例化了 `ila_0`)观察以下信号: - `sda`, `scl`, `state`, `write_byte_cnt`, `data_rx` - 检查是否成功进入 `RECV_DATA` 状态 - 检查 `data_rx` 是否有数据变化 2. 使用示波器/逻辑分析仪抓取 SCCB 信号,确认 SDA SCL 波形是否符合规范 3. 确保摄像头已正确上电并处于正常工作模式(`cam_pwdn`, `cam_rst` 是否正确控制) --- ### 6. **建议修改点** - 优化 `RECV_DATA` 状态的采样逻辑,确保在 SCL 高电平中间采样 - 确保 `NACK` 正确发出(SDA 拉高) - 在 `STOP_R2` 中确保 Stop 信号正确发出 - 降低 SCCB 时钟频率(如 100kHz)以确保兼容性 --- ### 示例修正代码(RECV_DATA 状态): ```verilog RECV_DATA: begin SDA_en <= 1'b0; // 输入模式 if(`SCL_HIG_MID) begin if(write_byte_cnt < 8) begin data_rx[7 - write_byte_cnt] <= sda_in; // 采样 SDA write_byte_cnt <= write_byte_cnt + 1; end else begin state <= NACK; write_byte_cnt <= 0; end end end ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值