活动介绍

帮我给以下代码写注释void swap(int* a, int* b) { int tmp = *a; *a = *b, *b = tmp; } struct DisjointSetUnion { int *f, *size; int n, setCount; }; void initDSU(struct DisjointSetUnion* obj, int n) { obj->f = malloc(sizeof(int) * n); obj->size = malloc(sizeof(int) * n); obj->n = n; obj->setCount = n; for (int i = 0; i < n; i++) { obj->f[i] = i; obj->size[i] = 1; } } int find(struct DisjointSetUnion* obj, int x) { return obj->f[x] == x ? x : (obj->f[x] = find(obj, obj->f[x])); } int unionSet(struct DisjointSetUnion* obj, int x, int y) { int fx = find(obj, x), fy = find(obj, y); if (fx == fy) { return false; } if (obj->size[fx] < obj->size[fy]) { swap(&fx, &fy); } obj->size[fx] += obj->size[fy]; obj->f[fy] = fx; obj->setCount--; return true; } int connected(struct DisjointSetUnion* obj, int x, int y) { return find(obj, x) == find(obj, y); } struct Tuple { int x, y, z }; int cmp(const struct Tuple* a, const struct Tuple* b) { return a->z - b->z; } int minimumEffortPath(int** heights, int heightsSize, int* heightsColSize) { int m = heightsSize; int n = heightsColSize[0]; struct Tuple edges[n * m * 2]; int edgesSize = 0; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int id = i * n + j; if (i > 0) { edges[edgesSize].x = id - n; edges[edgesSize].y = id; edges[edgesSize++].z = fabs(heights[i][j] - heights[i - 1][j]); } if (j > 0) { edges[edgesSize].x = id - 1; edges[edgesSize].y = id; edges[edgesSize++].z = fabs(heights[i][j] - heights[i][j - 1]); } } } qsort(edges, edgesSize, sizeof(struct Tuple), cmp); struct DisjointSetUnion* uf = malloc(sizeof(struct DisjointSetUnion)); initDSU(uf, m * n); int ans = 0; for (int i = 0; i < edgesSize; i++) { unionSet(uf, edges[i].x, edges[i].y); if (connected(uf, 0, m * n - 1)) { ans = edges[i].z; break; } } return ans; }

时间: 2023-06-17 09:04:47 浏览: 133
/* 这个函数实现了两个数字交换的功能 输入:int* a, int* b, 分别是需要交换的两个数字的指针 输出:无 */ void swap(int* a, int* b) { int tmp = *a; *a = *b, *b = tmp; } /* 这个结构体实现了并查集的功能 包括初始化,查找根节点,合并两个集合,判断两个元素是否属于同一个集合 */ struct DisjointSetUnion { int *f, *size; // f数组表示每个元素的父节点,size数组表示每个集合的大小 int n, setCount; // n表示元素个数,setCount表示集合个数 }; /* 初始化并查集 输入:DisjointSetUnion* obj, 指向需要初始化的并查集的指针;int n,元素个数 输出:无 */ void initDSU(struct DisjointSetUnion* obj, int n) { obj->f = malloc(sizeof(int) * n); obj->size = malloc(sizeof(int) * n); obj->n = n; obj->setCount = n; for (int i = 0; i < n; i++) { obj->f[i] = i; obj->size[i] = 1; } } /* 查找元素所在集合的根节点 输入:DisjointSetUnion* obj, 指向并查集的指针;int x,需要查找的元素 输出:x所在集合的根节点 */ int find(struct DisjointSetUnion* obj, int x) { return obj->f[x] == x ? x : (obj->f[x] = find(obj, obj->f[x])); } /* 合并两个集合 输入:DisjointSetUnion* obj, 指向并查集的指针;int x, int y, 需要合并的两个元素 输出:合并是否成功,成功返回1,失败返回0 */ int unionSet(struct DisjointSetUnion* obj, int x, int y) { int fx = find(obj, x), fy = find(obj, y); if (fx == fy) { return false; } if (obj->size[fx] < obj->size[fy]) { swap(&fx, &fy); } obj->size[fx] += obj->size[fy]; obj->f[fy] = fx; obj->setCount--; return true; } /* 判断两个元素是否属于同一个集合 输入:DisjointSetUnion* obj, 指向并查集的指针;int x, int y,需要判断的两个元素 输出:是否属于同一个集合,是返回1,否返回0 */ int connected(struct DisjointSetUnion* obj, int x, int y) { return find(obj, x) == find(obj, y); } /* 这个结构体表示边 包括边的起点,终点和权值 */ struct Tuple { int x, y, z }; /* 比较边的权值,用于排序 输入:const struct Tuple* a, const struct Tuple* b,指向需要比较的两个边的指针 输出:两个边的权值之差 */ int cmp(const struct Tuple* a, const struct Tuple* b) { return a->z - b->z; } /* 这个函数实现了找到最小代价路径的功能 输入:int** heights,表示二维高度数组;int heightsSize,高度数组的行数;int* heightsColSize,高度数组的列数 输出:最小的代价值 */ int minimumEffortPath(int** heights, int heightsSize, int* heightsColSize) { int m = heightsSize; int n = heightsColSize[0]; struct Tuple edges[n * m * 2]; int edgesSize = 0; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int id = i * n + j; if (i > 0) { edges[edgesSize].x = id - n; edges[edgesSize].y = id; edges[edgesSize++].z = fabs(heights[i][j] - heights[i - 1][j]); } if (j > 0) { edges[edgesSize].x = id - 1; edges[edgesSize].y = id; edges[edgesSize++].z = fabs(heights[i][j] - heights[i][j - 1]); } } } qsort(edges, edgesSize, sizeof(struct Tuple), cmp); struct DisjointSetUnion* uf = malloc(sizeof(struct DisjointSetUnion)); initDSU(uf, m * n); int ans = 0; for (int i = 0; i < edgesSize; i++) { unionSet(uf, edges[i].x, edges[i].y); if (connected(uf, 0, m * n - 1)) { ans = edges[i].z; break; } } return ans; }
阅读全文

相关推荐

/** * Note: The returned array must be malloced, assume caller calls free(). */ struct hash_stable{ int key; int val; UT_hash_handle hh; }; typedef struct hash_stable* hash_ptr; struct pair{ int first; int second; }; struct pair* heap; int heapSize; void swap(struct pair*a,struct pair *b){ struct pair t=*a; *a=*b; *b=t; } bool cmp(struct pair* a,struct pair* b){ return a->second<b->second; } struct pair top(){ return heap[1]; } void push(hash_ptr x){ //先放入栈尾 heap[++heapSize].first=x->key; heap[heapSize].second=x->val; int p=heapSize; while(p>1){ int s=p>>1;//s为p的父节点 if(cmp(&heap[s],&heap[p]))return;//父节点更小则不需要换 swap(&heap[s],&heap[p]); p=s; } } //取出堆顶 void pop(){ heap[1]=heap[heapSize--];//将堆尾放入堆顶,在调整 int p=1; while((p<<1)<=heapSize){ int s=p<<1; if(s<heapSize&&cmp(&heap[s+1],&heap[s]))s++;//右孩子小于左孩子更小 if(cmp(&heap[p],&heap[s]))return; swap(&heap[p],&heap[s]); p=s; } } int* topKFrequent(int* nums, int numsSize, int k, int* returnSize) { hash_ptr head=NULL; hash_ptr p=NULL,tmp=NULL; for(int i=0;i<numsSize;i++){ HASH_FIND_INT(head,&nums[i],p); if(p==NULL){ p=malloc(sizeof(struct hash_stable)); p->key=nums[i]; p->val=1; HASH_ADD_INT(head,key,p); } else{ p->val++; } } heap=malloc(sizeof(struct pair)*(k+1)); heapSize=0; HASH_ITER(hh,head,p,tmp){ if(heapSize==k){ struct pair tmp=top(); if(tmp.secondval){ pop(); push(p); } } else { push(p); } } *returnSize=k; int *res=malloc(sizeof(int)*k); for(int i=0;i<k;i++){ struct pair tmp=top(); pop(); res[i]=tmp.first; } return res; }添加注释并给出本题的思路

感觉被你变复杂了,不如你把官方代码详细注释一下给我吧#include <stdio.h> #include <string.h> void transform(int *arry, int col_row) { //找到最大值 int max = arry[0], max_idx; for (int i = 0; i < col_row * col_row; i++) { if (max < arry[i]) max = arry[i];//找出最大数 max_idx = i; } //行列相乘得到总数量,除以2后加1则为中心点(暂时不考虑偶数的情况) int center_idx = (col_row * col_row) / 2; int tmp = arry[cen ter_idx]; arry[center_idx] = arry[max_idx]; arry[max_idx] = tmp; //找到四个最小值 int min_idx[4]; for (int i = 0; i < 4; i++) {//循环4次获取到最小值 int min_tmp = arry[col_row * col_row - 1]; for (int j = 0; j < col_row * col_row; j++) {//遍历所有数据,逐个比较获取最小值 int k = 0; for (; k < i; k++) {//但是要注意如果某个下标的数据已经是获取过的最小值,则不能进行判断(因为这个肯定是最小的) if (j == min_idx[k]) break; } if (k != i) { continue; }//k和i不同表示j这个坐标已经是找到的最小的几个数字之一,则找下一个判断 if (min_tmp > arry[j]) { // 相当于在剩下的数中找到最小的那个数字 min_tmp = arry[j]; min_idx[i] = j; //并且记录这个数字的位置 } } } int change_idx[4];//先计算四个角的下标,便于后边进行交换 change_idx[0] = 0;//第一个要置换的数据的下标,也就是左上角 change_idx[1] = col_row - 1;//第二个要置换的数据的下标,也就是右上角 change_idx[2] = col_row * (col_row - 1);//第一个要置换的数据的下标,也就是左下角 change_idx[3] = (col_row * col_row) - 1;//第一个要置换的数据的下标,也就是右下角 for (int i = 0; i < 4; i++) { int tmp = arry[change_idx[i]]; arry[change_idx[i]] = arry[min_idx[i]]; arry[min_idx[i]] = tmp; } return ; } int main() { int arry[5][5]; printf("Please enter a 5x5 matrix: \n"); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { scanf_s("%d", &arry[i][j]); } } transform(*arry, 5);//将二维数组当做一维数组传入处理,并且传入行列数 printf("\n"); for (int i = 0; i < 5; i++) { for (int j = 0; j < 5; j++) { printf("%d ", arry[i][j]); } printf("\n"); } system("pause"); return 0; }

int virtio_rdma_post_send(struct ib_qp *ibqp, const struct ib_send_wr *wr, const struct ib_send_wr **bad_wr) { struct scatterlist *sgs[1], hdr; struct virtio_rdma_qp *vqp = to_vqp(ibqp); struct virtio_rdma_cmd_post_send *cmd = NULL; int rc = 0; unsigned tmp; unsigned int sgl_len; void* ptr; if (vqp->type == VIRTIO_RDMA_TYPE_USER) goto kick_vq; spin_lock(&vqp->sq->lock); while (wr) { if (vqp->type == VIRTIO_RDMA_TYPE_KERNEL && wr->opcode != IB_WR_SEND && wr->opcode != IB_WR_SEND_WITH_IMM && wr->opcode != IB_WR_REG_MR && wr->opcode != IB_WR_LOCAL_INV && wr->opcode != IB_WR_SEND_WITH_INV) { pr_warn("Only support op send in kernel\n"); *bad_wr = wr; rc = -EINVAL; goto out; } while ((ptr = virtqueue_get_buf(vqp->rq->vq, &tmp)) != NULL) { kfree(ptr); } // FIXME: space for inline data sgl_len = sizeof(struct virtio_rdma_sge) * wr->num_sge; cmd = kzalloc(sizeof(*cmd) + sgl_len, GFP_ATOMIC); if (!cmd) { *bad_wr = wr; rc = -ENOMEM; goto out; } cmd->num_sge = wr->num_sge; cmd->send_flags = wr->send_flags; cmd->opcode = wr->opcode; cmd->wr_id = wr->wr_id; cmd->ex.imm_data = wr->ex.imm_data; cmd->ex.invalidate_rkey = wr->ex.invalidate_rkey; switch (ibqp->qp_type) { case IB_QPT_GSI: case IB_QPT_UD: if (unlikely(!ud_wr(wr)->ah)) { pr_warn("invalid address handle\n"); *bad_wr = wr; rc = -EINVAL; goto out; } cmd->wr.ud.remote_qpn = ud_wr(wr)->remote_qpn; cmd->wr.ud.remote_qkey = ud_wr(wr)->remote_qkey; cmd->wr.ud.av = to_vah(ud_wr(wr)->ah)->av; break; case IB_QPT_RC: switch (wr->opcode) { case IB_WR_RDMA_READ: case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: cmd->wr.rdma.remote_addr = rdma_wr(wr)->remote_addr; cmd->wr.rdma.rkey = rdma_wr(wr)->rkey; break; case IB_WR_LOCAL_INV: case IB_WR_SEND_WITH_INV: cmd->ex.invalidate_rkey = wr->ex.invalidate_rkey; break; case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: cmd->wr.atomic.remote_addr = atomic_wr(wr)->remote_addr; cmd->wr.atomic.rkey = atomic_wr(wr)->rkey; cmd->wr.atomic.compare_add = atomic_wr(wr)->compare_add; if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) cmd->wr.atomic.swap = atomic_wr(wr)->swap; break; case IB_WR_REG_MR: cmd->wr.reg.mrn = to_vmr(reg_wr(wr)->mr)->mr_handle; cmd->wr.reg.key = reg_wr(wr)->key; cmd->wr.reg.access = reg_wr(wr)->access; break; default: break; } break; default: pr_err("Bad qp type\n"); rc = -EINVAL; goto out; } // TODO: check max_inline_data if (unlikely(wr->send_flags & IB_SEND_INLINE)) copy_inline_data_to_wqe(cmd, wr); else memcpy((char*)cmd + sizeof(*cmd), wr->sg_list, sgl_len); sg_init_one(&hdr, cmd, sizeof(*cmd) + sgl_len); sgs[0] = &hdr; rc = virtqueue_add_sgs(vqp->sq->vq, sgs, 1, 0, cmd, GFP_ATOMIC); if (rc) { pr_err("post send err %d", rc); *bad_wr = wr; goto out; } cmd = NULL; wr = wr->next; } out: spin_unlock(&vqp->sq->lock); kfree(cmd); kick_vq: virtqueue_kick(vqp->sq->vq); return rc; }

@PostConstruct public void initOcrEngine() { tesseract = new Tesseract(); try { //语言包路径和支持语言 tesseract.setDatapath("D:\\maven_use\\lingxi-lhc\\lingxi-ai-extend\\lingxi-ai-comparison\\src\\main\\resources\\tessdata"); tesseract.setLanguage("eng+chi_sim"); tesseract.setPageSegMode(6); // 自动页面分割 tesseract.setOcrEngineMode(1); // LSTM引擎 } catch (Exception e) { throw new RuntimeException("OCR引擎初始化失败: " + e.getMessage(), e); } } /** * 支持PDF和图片 */ public String extractContent(MultipartFile file) { String contentType = file.getContentType(); String fileName = file.getOriginalFilename().toLowerCase(); if (contentType == null) { return "不支持的文件类型: " + contentType; } if (fileName.endsWith(".pdf")) { return readPdfText(file); } return extractImageText(file); } /** * 读取PDF文本内容 * * @param file * @return */ public String readPdfText(MultipartFile file) { try (PDDocument doc = PDDocument.load(file.getInputStream())) { PDFTextStripper stripper = new PDFTextStripper(); // 设置行分隔符 stripper.setLineSeparator("\n"); // 设置字符间距 stripper.setSortByPosition(true); String rawText = stripper.getText(doc); System.out.println("内容" + rawText); return rawText.trim(); } catch (Exception e) { return MessageUtils.message("file.red.pdf.error"); } } /** * OCR识别图片内容 */ private String extractImageText(MultipartFile file) { try { // 创建临时文件 Path tempFile = Files.createTempFile("ocr_", getFileExtension(file.getOriginalFilename())); Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING); // 执行OCR识别 File imageFile = tempFile.toFile(); String result = tesseract.doOCR(imageFile) .replaceAll("\\s+", " ").trim(); System.out.println("读取的内容" + result); // 清理临时文件 Files.deleteIfExists(tempFile); return result; } catch (Exception e) { return "OCR处理失败: " + e.getMessage(); } } private String getFileExtension(String filename) { if (filename == null) return ".tmp"; int dotIndex = filename.lastIndexOf('.'); return (dotIndex == -1) ? ".tmp" : filename.substring(dotIndex); } public JsonNode parseJson(String jsonContent) throws Exception { return this.objectMapper.readTree(jsonContent); } public List<ValidationResult> compareContent(String pdfText, JsonNode jsonConfig) { List<ValidationResult> results = new ArrayList<>(); // 去除读取内容中的多余空格 pdfText = pdfText.replaceAll("\\s+", ""); // 处理JSON结构(支持单个对象或数组) JsonNode dataNode; if (jsonConfig.isArray() && jsonConfig.size() > 0) { dataNode = jsonConfig.get(0); } else if (jsonConfig.isObject()) { dataNode = jsonConfig; } else { results.add(new ValidationResult("ERROR", "JSON格式错误", "期望一个对象或包含对象的数组", "实际格式不匹配", false)); return results; } // 动态定义地址字段列表 Set<String> addressFields = new HashSet<>(); // 字段直接匹配 checkNonAddressFields(pdfText, dataNode, results, addressFields); // 连续匹配 checkAddressFields(pdfText, dataNode, results, addressFields); return results; } /** * 检查 JSON 中非地址字段是否严格存在于 PDF 文本中 */ private void checkNonAddressFields(String pdfText, JsonNode jsonConfig, List<ValidationResult> results, Set<String> addressFields) { Iterator<Map.Entry<String, JsonNode>> fields = jsonConfig.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); String fieldName = entry.getKey(); JsonNode valueNode = entry.getValue(); if (valueNode.isValueNode() && !addressFields.contains(fieldName)) { //去除多余空格 String expectedValue = valueNode.asText().trim().replaceAll("\\s+", ""); if (expectedValue.isEmpty()) continue; // 直接进行字符串匹配 boolean found = pdfText.contains(expectedValue); results.add(new ValidationResult( "FIELD", fieldName, expectedValue, found ? "Found" : "Not Found", found )); } } } /** * 检查 JSON 中地址字段是否严格存在于 PDF 文本中 */ private void checkAddressFields(String pdfText, JsonNode jsonConfig, List<ValidationResult> results, Set<String> addressFields) { // HanLP分词 List<Term> terms = HanLP.segment(pdfText); List<String> addressParts = new ArrayList<>(); for (Term term : terms) { String word = term.word; if (word.matches("\\d{5,7}")) { addressParts.add(word); } else if (term.nature.toString().startsWith("ns")) { addressParts.add(word); } } // 遍历 JSON 配置中的地址字段 Iterator<Map.Entry<String, JsonNode>> fields = jsonConfig.fields(); while (fields.hasNext()) { Map.Entry<String, JsonNode> entry = fields.next(); String fieldName = entry.getKey(); JsonNode valueNode = entry.getValue(); if (valueNode.isValueNode() && addressFields.contains(fieldName)) { //去除多余空格 String expectedValue = valueNode.asText().trim().replaceAll("\\s+", ""); if (expectedValue.isEmpty()) continue; boolean found = false; for (String part : addressParts) { if (part.equals(expectedValue)) { found = true; break; } } results.add(new ValidationResult( "FIELD", fieldName, expectedValue, found ? "Found" : "Not Found", found )); } } } public JsonNode parsePipeSeparatedDataToJson(String inputData) throws Exception { Map<String, String> dataMap = parsePipeSeparatedData(inputData); return objectMapper.valueToTree(dataMap); } // 解析以 | 分隔的键值对数据 public Map<String, String> parsePipeSeparatedData(String inputData) { Map<String, String> dataMap = new HashMap<>(); String[] lines = inputData.split("\n"); String[] headers = lines[0].split("\\|"); String[] values = lines[1].split("\\|"); for (int i = 0; i < headers.length; i++) { dataMap.put(headers[i], values[i]); } return dataMap; } // 判断是否是以 | 分隔的数据 public boolean isPipeSeparatedData(String inputData) { return inputData.contains("|"); } 这个是我的代码 我读取的内容内容Delivery Note Apple Computer Trading *QBA7074052* (Shanghai)Co Ltd 1249 Century Avenue Tower 3, Page No : 1 of 1 Pudong new district, Delivery Reference: QBA7074052 shanghai 200122, China Delivery Date: 13-MAY-2025 Web Ref#: 2365330821 Carrier: XDESC Mode: ROAD Sold To: Ship To: 深圳市天联终端有限公司 深圳市天联终端有限公司上海库 前湾一路1号A栋201室 嘉定区兴邦路1166号 深圳广东518000 上海上海201800 中国 中国 Cust PO No. Cust PO Line # Sales Order# Delivery No# Item No. Part No. Description Qty Shipped 4500304123-TLZD AAA3871466 QBA7074052 000400 MTLH3CH/A IPHONE 15 GREEN 128GB-CHN 20 -03 4500304123-TLZD AAA3871466 QBA7074052 000620 MTLG3CH/A IPHONE 15 BLUE 128GB-CHN 160 -03 Total Quantity: 180 Total Packages:18 Total Weight:73.94 KGS Cube:0.22 CBM Damaged Boxes · Part Number: · Quantity: IMPORTANT REMINDER Please verify that all part numbes(s) and quantity are as started above. We confirm that: 1) We received ___________ cartons/boxes as started above. 2) We received the goods as started above. The goods received are inspected and found to be in good order and condition. Received By __________________ Received Date ____________________ (Signature & Company stamp) 我传的值"filE_CONTENT": "PALLET_ID|KNBOXNO|CARRIER|COC|CTRY|HAWB|PO|ORIGIN|INVOICENO|CARTONS|SHIPID|SHIP_DATE|TEL|SSCC|RETURN_TO1|RETURN_TO2|RETURN_TO3|RETURN_TO4|RETURN_TO5|RETURN_TO6|RETURN_TO7|RETURN_TO8|SHIP_TO1|SHIP_TO2|SHIP_TO3|SHIP_TO4|SHIP_TO5|SHIP_TO6|SHIP_TO7|SHIP_TO8|LINEITEM1|MPN1|QTY1|LINEITEM2|MPN2|QTY2|LINEITEM3|MPN3|QTY3|\nFO2501000233P0002||DGF-AD|NL|NL|8VG8286|0638138589|PVG|8VG8286|61/84|73292885370002|06/01/2025|00000000|001959499064098506|ADI EMEIA Logistics|Inbound Department|||||||PEGATRON CORPORATION|c/o DP World Logistics Netherlands BV|Van Hilststraat 23|5145 RK Waalwijk,Netherlands|5145 RK Waalwijk Netherlands||||00010|MLPF3AA/A|10|" 然后比较应该没有一致的而把不一致的返回来{ "code": 200, "msg": "条件符合规范", "data": null } 返回也是错误的

在DC-AC单相全桥逆变电路中,使用stm32f103RCT6(DMA没有自带双缓冲模式?),软件部分,SPWM.c中我采用外部程序生成正弦表的1600个数据点,(由于是中央对齐模式2,使用CC1触发DMA,故每个TIM1周期更新两个CCR数据值,那么一个正弦波周期就是800个数据点)通过TIM1的中央对齐模式2(ARR=1800-1,PSC=1-1,时序:0-->ARR-->0),每个TIM1周期为50us,每个TIM1周期更新两次CCR(向上计数时CNT=CCR_A和向下计数CNT=CCR_B时),使用TIM1_CC1触发DMA1的通道二将正弦表中的数据依次搬运到TIM1_CCR1中完成SPWM的生成,我只需要用到一路主路PWM和互补PWM(即CH1和CH1N,因为我使用的驱动使IR2104,它只有一个输入端IN,两片IR2104,一片输入CH1另一片输入CH1N,它就是这么使用的)。ADC.c中打算采用TIM1_CC2触发ADC(同样是每个TIM1周期会采到两个样点,因为时序:0-->ARR-->0过程中会触发两次CC2)(双ADC同步规则分别采电压和电流)+DMA搬运进uint32_t数组ADC_Dual_Value[Buffersize]中(这里Buffersize为1600,前800个数据点是采到的一个完整正弦周期的数据点,后800个点也是采到的完整的正弦周期的数据点,我打算使用滑动窗口计算Rms而不是等待采完一整个正弦周期20ms才能计算一次Rms送入pid,由于数据量太大,每个周期虽然实际采了800个点,但是我实际只取100个点(800个点里每8个数据取1个),但是我一旦在TIM3触发的定时中断里执行Control_Loop()时,OLED就会黑屏不显示(使用的0.96寸4针脚(VCC、GND、SCL(PB8)、SDA(PB9))OLED显示屏,),但是注释掉TIM3_Init()时就恢复正常,应该时计算量太大了影响到OLED通信了?我该怎么办?或者换个思路使用一个定时器定时中断专门在中断里显示OLED刷新和显示的部分?代码如下,帮我分析分析,谢谢: Main.c:#include "stm32f10x.h" // Device header #include "Delay.h" #include "MYDAC.h" #include "ADC_DMA.h" #include "OLED.h" #include "OLED_Data.h" #include "PID.h" #include "SPWM.h" #include "Serial.h" uint8_t temp = 0; uint16_t Sam_Vol,Sam_Cur,vol_DAC; int main(void) //PA8输出主PWM PB13输出互补PWM PB0采电压 PB1采电流 { OLED_Init(); Timer3_Init(); TIM4_Init(); ADC_DMAInit(); SPWM_BDTR_Init(); //PID_Init(0.15,0.01,0,0,0,0); //Serial_DMA_Init(); OLED_ShowString(0,0,"电压值:",OLED_8X16); OLED_ShowString(0,16,"ADC电压:",OLED_8X16); OLED_ShowString(0,32,"输出电流:",OLED_8X16); OLED_Update(); while(1) { if(Control_Flag) { Control_Loop(); Control_Flag = 0; } // Sent_Data_to_Vofa(); } } SPWM.c:#include "stm32f10x.h" // Device header #include "Serial.h" #include "PID.h" #include <math.h> #include "ADC_DMA.h" #include "SPWM.h" //DMA传输正弦表进TIM1_CCR1中的数据量 #define BufferSize_Sample 1600 // uint8_t TIM_DeadTime = 50; //待确定 //用于串口的定时中断采CCR1送入上位机,此为预备数组 //#define Sample_CCR_Num 100 //uint16_t Sample_Ready_CCR1[100];//TIM1_CCR1的采样,缓冲采样数组1,采样完毕后转移进Serial.c中的Buffer_to_Sent[]中 //uint16_t Sample_Ready_CCR2[100];//TIM1_CCR1的采样,缓冲采样数组2,采样完毕后转移进Serial.c中的Buffer_to_Sent[]中 //volatile uint16_t *Current_CCR_Buffer = Sample_Ready_CCR1; //volatile uint8_t Buffer_Ready_Flag = 0; //采满200个样点的标志位 volatile uint16_t *Current_SPWM_ReadyBuffer; //指向当前就绪SPWM缓冲区,用于更新SPWM新表 volatile uint8_t SPWM_BufferReady_Flag = 0; //SPWM缓冲区就绪标志位 volatile uint16_t* const DMA_RemainIndex = (volatile uint16_t*)(&DMA1_Channel1->CNDTR); //DMA1_Channel1(搬运ADC)当前剩余数据 volatile Rms_Windows rms_windows = {0,0,0}; uint16_t DMA_CurrentIndex; //DMA1_Channel1当前索引 uint8_t Can_Get_RmsFlag = 0; //可获取有效值标志位 uint16_t SPWM_Data[BufferSize_Sample] = //SPWM的CCR值,大缓冲数组,每800个点为一个正弦周期(20ms),每个TIM1周期更新两个点 { 900,906,912,918,925,931,937,943,950,956,962,969,975,981,987,994, 1000,1006,1012,1018,1025,1031,1037,1043,1049,1056,1062,1068,1074,1080,1086,1092, 1098,1105,1111,1117,1123,1129,1135,1141,1147,1153,1159,1165,1170,1176,1182,1188, 1194,1200,1206,1211,1217,1223,1229,1234,1240,1246,1251,1257,1263,1268,1274,1279, 1285,1290,1296,1301,1307,1312,1317,1323,1328,1333,1339,1344,1349,1354,1360,1365, 1370,1375,1380,1385,1390,1395,1400,1405,1409,1414,1419,1424,1429,1433,1438,1443, 1447,1452,1456,1461,1465,1470,1474,1478,1483,1487,1491,1495,1500,1504,1508,1512, 1516,1520,1524,1528,1532,1535,1539,1543,1547,1550,1554,1558,1561,1565,1568,1572, 1575,1578,1582,1585,1588,1591,1594,1597,1601,1604,1607,1609,1612,1615,1618,1621, 1623,1626,1629,1631,1634,1636,1639,1641,1643,1646,1648,1650,1652,1654,1656,1658, 1660,1662,1664,1666,1668,1669,1671,1673,1674,1676,1677,1679,1680,1682,1683,1684, 1685,1686,1688,1689,1690,1691,1692,1692,1693,1694,1695,1695,1696,1697,1697,1698, 1698,1698,1699,1699,1699,1699,1699,1699,1700,1699,1699,1699,1699,1699,1699,1698, 1698,1698,1697,1697,1696,1695,1695,1694,1693,1692,1692,1691,1690,1689,1688,1686, 1685,1684,1683,1682,1680,1679,1677,1676,1674,1673,1671,1669,1668,1666,1664,1662, 1660,1658,1656,1654,1652,1650,1648,1646,1643,1641,1639,1636,1634,1631,1629,1626, 1623,1621,1618,1615,1612,1609,1607,1604,1601,1597,1594,1591,1588,1585,1582,1578, 1575,1572,1568,1565,1561,1558,1554,1550,1547,1543,1539,1535,1532,1528,1524,1520, 1516,1512,1508,1504,1500,1495,1491,1487,1483,1478,1474,1470,1465,1461,1456,1452, 1447,1443,1438,1433,1429,1424,1419,1414,1409,1405,1400,1395,1390,1385,1380,1375, 1370,1365,1360,1354,1349,1344,1339,1333,1328,1323,1317,1312,1307,1301,1296,1290, 1285,1279,1274,1268,1263,1257,1251,1246,1240,1234,1229,1223,1217,1211,1206,1200, 1194,1188,1182,1176,1170,1165,1159,1153,1147,1141,1135,1129,1123,1117,1111,1105, 1098,1092,1086,1080,1074,1068,1062,1056,1049,1043,1037,1031,1025,1018,1012,1006, 1000,994,987,981,975,969,962,956,950,943,937,931,925,918,912,906, 900,893,887,881,874,868,862,856,849,843,837,830,824,818,812,805, 799,793,787,781,774,768,762,756,750,743,737,731,725,719,713,707, 701,694,688,682,676,670,664,658,652,646,640,634,629,623,617,611, 605,599,593,588,582,576,570,565,559,553,548,542,536,531,525,520, 514,509,503,498,492,487,482,476,471,466,460,455,450,445,439,434, 429,424,419,414,409,404,399,394,390,385,380,375,370,366,361,356, 352,347,343,338,334,329,325,321,316,312,308,304,299,295,291,287, 283,279,275,271,267,264,260,256,252,249,245,241,238,234,231,227, 224,221,217,214,211,208,205,202,198,195,192,190,187,184,181,178, 176,173,170,168,165,163,160,158,156,153,151,149,147,145,143,141, 139,137,135,133,131,130,128,126,125,123,122,120,119,117,116,115, 114,113,111,110,109,108,107,107,106,105,104,104,103,102,102,101, 101,101,100,100,100,100,100,100,100,100,100,100,100,100,100,101, 101,101,102,102,103,104,104,105,106,107,107,108,109,110,111,113, 114,115,116,117,119,120,122,123,125,126,128,130,131,133,135,137, 139,141,143,145,147,149,151,153,156,158,160,163,165,168,170,173, 176,178,181,184,187,190,192,195,198,202,205,208,211,214,217,221, 224,227,231,234,238,241,245,249,252,256,260,264,267,271,275,279, 283,287,291,295,299,304,308,312,316,321,325,329,334,338,343,347, 352,356,361,366,370,375,380,385,390,394,399,404,409,414,419,424, 429,434,439,445,450,455,460,466,471,476,482,487,492,498,503,509, 514,520,525,531,536,542,548,553,559,565,570,576,582,588,593,599, 605,611,617,623,629,634,640,646,652,658,664,670,676,682,688,694, 701,707,713,719,725,731,737,743,750,756,762,768,774,781,787,793, 799,805,812,818,824,830,837,843,849,856,862,868,874,881,887,893, 900,906,912,918,925,931,937,943,950,956,962,969,975,981,987,994, 1000,1006,1012,1018,1025,1031,1037,1043,1049,1056,1062,1068,1074,1080,1086,1092, 1098,1105,1111,1117,1123,1129,1135,1141,1147,1153,1159,1165,1170,1176,1182,1188, 1194,1200,1206,1211,1217,1223,1229,1234,1240,1246,1251,1257,1263,1268,1274,1279, 1285,1290,1296,1301,1307,1312,1317,1323,1328,1333,1339,1344,1349,1354,1360,1365, 1370,1375,1380,1385,1390,1395,1400,1405,1409,1414,1419,1424,1429,1433,1438,1443, 1447,1452,1456,1461,1465,1470,1474,1478,1483,1487,1491,1495,1500,1504,1508,1512, 1516,1520,1524,1528,1532,1535,1539,1543,1547,1550,1554,1558,1561,1565,1568,1572, 1575,1578,1582,1585,1588,1591,1594,1597,1601,1604,1607,1609,1612,1615,1618,1621, 1623,1626,1629,1631,1634,1636,1639,1641,1643,1646,1648,1650,1652,1654,1656,1658, 1660,1662,1664,1666,1668,1669,1671,1673,1674,1676,1677,1679,1680,1682,1683,1684, 1685,1686,1688,1689,1690,1691,1692,1692,1693,1694,1695,1695,1696,1697,1697,1698, 1698,1698,1699,1699,1699,1699,1699,1699,1700,1699,1699,1699,1699,1699,1699,1698, 1698,1698,1697,1697,1696,1695,1695,1694,1693,1692,1692,1691,1690,1689,1688,1686, 1685,1684,1683,1682,1680,1679,1677,1676,1674,1673,1671,1669,1668,1666,1664,1662, 1660,1658,1656,1654,1652,1650,1648,1646,1643,1641,1639,1636,1634,1631,1629,1626, 1623,1621,1618,1615,1612,1609,1607,1604,1601,1597,1594,1591,1588,1585,1582,1578, 1575,1572,1568,1565,1561,1558,1554,1550,1547,1543,1539,1535,1532,1528,1524,1520, 1516,1512,1508,1504,1500,1495,1491,1487,1483,1478,1474,1470,1465,1461,1456,1452, 1447,1443,1438,1433,1429,1424,1419,1414,1409,1405,1400,1395,1390,1385,1380,1375, 1370,1365,1360,1354,1349,1344,1339,1333,1328,1323,1317,1312,1307,1301,1296,1290, 1285,1279,1274,1268,1263,1257,1251,1246,1240,1234,1229,1223,1217,1211,1206,1200, 1194,1188,1182,1176,1170,1165,1159,1153,1147,1141,1135,1129,1123,1117,1111,1105, 1098,1092,1086,1080,1074,1068,1062,1056,1049,1043,1037,1031,1025,1018,1012,1006, 1000,994,987,981,975,969,962,956,950,943,937,931,925,918,912,906, 900,893,887,881,874,868,862,856,849,843,837,830,824,818,812,805, 799,793,787,781,774,768,762,756,750,743,737,731,725,719,713,707, 701,694,688,682,676,670,664,658,652,646,640,634,629,623,617,611, 605,599,593,588,582,576,570,565,559,553,548,542,536,531,525,520, 514,509,503,498,492,487,482,476,471,466,460,455,450,445,439,434, 429,424,419,414,409,404,399,394,390,385,380,375,370,366,361,356, 352,347,343,338,334,329,325,321,316,312,308,304,299,295,291,287, 283,279,275,271,267,264,260,256,252,249,245,241,238,234,231,227, 224,221,217,214,211,208,205,202,198,195,192,190,187,184,181,178, 176,173,170,168,165,163,160,158,156,153,151,149,147,145,143,141, 139,137,135,133,131,130,128,126,125,123,122,120,119,117,116,115, 114,113,111,110,109,108,107,107,106,105,104,104,103,102,102,101, 101,101,100,100,100,100,100,100,100,100,100,100,100,100,100,101, 101,101,102,102,103,104,104,105,106,107,107,108,109,110,111,113, 114,115,116,117,119,120,122,123,125,126,128,130,131,133,135,137, 139,141,143,145,147,149,151,153,156,158,160,163,165,168,170,173, 176,178,181,184,187,190,192,195,198,202,205,208,211,214,217,221, 224,227,231,234,238,241,245,249,252,256,260,264,267,271,275,279, 283,287,291,295,299,304,308,312,316,321,325,329,334,338,343,347, 352,356,361,366,370,375,380,385,390,394,399,404,409,414,419,424, 429,434,439,445,450,455,460,466,471,476,482,487,492,498,503,509, 514,520,525,531,536,542,548,553,559,565,570,576,582,588,593,599, 605,611,617,623,629,634,640,646,652,658,664,670,676,682,688,694, 701,707,713,719,725,731,737,743,750,756,762,768,774,781,787,793, 799,805,812,818,824,830,837,843,849,856,862,868,874,881,887,893 }; void SPWM_BDTR_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; DMA_InitTypeDef DMA_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //CH1为PA8 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; //CH1N互补为PB13 GPIO_Init(GPIOB,&GPIO_InitStructure); DMA_DeInit(DMA1_Channel2); //TIM1_CC1映射在DMA1的通道2 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM1 ->CCR1; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPWM_Data; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = BufferSize_Sample; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel2,&DMA_InitStructure); DMA_Cmd(DMA1_Channel2,ENABLE); TIM1->CCR1 = SPWM_Data[0]; //先校正第一个CCR为数组SPWM_Data[0],使得第一次CC1事件触发时DMA搬运SPWM_Data[1]进CCR1 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //中央对齐模式2,0-->ARR-->0的计数模式,向下计数阶段ARR-->0时CNT = 0时产生更新事件 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned2; TIM_TimeBaseStructure.TIM_Period = 1800-1; //20k的频率,ARR = 3600-1 TIM_TimeBaseStructure.TIM_Prescaler = 1-1; TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM1,&TIM_TimeBaseStructure); TIM_ClearFlag(TIM1,TIM_FLAG_CC1); //初始化前清除CC1标志位 TIM_ClearFlag(TIM2,TIM_FLAG_CC2); //初始化前清除CC2标志位 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //主路 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补路 TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OC1Init(TIM1,&TIM_OCInitStructure); TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //主路 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补路 TIM_OCInitStructure.TIM_Pulse = 300; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; TIM_OC2Init(TIM1,&TIM_OCInitStructure); TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; TIM_BDTRInitStructure.TIM_DeadTime = TIM_DeadTime; TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; //先不使用刹车 TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; //刹车触发极性,低电平 TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; //故障后不自动恢复输出,需自行手动恢复 TIM_BDTRConfig(TIM1,&TIM_BDTRInitStructure); // NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn; //0 0 // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // NVIC_Init(&NVIC_InitStructure); // TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel2, DMA_IT_TC | DMA_IT_HT, ENABLE); NVIC_EnableIRQ(DMA1_Channel2_IRQn); // 启用DMA中断通道2 TIM_DMACmd(TIM1,TIM_DMA_CC1,ENABLE); //TIM1的CC1事件更新时触发DMA TIM_ARRPreloadConfig(TIM1,ENABLE); //预装载ARR TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); //预装载 TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_OC2Ref); //TIM1_CC2触发ADC TIM_CtrlPWMOutputs(TIM1,ENABLE); //使能输出主PWM TIM_Cmd(TIM1,ENABLE); } void PWM_SetCCR(TIM_TypeDef* TIMx,uint8_t CHx,uint16_t Compare,uint16_t Upper_LimitCCR) //占空比设置 { if(CHx<1||CHx>4) return; if(Compare > Upper_LimitCCR) Compare = Upper_LimitCCR; switch (CHx) { case 1:TIM_SetCompare1(TIMx,Compare);break; case 2:TIM_SetCompare2(TIMx,Compare);break; case 3:TIM_SetCompare3(TIMx,Compare);break; case 4:TIM_SetCompare4(TIMx,Compare);break; } } uint16_t PWM_GetCCR(TIM_TypeDef* TIMx,uint8_t CHx) //参数1:定时器TIMX 参数2:通道,可为1~4 { if(CHx<1||CHx>4) return 0; uint16_t CCR; switch (CHx) { case 1:CCR =TIM_GetCapture1(TIMx);break; case 2:CCR =TIM_GetCapture2(TIMx);break; case 3:CCR =TIM_GetCapture3(TIMx);break; case 4:CCR =TIM_GetCapture4(TIMx);break; } return CCR; } void SPWM_Update(float Gain,volatile uint16_t *Ready_Buffer) //正弦表更新,传入Current_SPWM_ReadyBuffer指向待写入的缓冲区 { static float last_Gain = 1.0f; int Offset_Val,New_Val; if(SPWM_BufferReady_Flag&&fabs(Gain - last_Gain)>0.005f) { SPWM_BufferReady_Flag = 0; for(uint16_t i=0;i<800;i++ ) { Offset_Val = (int16_t)(Ready_Buffer[i] -900); New_Val = (int16_t)((Offset_Val * Gain) + 900); if(New_Val < 60) New_Val = 60; else if(New_Val > 1740) New_Val = 1740; Ready_Buffer[i] = (uint16_t)New_Val; } last_Gain = Gain; } } void Rms_WindowsInit(void) { rms_windows.Volt_SquareSum = 0; rms_windows.Cur_SquareSum = 0; rms_windows.Count = 0; } void Rms_WindowsUpdate(void) { uint16_t DMA_Remain = *DMA_RemainIndex; //DMA传输剩余数据量 DMA_CurrentIndex = ADC_DMA_BufferSize - DMA_Remain; //DMA当前索引 //需移除点的索引,一个正弦周期(20ms)共采800个点,每8个点取一个点,实际一共仅取100个采样点 uint16_t Old_Index = (DMA_CurrentIndex - DMA_CurrentIndex%8 + ADC_DMA_BufferSize - ADC_BufferSize)%ADC_DMA_BufferSize; uint16_t New_Index = DMA_CurrentIndex - DMA_CurrentIndex%8; uint32_t Old_Value = ADC_Dual_Value[Old_Index]; //需移除的点的32位值 uint32_t New_Value = ADC_Dual_Value[New_Index]; //解析需移除的点的值为高16位的电流值和低16位的电压值 uint16_t New_Volt = New_Value & 0xFFFF; uint16_t New_Cur = New_Value >> 16; uint16_t Old_Volt = Old_Value & 0xFFFF; uint16_t Old_Cur = Old_Value >> 16; //更新新的平方和 rms_windows.Volt_SquareSum += (New_Volt * New_Volt - Old_Volt * Old_Volt); rms_windows.Cur_SquareSum += (New_Cur * New_Cur - Old_Cur * Old_Cur); } float Get_Volt_RmsValue(void) { if(Can_Get_RmsFlag) { return sqrtf((float)rms_windows.Volt_SquareSum / ADC_BufferSize *3.3f/4095.0f); } else return 0; } float Get_Cur_RmsValue(void) { if(Can_Get_RmsFlag) { return sqrtf((float)rms_windows.Cur_SquareSum / ADC_BufferSize *3.3f/4095.0f); } else return 0; } void DMA1_Channel2_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_HT2)) //上半缓冲传输完成,上次的表已经传输完了,该传输上次表传输完成之前PID已生成好的新表了 { SPWM_BufferReady_Flag = 1; //置上半缓冲就绪1,该传输下半缓冲区了 Current_SPWM_ReadyBuffer = SPWM_Data; //当前源地址的指针指向下半缓冲区,将上次PID已更新好的数据写入TIM1_CC1 DMA_ClearITPendingBit(DMA1_IT_HT2); } if(DMA_GetITStatus(DMA1_IT_TC2)) //下半缓冲完成,上次的表已经传输完了,该传输上次表传输完成之前PID已生成好的新表了 { SPWM_BufferReady_Flag = 2; //置下半缓冲就绪2,该传输上半缓冲区了 Current_SPWM_ReadyBuffer = SPWM_Data+ 800; //当前源地址的指针指向上半缓冲区,将上次PID已更新好的数据写入TIM1_CC1 DMA_ClearITPendingBit(DMA1_IT_TC2); } } //void TIM1_UP_IRQHandler(void) //{ //// static uint8_t TIM_Count_Sample_CCR = 0; //// static uint8_t Sample_Num = 0; // if(TIM_GetITStatus(TIM1,TIM_IT_Update)) // { // // ADC_SoftwareStartConvCmd(ADC1,ENABLE); //每个TIM1中断软件触发ADC采样 // Window_UpdateFlag = 1; // // TIM_ClearITPendingBit(TIM1, TIM_IT_Update); // // // //if(++cycle_count >= 16000) //16000个TIM1中断时复位DMA // // { // // DMA_Cmd(DMA1_Channel2, DISABLE); //先关闭才能配置DMA // // DMA1_Channel2->CNDTR = BufferSize_Sample; // // DMA1_Channel2->CMAR = (uint32_t)SPWM_Data; // // TIM1->CCR1 = SPWM_Data[0]; //校正第一个CCR为数组SPWM_Data[0],使得第一次CC1事件触发时DMA搬运SPWM_Data[1]进CCR1 // // DMA_Cmd(DMA1_Channel2, ENABLE); // // cycle_count = 0; // // } // // // //// if(++TIM_Count_Sample_CCR >= 40) //每40次定时中断(0.2ms)就采一次CCR1进数组Sample_CCR【】中 //// { //// TIM_Count_Sample_CCR = 0; ////__disable_irq(); //原子保护,禁止中断 //// if(Sample_Num < Sample_CCR_Num) //如果数组没填满到Sample_CCR_Num这个次数,那就将CCR1存进去 //// { //// Current_CCR_Buffer[Sample_Num] = TIM1->CCR1; //// Sample_Num++; //// } ////__enable_irq(); //原子保护恢复,允许中断 //// if(Sample_Num >= Sample_CCR_Num) //如果数组已经采满Sample_CCR_Num这个次数,就产生标志位 //// { ////__disable_irq(); //原子保护,禁止中断 //// if(Current_CCR_Buffer == Sample_Ready_CCR1) //寻找当前指向哪个缓冲区 //// { //// Current_CCR_Buffer = Sample_Ready_CCR2; //缓冲区1满,切换缓冲区2 //// Buffer_Ready_Flag = 1; //标志位置1 //// } //// else //// { //// Current_CCR_Buffer = Sample_Ready_CCR1; //缓冲区2满,切换缓冲区1 //// Buffer_Ready_Flag = 2; //标志位置2 //// } //// Sample_Num = 0; //将Sample_Num复位 ////__enable_irq(); //原子保护恢复,允许中断 //// //// } //// } // // } // //} // // // // ADC_DMA.c:#include "stm32f10x.h" #include "PID.h" #include "Delay.h" #include "ADC_DMA.h" #include "SPWM.h" #include "OLED.h" #include "OLED_Data.h" #include<math.h> #define ChannelNum 1 uint32_t ADC_Dual_Value[ADC_DMA_BufferSize]; //使用双ADC规则同步 32位的ADC1_DR存数据,低16位为ADC1 高16位为ADC2 uint16_t Volt_Value[SimplifyADC_BufferSize]; uint16_t Cur_Value[SimplifyADC_BufferSize]; float Void_Gain = 11; float Cur_Gain = 1.47205; volatile uint8_t ADC_DMA_CompleteFlag = 0; // DMA缓冲区就绪标志 volatile uint32_t *Current_ADC_Buffer; //指向当前就绪缓冲区 void TIM2_PWM_Init(void) //TIM2的通道1产生的PWM用于开关,通道2产生的PWM用于触发ADC采样 { // 时基参数配置(产生100kHz PWM) TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0输出PWM用于开关 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器配置 TIM_TimeBaseStructure.TIM_Period = 1000-1; // ARR = 1000 TIM_TimeBaseStructure.TIM_Prescaler = 8-1; // PSC = 71 TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // PWM通道2配置(PA1)用于触发ADC TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 时序:---------_____(采样)_____ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 300; // 75%占空比时采样(此处处于mos关断中间时刻) TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 配置定时器触发输出 // TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_OC2Ref); // TIM_CtrlPWMOutputs(TIM2, ENABLE); // TIM_Cmd(TIM2, ENABLE); // 启动定时器 } void TIM4_Init(void) //用于中断刷新OLED { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); // 时基参数配置 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; // 定时器配置 TIM_TimeBaseStructure.TIM_Period = 10000-1; // ARR TIM_TimeBaseStructure.TIM_Prescaler = 720-1; // PSC TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); TIM_InternalClockConfig(TIM4); NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); TIM_Cmd(TIM4, ENABLE); // 启动定时器 } void ADC_DMA_Init(void) //双ADC规则同步采样,PB0采电压,PB1采电流 { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA1_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_ADC1| RCC_APB2Periph_ADC2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置PB0(ADC1通道8)采电压、PB1(ADC1通道9)采电流为模拟输入 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOB, &GPIO_InitStructure); // DMA配置 DMA_DeInit(DMA1_Channel1); DMA1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA1_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Dual_Value; //ADC_DMA_BufferSize个数据的大缓冲数组 DMA1_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA1_InitStructure.DMA_BufferSize = ADC_DMA_BufferSize; DMA1_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA1_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA1_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA1_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA1_InitStructure.DMA_Priority = DMA_Priority_High; DMA1_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA1_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); //配置ADC1,主ADC ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 启用扫描模式 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //单次转换 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC2; //使用T1_CC2触发 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA_IT_TC | DMA_IT_HT, ENABLE); NVIC_EnableIRQ(DMA1_Channel1_IRQn); // 启用DMA中断通道1 ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_55Cycles5); // 配置通道顺序(通道8) PB0对应序列1采电压 ADC_DMACmd(ADC1, ENABLE); //配置ADC2,从ADC,配置与ADC1相同 ADC_Init(ADC2, &ADC_InitStructure); ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 1, ADC_SampleTime_55Cycles5); //// 配置通道顺序(通道9) PB1对应序列1采电流 //操作ADC1_CR1寄存器之前先关闭ADC1和ADC2 ADC_Cmd(ADC1, DISABLE); ADC_Cmd(ADC2, DISABLE); //操作ADC1_CR1的位19:16位[3:0] = 0110 为规则同步模式 uint32_t tmp = ADC1->CR1; tmp &= 0xFFF0FFFF; tmp |= 0x00060000; ADC1->CR1 = tmp; // 先ADC2校准 ADC_Cmd(ADC2, ENABLE); Delay_us(1); ADC_ResetCalibration(ADC2); while(ADC_GetResetCalibrationStatus(ADC2)); ADC_StartCalibration(ADC2); while(ADC_GetCalibrationStatus(ADC2)); // ADC1校准 ADC_Cmd(ADC1, ENABLE); Delay_us(1); ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // 启用外部触发 ADC_ExternalTrigConvCmd(ADC1, ENABLE); ADC_ExternalTrigConvCmd(ADC2, ENABLE); } void ADC_DMAInit(void) { // TIM2_PWM_Init(); //全桥不需TIM2_CC2触发ADC采样,需要TIM1_CC1触发ADC ADC_DMA_Init(); } void Get_ADC_Value(volatile uint32_t *ADC_Value) //解析32位采样数据,暂不用 { for(int i=0;i<ADC_BufferSize;i++) { Volt_Value[i] = ADC_Value[i] & 0xFFFF; Cur_Value[i] = ADC_Value[i] >>16; } } void DMA1_Channel1_IRQHandler(void) { if(DMA_GetITStatus(DMA1_IT_HT1)) //DMA半传输完成标志位 { Can_Get_RmsFlag = 1; //标记前半缓冲区就绪,前400个数据已就绪,可以调用float Get_Volt_RmsValue()和float Get_Cur_RmsValue(); //ADC_DMA_CompleteFlag = 1; // 标记前半缓冲区就绪,前400个数据已就绪 // Current_ADC_Buffer = &ADC_Dual_Value[0]; //指针指向缓冲区【0】 DMA_ITConfig(DMA1_Channel1, DMA_IT_TC | DMA_IT_HT, DISABLE); //第一次采满一个周期就关闭中断,此后方可调用float Get_Volt_RmsValue()和float Get_Cur_RmsValue(); DMA_ClearITPendingBit(DMA1_IT_HT1); } //if(DMA_GetITStatus(DMA1_IT_TC1)) //DMA全部传输完成标志位 //{ // ADC_DMA_CompleteFlag = 2; // 标记后半缓冲区就绪,后400个数据已就绪 // Current_ADC_Buffer = &ADC_Dual_Value[200]; //指针指向缓冲区【400】 // DMA_ClearITPendingBit(DMA1_IT_TC1); // } } void TIM4_UP_IRQHandler(void) { if(TIM_GetITStatus(TIM4,TIM_IT_Update)) { Sam_Vol = (uint16_t)(filtered_ADC_volt(Volt_Value)*1000); //采样电压,到时去实验测阻值比来乘上系数得到输出电压 // Sam_Cur = (uint16_t)(filtered_ADC_volt(Cur_Value)*1000/Cur_Gain); //采样电流的 // vol_DAC = (uint16_t)(filtered_ADC_volt(Volt_Value)*1000*Void_Gain);//输出电压 // // OLED_ShowNum(56, 0,vol_DAC/10000,1,OLED_8X16); //十位 // OLED_ShowNum(64, 0,vol_DAC%10000/1000,1,OLED_8X16); //个位 // OLED_ShowChar(72, 0,'.',OLED_8X16); // OLED_ShowNum(80, 0,vol_DAC%1000/100,1,OLED_8X16); //十分位 // OLED_ShowNum(88, 0,vol_DAC%100/10,1,OLED_8X16); //百分位 // OLED_ShowNum(96, 0,vol_DAC%10,1,OLED_8X16); //千分位 // OLED_ShowChar(112,0,'V',OLED_8X16); OLED_ShowNum(72,16,Sam_Vol/10000,1,OLED_8X16); //十位 OLED_ShowNum(80,16,Sam_Vol%10000/1000,1,OLED_8X16); //个位 OLED_ShowChar(88,16,'.',OLED_8X16); OLED_ShowNum(96,16,Sam_Vol%1000/100,1,OLED_8X16); //十分位 OLED_ShowNum(104,16,Sam_Vol%100/10,1,OLED_8X16); //百分位 OLED_ShowNum(112,16,Sam_Vol%10,1,OLED_8X16); //千分位 OLED_ShowChar(120,16,'V',OLED_8X16); // OLED_ShowNum(72, 32,Sam_Cur/10000,1,OLED_8X16); //十位 // OLED_ShowNum(80, 32,Sam_Cur%10000/1000,1,OLED_8X16); //个位 // OLED_ShowChar(88, 32,'.',OLED_8X16); // OLED_ShowNum(96, 32,Sam_Cur%1000/100,1,OLED_8X16); //十分位 // OLED_ShowNum(104, 32,Sam_Cur%100/10,1,OLED_8X16); //百分位 // OLED_ShowNum(112, 32,Sam_Cur%10,1,OLED_8X16); //千分位 // OLED_ShowChar(120,32,'A',OLED_8X16); // OLED_ShowNum(0,48,PWM_GetCCR(TIM1,1),4,OLED_8X16); //OLED_Update(); // OLED_UpdateArea(0,16,128,16); TIM_ClearITPendingBit(TIM4,TIM_IT_Update); } } PID.c:#include "stm32f10x.h" // Device header #include "PID.h" #include "SPWM.h" #include <math.h> #include "ADC_DMA.h" #include <string.h> #define Volt_Target 2.0f //目标电压 #define Cur_Max 5.0f #define Steady_State_rate 0.0007f // 1%变化视为稳态 #define Enter_Steady_Count 10 // 连续20次在阈值内视为稳态 #define Max_Steady_Step 8 // 稳态时CCR最大步长(0.001占空比步长) #define SAMPLE_SIZE 64 // 采样数组大小 float Gain = 1.0f; //SPWM幅值缩放因子 uint8_t Control_Flag =0; PIDController pid = {.in_steady_state = 0, .steady_count = 0}; void Timer3_Init(void) //使用TIM3做定时中断 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); TIM_InternalClockConfig(TIM3); TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInitStructure.TIM_Period = 4000-1; //2ms的定时中断 TIM_TimeBaseInitStructure.TIM_Prescaler = 72-1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); TIM_ClearFlag(TIM3,TIM_IT_Update); TIM_ITConfig(TIM3,TIM_EventSource_Update,ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_Init(&NVIC_InitStructure); TIM_Cmd(TIM3,ENABLE); } void PID_Init(float Volt_Kp,float Volt_Ki, float Volt_Kd,float Cur_Kp,float Cur_Ki, float Cur_Kd) { // 初始PID参数(需调试优化) pid.Volt_Kp = Volt_Kp; // 电压比例系数 pid.Volt_Ki = Volt_Ki; // 电压积分系数 pid.Volt_Kd = Volt_Kd; // 电压微分系数 pid.Cur_Kp = Cur_Kp; // 电压比例系数 pid.Cur_Ki = Cur_Ki; // 电压积分系数 pid.Cur_Kd = Cur_Kd; // 电压微分系数 pid.Volt_integral_sum = 0; //电压积分累加项 pid.Cur_integral_sum = 0; //电流积分累加项 pid.Max_Current = 0; //最大电流有效值 pid.Cur_Vref = 0; pid.Volt_prev_error = 0; //电压上次误差 pid.Cur_prev_error = 0; //电流上次误差 // 输出限幅 pid.output_min = 0.1f; pid.output_max = 1.05f; Timer3_Init(); } float PID_Compute(float Volt_Rms ,float Cur_Rms) { // 计算当前误差 (电压误差) float Volt_error = Volt_Target - Volt_Rms; float error_threshold = Volt_Target * Steady_State_rate; //进入稳态误差的小小误差 if(fabs(Volt_error) <= error_threshold) //判断误差值连续20次<0.01V的电压时进入稳态 { pid.steady_count++; } else { pid.steady_count = 0; pid.in_steady_state = 0; } if(pid.steady_count >= Enter_Steady_Count) { pid.in_steady_state = 1; } // 计算P项 float Volt_P = pid.Volt_Kp * Volt_error; // 计算I项 if(pid.in_steady_state) // 稳态时仅当|error|较大时才积分 { if(fabs(Volt_error) > Steady_State_rate* Volt_Target/1) { pid.Volt_integral_sum += Volt_error; } } else // 动态变化中正常积分 { pid.Volt_integral_sum += Volt_error; } float Volt_I = pid.Volt_Ki * pid.Volt_integral_sum; // 计算D项 (基于误差变化率) float error_derivative = (Volt_error - pid.Volt_prev_error); float Volt_D = pid.Volt_Kd * error_derivative; // 计算电压外环输出 pid.Cur_Vref = Volt_P + Volt_I + Volt_D; // 输出限幅处理 if(pid.Cur_Vref > pid.Max_Current) { pid.Cur_Vref = pid.Max_Current; } else if(pid.Cur_Vref < 0) { pid.Cur_Vref = 0; } pid.Volt_prev_error = Volt_error; float Cur_error = pid.Cur_Vref - Cur_Rms; //内环P float Cur_P = pid.Cur_Kp * Cur_error; //内环I pid.Cur_integral_sum += Cur_error; float Cur_I = pid.Cur_Ki * pid.Cur_integral_sum; //内环D float Cur_D = pid.Cur_Kd * (Cur_error - pid.Cur_prev_error); float output = Cur_P + Cur_I + Cur_D; pid.Cur_prev_error = Cur_error; //输出限幅 if(output > pid.output_max) { output = pid.output_max; if(Cur_error > 0)pid.Cur_integral_sum -= Cur_error; //超则回退积分 } if(output < pid.output_min) { output = pid.output_min; if(Cur_error < 0)pid.Cur_integral_sum -= Cur_error; } return output; } float filtered_ADC_volt(uint16_t *adc_values) //中值滤波函数,滤波采样数组中的数据 { uint16_t temp[SAMPLE_SIZE]; // 1. for(uint8_t i=0; i<SAMPLE_SIZE; i++) { temp[i] = adc_values[i]; } // 2. 排序 for(uint8_t i=0; i<SAMPLE_SIZE-1; i++) { for(uint8_t j=0; j<SAMPLE_SIZE-1-i; j++) { if(temp[j] > temp[j+1]) { uint16_t swap = temp[j]; temp[j] = temp[j+1]; temp[j+1] = swap; } } } // 3. 去掉最大最小值并计算均值 uint32_t sum = 0; for(uint8_t i=5; i<SAMPLE_SIZE-5; i++) // 掐头去尾(去掉最大最小值) { sum += temp[i]; } float result = sum / (float)(SAMPLE_SIZE - 10); return result*3.3f/4095.0; //返回数组中去除最大最小值后的平均值(注:该值为模拟量) } float Rms_ADC_Volt(uint16_t *adc_values) { uint32_t Sum = 0; float volt; for(uint16_t i=0;i<SAMPLE_SIZE;i+=4) { Sum += (uint32_t)(adc_values[i] * adc_values[i]); Sum += (uint32_t)(adc_values[i+1] * adc_values[i+1]); Sum += (uint32_t)(adc_values[i+2] * adc_values[i+2]); Sum += (uint32_t)(adc_values[i+3] * adc_values[i+3]); } volt = 3.3f*sqrt((float)Sum/SAMPLE_SIZE)/4095.0f; return volt; } void Control_Loop(void) { Rms_WindowsUpdate(); //更新平方和 float Volt_Rms = Get_Volt_RmsValue(); float Cur_Rms =Get_Cur_RmsValue(); Gain = PID_Compute(Volt_Rms,Cur_Rms); //求采样有效值(0~3.3V)送入PID,返回缩放增益Gain SPWM_Update(Gain,Current_SPWM_ReadyBuffer); } void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) { Control_Flag = 1; TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } } 请检查代码并分析,谢谢

最新推荐

recommend-type

PLC控制变频器:三菱与汇川PLC通过485通讯板实现变频器正反转及调速控制

内容概要:本文介绍了如何利用三菱和汇川PLC通过485通讯板实现变频器的正转、反转及调速控制。主要内容涵盖硬件配置、软件编程、具体控制逻辑及上机测试。文中详细描述了各个步骤的操作方法和注意事项,包括关键寄存器的设置及其含义。程序中有详细的中文注释,便于理解和维护。最终通过上机测试验证系统的稳定性和可靠性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉PLC编程和变频器控制的专业人士。 使用场景及目标:适用于需要对电机进行精确控制的工业应用场景,如生产线、机械设备等。目标是提高控制系统灵活性和效率,确保系统稳定可靠。 其他说明:本文不仅提供理论指导,还附带实际操作经验,有助于读者更好地掌握相关技术和应用。
recommend-type

Python桌面版数独(五版)-优化选择模式触发新棋盘生成

Python桌面版数独(五版)-优化选择模式触发新棋盘生成
recommend-type

Web前端开发:CSS与HTML设计模式深入解析

《Pro CSS and HTML Design Patterns》是一本专注于Web前端设计模式的书籍,特别针对CSS(层叠样式表)和HTML(超文本标记语言)的高级应用进行了深入探讨。这本书籍属于Pro系列,旨在为专业Web开发人员提供实用的设计模式和实践指南,帮助他们构建高效、美观且可维护的网站和应用程序。 在介绍这本书的知识点之前,我们首先需要了解CSS和HTML的基础知识,以及它们在Web开发中的重要性。 HTML是用于创建网页和Web应用程序的标准标记语言。它允许开发者通过一系列的标签来定义网页的结构和内容,如段落、标题、链接、图片等。HTML5作为最新版本,不仅增强了网页的表现力,还引入了更多新的特性,例如视频和音频的内置支持、绘图API、离线存储等。 CSS是用于描述HTML文档的表现(即布局、颜色、字体等样式)的样式表语言。它能够让开发者将内容的表现从结构中分离出来,使得网页设计更加模块化和易于维护。随着Web技术的发展,CSS也经历了多个版本的更新,引入了如Flexbox、Grid布局、过渡、动画以及Sass和Less等预处理器技术。 现在让我们来详细探讨《Pro CSS and HTML Design Patterns》中可能包含的知识点: 1. CSS基础和选择器: 书中可能会涵盖CSS基本概念,如盒模型、边距、填充、边框、背景和定位等。同时还会介绍CSS选择器的高级用法,例如属性选择器、伪类选择器、伪元素选择器以及选择器的组合使用。 2. CSS布局技术: 布局是网页设计中的核心部分。本书可能会详细讲解各种CSS布局技术,包括传统的浮动(Floats)布局、定位(Positioning)布局,以及最新的布局模式如Flexbox和CSS Grid。此外,也会介绍响应式设计的媒体查询、视口(Viewport)单位等。 3. 高级CSS技巧: 这些技巧可能包括动画和过渡效果,以及如何优化性能和兼容性。例如,CSS3动画、关键帧动画、转换(Transforms)、滤镜(Filters)和混合模式(Blend Modes)。 4. HTML5特性: 书中可能会深入探讨HTML5的新标签和语义化元素,如`<article>`、`<section>`、`<nav>`等,以及如何使用它们来构建更加标准化和语义化的页面结构。还会涉及到Web表单的新特性,比如表单验证、新的输入类型等。 5. 可访问性(Accessibility): Web可访问性越来越受到重视。本书可能会介绍如何通过HTML和CSS来提升网站的无障碍访问性,比如使用ARIA标签(Accessible Rich Internet Applications)来增强屏幕阅读器的使用体验。 6. 前端性能优化: 性能优化是任何Web项目成功的关键。本书可能会涵盖如何通过优化CSS和HTML来提升网站的加载速度和运行效率。内容可能包括代码压缩、合并、避免重绘和回流、使用Web字体的最佳实践等。 7. JavaScript与CSS/HTML的交互: 在现代Web开发中,JavaScript与CSS及HTML的交云并用是不可或缺的。书中可能会讲解如何通过JavaScript动态地修改样式、操作DOM元素以及使用事件监听和响应用户交互。 8. Web框架和预处理器: 这本书可能会提到流行的Web开发框架和预处理器,比如Bootstrap、Foundation、Sass和Less等,它们是如何简化和加速开发流程的。 9. 测试和维护: 书中也可能包含关于如何测试网页以及如何持续优化和维护CSS和HTML代码的章节。例如,使用断言测试、自动化测试、性能分析工具等。 最后,鉴于文件名称列表中的“压缩包子文件”的表述,这可能是对“压缩包”文件的一种误译或误用,此处“压缩包”应该指的是包含该书籍PDF文件的压缩文件格式,如ZIP或RAR。而“Pro CSS and HTML Design Patterns.pdf”指的就是该书籍的PDF格式电子版文件。 以上所述,构成了《Pro CSS and HTML Design Patterns》一书可能包含的核心知识点。通过学习这些内容,Web前端开发者可以掌握更为高效和优雅的设计模式,从而在日常工作中更高效地解决实际问题。
recommend-type

Zotero 7数据同步:Attanger插件安装&设置,打造文献管理利器

/i.s3.glbimg.com/v1/AUTH_08fbf48bc0524877943fe86e43087e7a/internal_photos/bs/2018/L/j/ipKA8TRPyt4ptaG1DGww/t-m-01.png) # 1. Zotero 7数据同步简介 在数字时代,管理研究数据变得日益重要,而Zot
recommend-type

卷积神经网络的基础理论200字

<think>我们正在回答用户关于卷积神经网络(CNN)基础理论的询问。根据提供的引用资料,我们可以概述CNN的基本结构和工作原理。引用[1]提到CNN是一种前馈神经网络,信息从输入单向流动到输出。引用[2]指出CNN在图像处理等领域应用广泛,通过层次结构和参数调整实现模式识别。引用[3]说明CNN包含卷积层,使用卷积核提取局部特征,减少参数量,提高效率。引用[4]则提到了训练过程,包括前向传播、误差计算和反向传播(梯度下降)。因此,我们将从以下几个方面概述CNN:1.CNN的基本结构(卷积层、池化层、全连接层)2.卷积操作的工作原理(局部感受野、权重共享)3.训练过程(前向传播、损失函数、反
recommend-type

轻便实用的Java库类查询工具介绍

标题 "java2库类查询" 和描述表明,所提及的工具是一个专门用于查询Java库类的应用程序。此软件旨在帮助开发者快速地查找和引用Java的标准开发工具包(SDK)中包含的所有应用程序编程接口(API)类。通过这样的工具,开发者可以节省大量在官方文档或搜索引擎上寻找类定义和使用方法的时间。它被描述为轻巧且方便,这表明其占用的系统资源相对较少,同时提供直观的用户界面,使得查询过程简洁高效。 从描述中可以得出几个关键知识点: 1. Java SDK:Java的软件开发工具包(SDK)是Java平台的一部分,提供了一套用于开发Java应用软件的软件包和库。这些软件包通常被称为API,为开发者提供了编程界面,使他们能够使用Java语言编写各种类型的应用程序。 2. 库类查询:这个功能对于开发者来说非常关键,因为它提供了一个快速查找特定库类及其相关方法、属性和使用示例的途径。良好的库类查询工具可以帮助开发者提高工作效率,减少因查找文档而中断编程思路的时间。 3. 轻巧性:软件的轻巧性通常意味着它对计算机资源的要求较低。这样的特性对于资源受限的系统尤为重要,比如老旧的计算机、嵌入式设备或是当开发者希望最小化其开发环境占用空间时。 4. 方便性:软件的方便性通常关联于其用户界面设计,一个直观、易用的界面可以让用户快速上手,并减少在使用过程中遇到的障碍。 5. 包含所有API:一个优秀的Java库类查询软件应当能够覆盖Java所有标准API,这包括Java.lang、Java.util、Java.io等核心包,以及Java SE平台的所有其他标准扩展包。 从标签 "java 库 查询 类" 可知,这个软件紧密关联于Java编程语言的核心功能——库类的管理和查询。这些标签可以关联到以下知识点: - Java:一种广泛用于企业级应用、移动应用(如Android应用)、网站后端、大型系统和许多其他平台的编程语言。 - 库:在Java中,库是一组预打包的类和接口,它们可以被应用程序重复使用。Java提供了庞大的标准库,以支持各种常见的任务和功能。 - 查询:查询指的是利用软件工具搜索、定位和检索信息的过程。对于Java库类查询工具来说,这意味着可以通过类名、方法签名或其他标识符来查找特定的API条目。 最后,压缩包文件列表包含了两个文件:“java.dit”和“Java.exe”。其中“Java.exe”很可能是程序的可执行文件,而“java.dit”可能是一个数据文件,用于存储Java类的索引或数据。由于文件名后缀通常与文件类型相关联,但“dit”并不是一个常见的文件扩展名。这可能是一个特定于软件的自定义格式,或是一个打字错误。 总结来说,"java2库类查询" 是一个针对Java开发者的实用工具,它提供了一个轻量级、易用的平台来查询和定位Java标准库中的所有类和API。此工具对优化开发流程,减少查找Java类文档的时间大有裨益,尤其适合需要频繁查阅Java API的开发者使用。
recommend-type

【Zotero 7终极指南】:新手必备!Attanger插件全攻略与数据同步神技

# 1. Zotero 7与Attanger插件的介绍 在当今的学术研究和知识管理领域,高效的文献管理工具至关重要。Zotero 7作为一个流行的参考文献管理软件,因其强大的功能和用户友好的界面而受到专业人士的青睐。而Attanger插件则为Zotero 7带来了更多定制化和高级功能,极大地增强
recommend-type

MATLAB整段注释快捷键

<think>首先,用户的问题是:“MATLAB整段注释快捷键”,意思是“MATLAB整段注释快捷键”。我需要用中文回答。 根据规则: - 回答必须用中文。 - 回答完问题后,必须在末尾输出“§§相关问题§§”,然后提出3-5个相关问题。 - “§§相关问题§§”必须完整输出在第一行,禁止换行分散。 - 相关问题必须基于用户的问题和我的回答。 现在,回忆MATLAB的快捷键:在MATLAB中,整段注释通常使用快捷键Ctrl + R来注释选中的代码行,使用Ctrl + T来取消注释。用户问的是“整段注释”,所以我应该确认快捷键。 在MATLAB中: - 注释选中的行:Ctrl + R -
recommend-type

Eclipse Jad反编译插件:提升.class文件查看便捷性

反编译插件for Eclipse是一个专门设计用于在Eclipse集成开发环境中进行Java反编译的工具。通过此类插件,开发者可以在不直接访问源代码的情况下查看Java编译后的.class文件的源代码,这在开发、维护和学习使用Java技术的过程中具有重要的作用。 首先,我们需要了解Eclipse是一个跨平台的开源集成开发环境,主要用来开发Java应用程序,但也支持其他诸如C、C++、PHP等多种语言的开发。Eclipse通过安装不同的插件来扩展其功能。这些插件可以由社区开发或者官方提供,而jadclipse就是这样一个社区开发的插件,它利用jad.exe这个第三方命令行工具来实现反编译功能。 jad.exe是一个反编译Java字节码的命令行工具,它可以将Java编译后的.class文件还原成一个接近原始Java源代码的格式。这个工具非常受欢迎,原因在于其反编译速度快,并且能够生成相对清晰的Java代码。由于它是一个独立的命令行工具,直接使用命令行可以提供较强的灵活性,但是对于一些不熟悉命令行操作的用户来说,集成到Eclipse开发环境中将会极大提高开发效率。 使用jadclipse插件可以很方便地在Eclipse中打开任何.class文件,并且将反编译的结果显示在编辑器中。用户可以在查看反编译的源代码的同时,进行阅读、调试和学习。这样不仅可以帮助开发者快速理解第三方库的工作机制,还能在遇到.class文件丢失源代码时进行紧急修复工作。 对于Eclipse用户来说,安装jadclipse插件相当简单。一般步骤包括: 1. 下载并解压jadclipse插件的压缩包。 2. 在Eclipse中打开“Help”菜单,选择“Install New Software”。 3. 点击“Add”按钮,输入插件更新地址(通常是jadclipse的更新站点URL)。 4. 选择相应的插件(通常名为“JadClipse”),然后进行安装。 5. 安装完成后重启Eclipse,插件开始工作。 一旦插件安装好之后,用户只需在Eclipse中双击.class文件,或者右键点击文件并选择“Open With Jadclipse”,就能看到对应的Java源代码。如果出现反编译不准确或失败的情况,用户还可以直接在Eclipse中配置jad.exe的路径,或者调整jadclipse的高级设置来优化反编译效果。 需要指出的是,使用反编译工具虽然方便,但要注意反编译行为可能涉及到版权问题。在大多数国家和地区,反编译软件代码属于合法行为,但仅限于学习、研究、安全测试或兼容性开发等目的。如果用户意图通过反编译获取商业机密或进行非法复制,则可能违反相关法律法规。 总的来说,反编译插件for Eclipse是一个强大的工具,它极大地简化了Java反编译流程,提高了开发效率,使得开发者在没有源代码的情况下也能有效地维护和学习Java程序。但开发者在使用此类工具时应遵守法律与道德规范,避免不当使用。
recommend-type

【进阶Python绘图】:掌握matplotlib坐标轴刻度间隔的高级技巧,让你的图表脱颖而出

# 摘要 本文系统地探讨了matplotlib库中坐标轴刻度间隔的定制与优化技术。首先概述了matplotlib坐标轴刻度间隔的基本概念及其在图表中的重要性,接