404 lines
14 KiB
C
404 lines
14 KiB
C
/**
|
||
* @file ips.c
|
||
* @brief
|
||
*
|
||
* Copyright (c) 2021 Bouffalolab team
|
||
*
|
||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||
* contributor license agreements. See the NOTICE file distributed with
|
||
* this work for additional information regarding copyright ownership. The
|
||
* ASF licenses this file to you 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 "drv_mmheap.h"
|
||
#include "hal_uart.h"
|
||
#include "hal_dma.h"
|
||
#include "data_protocol.h"
|
||
|
||
struct device *uart;
|
||
static isp_obj_t *isp_obj_uart;
|
||
|
||
void ips_obj_init(isp_obj_t *isp_obj_p)
|
||
{
|
||
if (!isp_obj_p)
|
||
return;
|
||
isp_obj_p->cmd_id = 0;
|
||
isp_obj_p->check = 0;
|
||
isp_obj_p->length = 0;
|
||
isp_obj_p->file_type = 0;
|
||
isp_obj_p->auot_ack = 1;
|
||
isp_obj_p->status.isp_state_mode = 0;
|
||
isp_obj_p->status.already_steps = 0;
|
||
}
|
||
|
||
/**
|
||
* @brief uart tx dma irq, ch0
|
||
*
|
||
* @param dev
|
||
* @param args
|
||
* @param size
|
||
* @param state
|
||
*/
|
||
void dma_ch0_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
|
||
{
|
||
if (isp_obj_uart == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* close dma and dma irq */
|
||
device_control(uart, DEVICE_CTRL_TX_DMA_SUSPEND, NULL);
|
||
device_control(UART_DEV(uart)->tx_dma, DEVICE_CTRL_CLR_INT, NULL);
|
||
|
||
if (isp_obj_uart->status.isp_state_mode == SEND_DATA) {
|
||
isp_obj_uart->status.isp_state_mode = SEND_WAIT_ACK;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_SEND_ACK_WAIT);
|
||
}
|
||
}
|
||
|
||
// void dma_ch1_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
|
||
// {
|
||
// if (isp_obj_uart == NULL) {
|
||
// return;
|
||
// }
|
||
|
||
// if (isp_obj_uart->status.isp_state_mode == RECEIVE_DATA) {
|
||
// isp_obj_uart->status.isp_state_mode = RECEIVE_WAIT_ACK;
|
||
// isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_RECEIVE_ACK_WAIT);
|
||
// }
|
||
// device_control(uart, DEVICE_CTRL_RX_DMA_SUSPEND, 0);
|
||
// isp_obj_uart->status.already_steps = 0;
|
||
// device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT));
|
||
|
||
// MSG("tx dma init\r\n");
|
||
// }
|
||
|
||
uint8_t isp_check(isp_obj_t *isp_obj)
|
||
{
|
||
if (isp_obj == NULL) {
|
||
return 0;
|
||
}
|
||
uint8_t check = isp_obj->cmd_id + (isp_obj->length & 0x00FF) + ((isp_obj->length >> 8) & 0x00FF);
|
||
|
||
if (isp_obj->length && isp_obj->file_data) {
|
||
for (uint16_t i = 0; i < isp_obj->length; i++) {
|
||
check += isp_obj->file_data[i];
|
||
}
|
||
}
|
||
return check;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @param isp_obj_uart
|
||
* @return int
|
||
*/
|
||
int isp_uart_send_data(isp_obj_t *isp_obj_uart)
|
||
{
|
||
if (isp_obj_uart == NULL || isp_obj_uart->cmd_id == 0) {
|
||
return -1;
|
||
}
|
||
|
||
if ((isp_obj_uart->cmd_id & 0xF0) != 0x50)
|
||
return -2;
|
||
|
||
if (isp_obj_uart->status.isp_state_mode != NO_TASK)
|
||
return -3;
|
||
|
||
isp_obj_uart->check = isp_check(isp_obj_uart);
|
||
|
||
/* use tx blocking */
|
||
device_write(uart, 0, (void *)isp_obj_uart, sizeof(isp_cmd_id_t) + sizeof(uint8_t) + sizeof(uint16_t) + sizeof(isp_file_type_t));
|
||
|
||
/* use DMA tx non-blocking */
|
||
device_control(uart, DEVICE_CTRL_TX_DMA_RESUME, NULL);
|
||
device_control(UART_DEV(uart)->tx_dma, DEVICE_CTRL_SET_INT, NULL);
|
||
device_write(uart, 0, isp_obj_uart->file_data, isp_obj_uart->length);
|
||
|
||
isp_obj_uart->status.already_steps = 0;
|
||
isp_obj_uart->status.isp_state_mode = SEND_DATA;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @param isp_obj_uart
|
||
* @return int
|
||
*/
|
||
|
||
int isp_uart_send_ack(isp_obj_t *isp_obj_uart, ips_reply_t ips_reply)
|
||
{
|
||
if (isp_obj_uart == NULL || isp_obj_uart->cmd_id == 0) {
|
||
return -1;
|
||
}
|
||
|
||
// if (isp_obj_uart->status.isp_state_mode != RECEIVE_WAIT_ACK) {
|
||
// return -2;
|
||
// }
|
||
|
||
/* use DMA tx blocking */
|
||
device_write(uart, 0, (uint8_t *)&ips_reply, 2);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
* @param isp_obj
|
||
* @param buff
|
||
* @param size
|
||
*/
|
||
void isp_task_handler(isp_obj_t *isp_obj, uint8_t *buff, uint32_t size)
|
||
{
|
||
static uint32_t time_last = 0;
|
||
|
||
uint32_t time_now = bflb_platform_get_time_ms();
|
||
|
||
/* debug information */
|
||
// MSG("hand:%d, len:%d, mode:%d\r\n", isp_obj_uart->status.already_steps, isp_obj_uart->status.receive_length, isp_obj_uart->status.isp_state_mode);
|
||
|
||
if (size == 0) {
|
||
if ((time_now - time_last > isp_obj->time_out) && isp_obj->time_out) {
|
||
/* time out */
|
||
}
|
||
}
|
||
|
||
for (uint32_t i = 0; i < size; i++) {
|
||
/* receive 状态机 */
|
||
switch (isp_obj_uart->status.already_steps) {
|
||
case 0:
|
||
if (isp_obj_uart->status.isp_state_mode == NO_TASK) {
|
||
isp_obj_uart->status.isp_state_mode = OTHER_STATE;
|
||
} else if (isp_obj_uart->status.isp_state_mode != SEND_WAIT_ACK) {
|
||
return;
|
||
}
|
||
|
||
isp_obj_uart->cmd_id = *buff;
|
||
isp_obj_uart->status.already_steps++;
|
||
|
||
break;
|
||
case 1:
|
||
isp_obj_uart->check = *buff;
|
||
isp_obj_uart->status.already_steps++;
|
||
|
||
if (isp_obj_uart->status.isp_state_mode == SEND_WAIT_ACK) {
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
if (isp_obj_uart->cmd_id == 0X4F && isp_obj_uart->check == 0X4B) {
|
||
isp_obj_uart->status.ips_reply = REPLY_SUCCES;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_SEND_SUCCES_ACK);
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
isp_obj_uart->status.already_steps = 0;
|
||
} else if (isp_obj_uart->cmd_id == 0X52 && isp_obj_uart->check == 0X45) {
|
||
isp_obj_uart->status.ips_reply = REPLY_ERROR;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_SEND_ERROR_ACK);
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
isp_obj_uart->status.already_steps = 0;
|
||
}
|
||
}
|
||
break;
|
||
case 2:
|
||
isp_obj_uart->length = *buff;
|
||
isp_obj_uart->status.already_steps++;
|
||
break;
|
||
case 3:
|
||
isp_obj_uart->length += *buff << 8;
|
||
isp_obj_uart->status.already_steps++;
|
||
break;
|
||
case 4: /* 接收的如果是命令,无法判断命令是简短命令(只需回复2个字节信息),还是复杂命令(需要回复一段数据包),需要自己在用户回调函数里处理,但这似乎不太合理 */
|
||
isp_obj_uart->file_type = *buff;
|
||
isp_obj_uart->status.already_steps++;
|
||
if ((isp_obj_uart->cmd_id & 0xF0) == 0x50) {
|
||
isp_obj_uart->status.isp_state_mode = RECEIVE_DATA;
|
||
isp_obj_uart->status.receive_length = 0;
|
||
} else {
|
||
isp_obj_uart->status.already_steps = 0;
|
||
/* 等待ACK的时候收到命令,同时代表 命令 与 成功ACK */
|
||
if (isp_obj_uart->status.isp_state_mode == SEND_WAIT_ACK) {
|
||
/* 响应 CMD 含义 */
|
||
isp_obj_uart->status.isp_state_mode = RECEIVE_WAIT_ACK;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_CMD_);
|
||
/* 响应 ACK 含义 */
|
||
isp_obj_uart->status.ips_reply = REPLY_SUCCES;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_SEND_SUCCES_ACK);
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
} else {
|
||
/* 正常空闲收到命令 */
|
||
isp_obj_uart->status.isp_state_mode = RECEIVE_WAIT_ACK;
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_CMD_);
|
||
}
|
||
//isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
}
|
||
|
||
break;
|
||
case 5:
|
||
if (isp_obj_uart->status.receive_length < isp_obj_uart->length) {
|
||
isp_obj_uart->file_data[isp_obj_uart->status.receive_length] = *buff;
|
||
isp_obj_uart->status.receive_length++;
|
||
}
|
||
if (isp_obj_uart->status.receive_length >= isp_obj_uart->length) {
|
||
isp_obj_uart->status.isp_state_mode = RECEIVE_WAIT_ACK;
|
||
uint8_t check_data = isp_check(isp_obj_uart);
|
||
//MSG("check:%X\r\n", check);
|
||
if (isp_obj_uart->check == check_data) {
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_RECEIVE_ACK_WAIT);
|
||
/* auot send ack */
|
||
if (isp_obj_uart->auot_ack == 0) {
|
||
isp_uart_send_ack(isp_obj_uart, REPLY_SUCCES);
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
}
|
||
} else {
|
||
isp_obj_uart->isp_callback(isp_obj_uart, ISP_CALLBACK_RECEIVE_NACK_WAIT);
|
||
/* auot send Nack */
|
||
if (isp_obj_uart->auot_ack == 0) {
|
||
isp_uart_send_ack(isp_obj_uart, REPLY_ERROR);
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
}
|
||
}
|
||
|
||
isp_obj_uart->status.receive_length = 0;
|
||
isp_obj_uart->status.already_steps = 0;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
buff++;
|
||
}
|
||
|
||
time_last = time_now;
|
||
}
|
||
|
||
/**
|
||
* @brief uart_irq_callback
|
||
*
|
||
* @param dev
|
||
* @param args
|
||
* @param size
|
||
* @param state
|
||
*/
|
||
void uart_irq_callback(struct device *dev, void *args, uint32_t size, uint32_t state)
|
||
{
|
||
static uint8_t fer_err_flag = 0;
|
||
if (state == UART_EVENT_RX_FIFO || state == UART_EVENT_RTO) {
|
||
if (isp_obj_uart == NULL) {
|
||
return;
|
||
}
|
||
|
||
/* Tx fifo overflow, send nack*/
|
||
if(fer_err_flag){
|
||
if(state == UART_EVENT_RX_FIFO){
|
||
|
||
}else if(state == UART_EVENT_RTO){
|
||
isp_obj_uart->status.isp_state_mode = NO_TASK;
|
||
isp_uart_send_ack(isp_obj_uart, REPLY_ERROR);
|
||
fer_err_flag = 0;
|
||
}
|
||
return;
|
||
}
|
||
|
||
isp_task_handler(isp_obj_uart, args, size);
|
||
}
|
||
else if(state == UART_EVENT_RX_FER){
|
||
/* Tx fifo overflow, baud rate should be reduced */
|
||
fer_err_flag = 1;
|
||
device_control(dev,DEVICE_CTRL_UART_CLEAR_RX_FIFO,NULL);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief
|
||
*
|
||
*/
|
||
uint8_t isp_uart_init(isp_obj_t *isp_obj)
|
||
{
|
||
if (isp_obj == NULL)
|
||
return 1;
|
||
|
||
uart = device_find("debug_log");
|
||
if(uart){
|
||
device_close(uart);
|
||
}else{
|
||
uart_register(UART0_INDEX, "debug_log");
|
||
uart = device_find("debug_log");
|
||
}
|
||
|
||
if (uart) {
|
||
device_close(uart);
|
||
UART_DEV(uart)->id = 0;
|
||
UART_DEV(uart)->baudrate = 3000000;
|
||
UART_DEV(uart)->databits = UART_DATA_LEN_8;
|
||
UART_DEV(uart)->stopbits = UART_STOP_ONE;
|
||
UART_DEV(uart)->parity = UART_PAR_NONE;
|
||
UART_DEV(uart)->fifo_threshold = 4;
|
||
|
||
device_close(uart);
|
||
device_open(uart, DEVICE_OFLAG_DMA_TX | DEVICE_OFLAG_INT_RX);
|
||
bflb_platform_delay_ms(5);
|
||
device_set_callback(uart, uart_irq_callback);
|
||
device_control(uart, DEVICE_CTRL_SET_INT, (void *)(UART_RX_FIFO_IT | UART_RTO_IT));
|
||
}
|
||
|
||
dma_register(DMA0_CH0_INDEX, "ch0");
|
||
struct device *dma_ch0 = device_find("ch0");
|
||
|
||
if (dma_ch0) {
|
||
DMA_DEV(dma_ch0)->direction = DMA_MEMORY_TO_PERIPH;
|
||
DMA_DEV(dma_ch0)->transfer_mode = DMA_LLI_ONCE_MODE;
|
||
DMA_DEV(dma_ch0)->src_req = DMA_REQUEST_NONE;
|
||
DMA_DEV(dma_ch0)->dst_req = DMA_REQUEST_UART0_TX;
|
||
DMA_DEV(dma_ch0)->src_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
|
||
DMA_DEV(dma_ch0)->dst_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
|
||
DMA_DEV(dma_ch0)->src_burst_size = DMA_BURST_INCR1;
|
||
DMA_DEV(dma_ch0)->dst_burst_size = DMA_BURST_INCR1;
|
||
DMA_DEV(dma_ch0)->src_width = DMA_TRANSFER_WIDTH_8BIT;
|
||
DMA_DEV(dma_ch0)->dst_width = DMA_TRANSFER_WIDTH_8BIT;
|
||
device_open(dma_ch0, 0);
|
||
|
||
device_control(uart, DEVICE_CTRL_ATTACH_TX_DMA, dma_ch0);
|
||
device_set_callback(dma_ch0, dma_ch0_irq_callback);
|
||
/* close dma irq */
|
||
//device_control(UART_DEV(uart)->tx_dma, DEVICE_CTRL_CLR_INT, NULL);
|
||
}
|
||
|
||
// dma_register(DMA0_CH1_INDEX, "ch1");
|
||
// struct device *dma_ch1 = device_find("ch1");
|
||
|
||
// if (dma_ch1) {
|
||
// DMA_DEV(dma_ch1)->direction = DMA_PERIPH_TO_MEMORY;
|
||
// DMA_DEV(dma_ch1)->transfer_mode = DMA_LLI_ONCE_MODE;
|
||
// DMA_DEV(dma_ch1)->src_req = DMA_REQUEST_UART0_RX;
|
||
// DMA_DEV(dma_ch1)->dst_req = DMA_REQUEST_NONE;
|
||
// DMA_DEV(dma_ch1)->src_addr_inc = DMA_ADDR_INCREMENT_DISABLE;
|
||
// DMA_DEV(dma_ch1)->dst_addr_inc = DMA_ADDR_INCREMENT_ENABLE;
|
||
// DMA_DEV(dma_ch1)->src_burst_size = DMA_BURST_INCR1;
|
||
// DMA_DEV(dma_ch1)->dst_burst_size = DMA_BURST_INCR1;
|
||
// DMA_DEV(dma_ch1)->src_width = DMA_TRANSFER_WIDTH_8BIT;
|
||
// DMA_DEV(dma_ch1)->dst_width = DMA_TRANSFER_WIDTH_8BIT;
|
||
// device_open(dma_ch1, 0);
|
||
|
||
// device_control(uart, DEVICE_CTRL_ATTACH_RX_DMA, dma_ch1);
|
||
// device_set_callback(dma_ch1, dma_ch1_irq_callback);
|
||
// device_control(dma_ch1, DEVICE_CTRL_SET_INT, NULL);
|
||
// }
|
||
|
||
device_control(uart, DEVICE_CTRL_TX_DMA_SUSPEND, NULL);
|
||
|
||
isp_obj_uart = isp_obj;
|
||
|
||
return 0;
|
||
}
|