痞酷網_PIGOO

 找回密碼
 立即註冊
!!! [系統偵測到廣告阻擋軟體] !!!

如果您覺得痞酷網對您有些許幫助,或者您認同痞酷網的理想,

那麼希望您將痞酷網設定為白名單.

並請在上論壇的時候,動動您的手指,用行動支持我們.

謝謝!
查看: 283|回復: 9

带数显电压显示的自动升降压模块修复

[複製鏈接]
發表於 4 天前 | 顯示全部樓層 |閱讀模式
几年前用一块带LED数显电压的自动升降压(Boost-Bucker)模块将48VDC转成12V给爱车电瓶补电时,一不小心烧死了它的电压数显部分……因为该模块的数字电压表是由单片机STM8S003F3P6和三位LED数码管(共阴极)等构成,所以单单搞一块MCU上去是万万不行的。而当时自己对STM8系列单片机的编程可以说是毫无经验的,因此只能将模块丢一边从长计议,但心中执念尚存。

説到执念,的确,俺对编程这东西甚是感兴趣,认为能编程的大拿真的很酷——一番行云流水后,硅片中注入他的思想,就像是让硅基生命顿时获得了灵魂一样。可是,俺的脑瓜子不怎么灵活,也比较懒散,羡慕他人却没那恒心。年轻时学了一阵子汇编,没继续深入,就这么荒废了;N年前,见着人家玩STM32烧录器,手痒跟进了一段时间,照猫画虎成功后热度退却也没深入,叕荒废了……

但如今为了恢复这块损坏的模块,也算是对自己的挑战吧,把STM8系列MCU开发用到的软件、硬件一一搞来,熟悉了一阵子。后来眼见自己又要打退堂鼓,暗下决心这次一定要完成任务。于是就有了这篇啰嗦文。

言归正传,在网上兜兜转转正巧找到一篇关于怎样魔改数显电压表的文章,该作者是将一块廉价4位LED数显电压表改成了电流表,该电压表也是采用STM8S003F3P6,因此程序有相当高的参考性。经过多日断断续续的研究,了解其中每一个语句的作用,了解了ADC、Timer、中断……最后慢慢调整以将其适配自己那个DC-DC模块。

时至今日,总算完成所有程序改造,基本驯服了这个小小的硅基生命体,让整个DC-DC模块重获新生。期间尽管遇到很多困难,但配合AI问答都一一解决,让自己又学到很多东西,太棒了!不得不说,如今的AI会话在编程辅助方面真得很赞。

最终的效果如下:

20250103_213446(BoostBucker).gif


下边是它的原理图。

三位LED数码管显示双路电压部分:


3digitsVoltmeter.png

Boost-Bucker 部分:

BoostBucker.png

以下是俺的程序部分:

Main.C

  1. #include "stm8s.h"
  2. #include "led.h"

  3. #define LED_delay (1)     // one digit emitting time
  4. #define ADC_delay (50)    // delay for reading voltage
  5. #define Display_delay (400) // delay for displaying value

  6. uint32_t Global_time = 0L;    // global time in ms
  7. uint32_t ADC_time = 0L;
  8. uint32_t Display_time = 0L;
  9. uint8_t waitforADC = 0;
  10. uint8_t ADC_started = 0;
  11. uint16_t ADC_values[3] = { 0, 0, 0 };
  12. uint8_t index = 0;

  13. uint8_t current_channel = 1; // 0: AIN3, 1: AIN4

  14. void simpleDelay(uint8_t how_much);

  15. uint16_t adc_read(void)
  16. {
  17.     uint16_t temph = 0;
  18.     uint8_t templ = 0;

  19.     ADC1->CR1 |= ADC1_CR1_ADON; // wake it up
  20.     ADC1->CR1 |= ADC1_CR1_ADON; // start the conversion, wait few ms
  21.     simpleDelay(1);
  22.     while (!(ADC1->CSR & ADC1_CSR_EOC)); // wait for conversion to finish

  23.     /* Read LSB first */
  24.     templ = ADC1->DRL;
  25.     /* Then read MSB */
  26.     temph = ADC1->DRH;   
  27.     temph = (uint16_t)(templ | (uint16_t)(temph << (uint8_t)8));
  28.     ADC1->CR1 &= (uint8_t)(~ADC1_CR1_ADON); // power it off
  29.     return (uint16_t)temph;
  30. }

  31. // store ADC value in a rolling array
  32. void store(uint16_t value)
  33. {
  34.     ADC_values[index++] = value; // store value in array
  35.     if (index == 3) index = 0; // check index overflow
  36. }

  37. // get median value
  38. uint16_t median(uint16_t value1, uint16_t value2, uint16_t value3)
  39. {
  40.     if ((value1 <= value2) && (value1 <= value3))
  41.     {
  42.         return (value2 <= value3) ? value2 : value3;
  43.     }
  44.     else
  45.     {
  46.         if ((value2 <= value1) && (value2 <= value3))
  47.         {
  48.             return (value1 <= value3) ? value1 : value3;
  49.         }
  50.         else
  51.         {
  52.             return (value1 <= value2) ? value1 : value2;
  53.         }
  54.     }
  55. }

  56. // calculate average value
  57. uint16_t average(uint16_t value1, uint16_t value2, uint16_t value3)
  58. {
  59.     uint16_t value = 0;
  60.     uint8_t count = 0;

  61.     if (value1 > 0)
  62.     {
  63.         value += value1;
  64.         count++;
  65.     }
  66.     if (value2 > 0)
  67.     {
  68.         value += value2;
  69.         count++;
  70.     }   
  71.     if (value3 > 0)
  72.     {
  73.         value += value3;
  74.         count++;
  75.     }   
  76.     if (count > 0) value /= count;

  77.     return value;
  78. }

  79. // ADC initialization
  80. void ADC_init()
  81. {
  82.     // Initialize ADC
  83.     // Setup ADC on AIN6/PD6, AIN4/PD3, AIN3/PD2
  84.     GPIOD->DDR &= (uint8_t)(0b10110011); // Set PD2/3/6 as floating input
  85.     GPIOD->CR1 &= (uint8_t)(0b10110011);

  86.     ADC1->CR1 &= (uint8_t)(~ADC1_CR1_CONT); // Set single conversion mode
  87.     ADC1->CR1 &= (uint8_t)(~ADC1_CR1_SPSEL); // Clear the SPSEL bits (prescaler)
  88.     ADC1->CR1 |= 0x40; // Select the prescaler division factor

  89.     ADC1->CR2 |= ADC1_CR2_ALIGN; // Configure right data alignment
  90.     ADC1->CR2 &= (uint8_t)(~ADC1_CR2_EXTTRIG); // Disable external trigger
  91.     ADC1->CR2 &= (uint8_t)(~ADC1_CR2_EXTSEL); // Clear external trigger selection bits
  92.     ADC1->CR2 &= (uint8_t)(~ADC1_CR2_SCAN); // Disable scan mode

  93.     ADC1->CSR = 0x06; // Select AIN6 as initial channel
  94. }

  95. void main()
  96. {
  97.     uint32_t T_LED = 0L; // time of last digit update
  98.     uint16_t ADC_value = 0;
  99.     uint16_t value = 0, vref = 0, value1 = 0, value2 = 0, value3 = 0;
  100.     uint8_t i, count = 0;

  101.     ADC_init();

  102.     // Setup Timer1
  103.     TIM1->PSCRH = 0;
  104.     TIM1->PSCRL = 15; // LSB should be written last
  105.     TIM1->ARRH = 0x03; // auto-reload each 1ms
  106.     TIM1->ARRL = 0xE8;
  107.     TIM1->IER = TIM1_IER_UIE;
  108.     TIM1->CR1 = TIM1_CR1_ARPE | TIM1_CR1_URS | TIM1_CR1_CEN;

  109.     // Configure clocking
  110.     CLK->CKDIVR = 0; // F_HSI = 16MHz

  111.     // Configure pins
  112.     CFG->GCR |= 1; // disable SWIM

  113.     LED_init();
  114.     set_display_buf("000");

  115.     Button_init();
  116.     enableInterrupts();

  117.     while (1)
  118.     {
  119.         if (((uint16_t)(Global_time - ADC_time) > ADC_delay) || (ADC_time > Global_time))
  120.         {
  121.             ADC_time = Global_time;

  122.             // read three values and get median
  123.             value1 = adc_read();
  124.             value2 = adc_read();
  125.             value3 = adc_read();
  126.             vref = median(value1, value2, value3);
  127.             
  128.             if (current_channel)
  129.             {
  130.                 ADC1->CSR = (ADC1->CSR & 0xF0) | 0x04; // select AIN4
  131.                 GPIOA->ODR &= (uint8_t)(0b11111011); // light blue LED
  132.             }
  133.             else
  134.             {
  135.                 ADC1->CSR = (ADC1->CSR & 0xF0) | 0x03; // select AIN3
  136.                 GPIOA->ODR |= (uint8_t)(0x04); // light red LED
  137.             }
  138.             
  139.             value1 = adc_read();
  140.             value2 = adc_read();
  141.             value3 = adc_read();
  142.             value = median(value1, value2, value3);
  143.             store(value * (long)3120 / vref); // avoid uint16_t overflow
  144.             ADC1->CSR = 0x06; // restore AIN6 as sampling channel
  145.         }

  146.         if ((uint16_t)(Global_time - Display_time) > Display_delay)
  147.         {
  148.             Display_time = Global_time;
  149.             value = average(ADC_values[0], ADC_values[1], ADC_values[2]);
  150.             display_int(value, 1); // allow auto decimal point
  151.         }

  152.         if (Display_time > Global_time) Display_time = Global_time; // reset Display_time when Global_time overflows
  153.         
  154.         if ((uint8_t)(Global_time - T_LED) > LED_delay)
  155.         {
  156.             T_LED = Global_time;
  157.             show_next_digit();
  158.         }

  159.         // used for non-blocking read
  160.         if (ADC_started)
  161.         {
  162.             wait_ADC();
  163.         }
  164.         if (waitforADC)
  165.         {
  166.             ADC_value = adc_read_end();        
  167.             store(value * (long)100 * 3.3 / 1024); // store current voltage value
  168.             waitforADC = 0;
  169.         }
  170.     }
  171. }

  172. // keep the processor busy for some time
  173. void simpleDelay(uint8_t how_much)
  174. {
  175.     unsigned int i, j;
  176.     for (i = 0; i < how_much; i++)
  177.     {
  178.         for (j = 0; j < 40; j++)
  179.         {
  180.         }
  181.     }
  182. }

  183. // Timer1 Update/Overflow/Trigger/Break Interrupt
  184. INTERRUPT_HANDLER(TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11)
  185. {
  186.     if (TIM1->SR1 & TIM1_SR1_UIF) // update interrupt
  187.     {
  188.         Global_time++; // increase timer
  189.     }
  190.     TIM1->SR1 = 0; // clear all interrupt flags
  191. }

  192. INTERRUPT_HANDLER(EXTI_PORTA_IRQHandler, 3)
  193. {
  194.     if ((GPIOA->IDR & 0x08) == 0)
  195.     {
  196.         // delay 2ms for debounce
  197.         unsigned int i, j;
  198.         for (i = 0; i < 50; i++)
  199.         {
  200.             for (j = 0; j < 40; j++) {}
  201.         }

  202.         // release detection
  203.         while ((GPIOA->IDR & 0x08) == 0) {}

  204.         // toggle ADC channel
  205.         current_channel = !current_channel;
  206.     }
  207. }
複製代碼

LED.C

  1. #include "stm8s.h"
  2. #include "led.h"

  3. #define GPIO_PIN3 0b00001000 // 处理小数点用到

  4. /*
  5. *   ***A***             Now(Org)   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  -  h
  6. *   *     *         (F) PC7(PB5)   0  1  1  1  0  0  0  1  0  0  0  0  0  1  0  0  1  0
  7. *   F     B         (B) PD1(PC5)   0  0  0  0  0  1  1  0  0  0  0  1  1  0  1  1  1  1
  8. *   *     *         (A) PC6(PB4)   0  1  0  0  1  0  0  0  0  0  0  1  0  1  0  0  1  1
  9. *   ***G***         (G) PB5(PC6)   1  1  0  0  0  0  0  1  0  0  0  0  1  0  0  0  0  0
  10. *   *     *         (C) PB4(PC7)   0  0  1  0  0  0  0  0  0  0  0  0  1  0  1  1  1  0
  11. *   E     C         (DP)PC3(PD3)   1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
  12. *   *     *   **    (D) PC4(PD2)   0  1  0  0  1  0  0  1  0  0  1  0  0  0  0  1  1  1
  13. *   ***D***  *DP*   (E) PC5(PD5)   0  1  0  1  1  1  0  1  0  1  0  0  0  0  0  0  1  0
  14. *             **
  15. */

  16. static uint8_t display_buffer[3] = {' ', ' ', ' '}; // blank by default
  17. static uint8_t N_current = 0; // current digit to display

  18. /*
  19. * Number of digit on indicator with common anode
  20. * digits 0..2: 三个数字位PC3, PC4, PA3
  21. */
  22. #define CLEAR_CATHODES() do { GPIOD->ODR |= 0x30; GPIOA->ODR |= 0x02; } while (0) // set cathodes high

  23. /************* arrays for ports *************/
  24. // PB, mask: 0x30 (dec: 48), PB4:0x10=16, PB5:0x20=32
  25. #define PB_BLANK 0x30
  26. static uint8_t PB_bits[18] = {16, 16, 32, 48, 48, 48, 48, 16, 48, 48, 48, 48, 0, 48, 32, 32, 32, 48};

  27. // PC, mask: 0xF8 (dec: 248)
  28. #define PC_BLANK 0xF8
  29. static uint8_t PC_bits[18] = {240, 0, 112, 80, 128, 208, 240, 64, 240, 208, 224, 176, 240, 48, 240, 224, 0, 160};

  30. // PD, mask: 0x02 (dec: 2)
  31. #define PD_BLANK 0x02
  32. static uint8_t PD_bits[18] = {2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 0, 2, 0, 0, 0, 0};

  33. // 新增按钮初始化
  34. void Button_init() {
  35.     // 设置PA3为输入模式
  36.     GPIOA->DDR &= ~(0b00001000); // PA3为输入
  37.     GPIOA->CR1 |= 0b00001000;    // PA3上拉输入
  38.     GPIOA->CR2 |= 0b00001000;    // PA3外部中断打开
  39.     EXTI->CR1 |= 0b00000010;     // 配置PA3为中断源,下降沿触发
  40. }

  41. /**
  42. * Initialize ports
  43. * anodes (PB4~5、PC3~7、PD1) are push-pull outputs
  44. * cathodes PD5、PD4、PA1 are ODouts in active mode, pullup inputs in buttons reading and floating inputs in inactive
  45. * PA1(DIG3), PD1|4|5(B、DIG2、DIG1), PC3|4|5|6|7(DP、D、E、A、F), PB4|5(C、G)
  46. */
  47. void LED_init() {
  48.     GPIOB->DDR |= 0b00110000; // set output for PB4-5 anodes
  49.     GPIOC->DDR |= 0b11111000; // set output for PC3~7 anodes
  50.     GPIOD->DDR |= 0b00110010; // set output for PD1 anodes
  51.     GPIOA->DDR |= 0b00000110; // set output for PA1 as DIG3 cathode

  52.     // 共阴修改
  53.     GPIOA->CR1 |= ~(0b00000010);
  54.     GPIOC->CR1 |= ~(0b00000000);
  55.     GPIOB->CR1 |= ~(0b00000000);
  56.     GPIOD->CR1 |= ~(0b00110000); // All anodes should be push-pull

  57.     // set anodes high
  58.     GPIOB->ODR |= PB_BLANK;
  59.     GPIOC->ODR |= PC_BLANK;
  60.     GPIOD->ODR |= PD_BLANK;

  61.     // set cathodes high
  62.     CLEAR_CATHODES();
  63. }

  64. /*
  65. ********************* GPIO ********************
  66. * Px_ODR - Output data register bits
  67. * Px_IDR - Pin input values
  68. * Px_DDR - Data direction bits (1 - output)
  69. * Px_CR1 - DDR=0: 0 - floating, 1 - pull-up input; DDR=1: 0 - pseudo-open-drain, 1 - push-pull output
  70. * Px_CR2 - DDR=0: 0/1 - EXTI disabled/enabled; DDR=1: 0/1 - 2/10MHz
  71. */

  72. /**
  73. * Show next digit - function calls from main() by some system time value amount
  74. */
  75. void show_next_digit() {
  76.     uint8_t L = display_buffer[N_current] & 0x7f;

  77.     // first turn all off
  78.     CLEAR_CATHODES();

  79.     // set all cathodes high
  80.     GPIOB->ODR &= ~PB_BLANK;
  81.     GPIOC->ODR &= ~PC_BLANK;
  82.     GPIOD->ODR &= ~PD_BLANK;

  83.     if (L < 18) { // letter
  84.         // set required anodes to high to light up the letter
  85.         GPIOB->ODR |= PB_bits[L];
  86.         GPIOC->ODR |= PC_bits[L];
  87.         GPIOD->ODR |= PD_bits[L];
  88.     }

  89.     // display decimal point if required
  90.     if (display_buffer[N_current] & 0x80) {
  91.         GPIOC->ODR |= GPIO_PIN3; // 点亮小数点
  92.     } else {
  93.         GPIOC->ODR &= ~GPIO_PIN3; // 不要点亮小数点
  94.     }

  95.     // set anode up for current digit to light it up
  96.     switch (N_current) {
  97.         case 0:
  98.             GPIOD->ODR &= ~(0x20); // DIG1
  99.             break;
  100.         case 1:
  101.             GPIOD->ODR &= ~(0x10); // DIG2
  102.             break;
  103.         case 2:
  104.             GPIOA->ODR &= ~(0x02); // DIG3
  105.             break;
  106.     }

  107.     if (++N_current > 2) N_current = 0;
  108. }

  109. /**
  110. * fills buffer to display
  111. * @param str - string to display
  112. */
  113. void set_display_buf(char *str) {
  114.     uint8_t B[3];
  115.     signed char ch, M = 0, i;
  116.     N_current = 0; // refresh current digit number

  117.     // empty buffer
  118.     for (i = 0; i < 3; i++)
  119.         display_buffer[i] = ' '; // 三个数位缓存都显示“0”

  120.     if (!str) return;
  121.     i = 0;
  122.     for ( ; (ch = *str) && (i < 3); str++) {
  123.         M = 0;
  124.         if (ch > '/' && ch < ':') { // digit
  125.             M = '0';
  126.         } else if (ch > '`' && ch < 'g') { // a..f
  127.             M = 'a' - 10;
  128.         } else if (ch > '@' && ch < 'G') { // A..F
  129.             M = 'A' - 10;
  130.         } else if (ch == '-') { // minus
  131.             M = '-' - 16;
  132.         } else if (ch == 'h' || ch == 'H') { // hex
  133.             M = ch - 17;
  134.         } else if (ch == '.') { // DP
  135.             if (i == 0) { // word starts from '.'
  136.                 B[0] = 0xff;
  137.             } else { // set point for previous character
  138.                 B[i - 1] |= 0x80;
  139.             }
  140.             continue;
  141.         } else if (ch != ' ') { // bad character - continue
  142.             continue;
  143.         }
  144.         B[i] = ch - M;
  145.         i++;
  146.     }

  147.     // now make align to right
  148.     ch = 2;
  149.     for (M = i - 1; M > -1; M--, ch--) {
  150.         display_buffer[ch] = B[M];
  151.     }
  152. }

  153. /**
  154. * convert integer value I into string and display it
  155. * @param I - value to display, The second parameter "Voltmeter" determines whether to allow or not allow automatic decimal points.
  156. */
  157. void display_int(uint16_t I, char voltmeter) {
  158.     int rem;
  159.     uint8_t pos = 0; // DP position
  160.     char N = 2, sign = 0, i;

  161.     // -99 <= I <= 4000
  162.     if (I < -99 || I > 4000) {
  163.         set_display_buf("--E");
  164.         return;
  165.     }

  166.     // prepare buffer for voltmeter's values
  167.     if (voltmeter) {
  168.         for (i = 0; i < 3; i++)
  169.             display_buffer[i] = 0;
  170.         if (I > 999) {
  171.             if (I % 10 >= 5) {
  172.                 I = I / 10 + 1;
  173.             } else {
  174.                 I /= 10;
  175.             }
  176.             pos = 1; // DP is in 2nd position
  177.         }
  178.     } else {
  179.         for (i = 0; i < 3; i++)
  180.             display_buffer[i] = ' ';
  181.     }

  182.     if (I == 0) { // just show zero
  183.         display_buffer[2] = 0;
  184.         return;
  185.     }
  186.     if (I < 0) {
  187.         sign = 1;
  188.         I *= -1;
  189.     }

  190.     do {
  191.         rem = I % 10;
  192.         display_buffer[N] = rem;
  193.         I /= 10;
  194.     } while (--N > -1 && I);

  195.     if (sign && N > -1) display_buffer[N] = 16; // minus sign
  196.     if (voltmeter) display_buffer[pos] |= 0x80; // 点亮pos位小数点
  197.     N_current = 0;
  198. }

  199. /**
  200. * displays digital point at position i
  201. * @param i - position to display DP
  202. */
  203. void display_DP_at_pos(uint8_t i) {
  204.     if (i > 2) return;
  205.     display_buffer[i] |= 0x80;
  206. }
複製代碼


led.h

  1. #pragma once
  2. #ifndef __LED_H__//#ifndef是"if not defined"的简写
  3. /*在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用#ifndef宏定义,一个c文件多次包含同一个h文件也不会报错。使用#ifndef可以避免下面这种错误:如果在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef,则不会出现这种错。*/
  4. #define __LED_H__

  5. #include "stm8s.h"

  6. void LED_init();
  7. void Button_init();//$$
  8. void set_display_buf(char *str);
  9. void show_next_digit();
  10. void display_int(uint16_t i, char voltmeter);
  11. void display_DP_at_pos(uint8_t i);

  12. #endif // __LED_H__
複製代碼


stm8_interrupt_vector.c

  1. /*        BASIC INTERRUPT VECTOR TABLE FOR STM8 devices
  2. *        Copyright (c) 2007 STMicroelectronics
  3. */
  4. #include "stm8s.h"

  5. INTERRUPT void TIM1_UPD_OVF_TRG_BRK_IRQHandler(void); /* TIM1 UPD/OVF/TRG/BRK ,“INTERRUPT”被声名为“@far @interrupt”*/
  6. //增加按钮中断服务程序$$
  7. INTERRUPT void EXTI_PORTA_IRQHandler(void);
  8. /*{
  9.     //if (EXTI->SR & 0b00000001) { // 检查PA3的中断标志,STM32才有“SR”寄存器
  10.                 if((GPIOA->IDR & 0x08)==0)
  11.                 {
  12.                         unsigned int i, j;
  13.                         for(i = 0; i < 50; i ++)
  14.                         {
  15.                         for(j=0; j < 40; j ++)
  16.                                 {
  17.                                 }
  18.                         }
  19.                         
  20.                         
  21.                         //simpleDelay(50);延时50ms,消抖(在main.c中的函数不能用)
  22.                         if((GPIOA->IDR & 0x08)==0)//再次判断是否按下
  23.                         {
  24.                                 while(!(GPIOA->IDR & 0x08))//松手检测
  25.                                 {
  26.         // 切换ADC通道
  27.         static uint8_t current_channel = 0; // 0: AIN3, 1: AIN4
  28.         current_channel = !current_channel;

  29.         if (current_channel){
  30.             // 切换到AIN4
  31.             ADC1->CSR = (ADC1->CSR & 0xF0) | 0x04; // 选择AIN4(输出电压)
  32.                                                 GPIOD->ODR |= (uint8_t)(0b00000100);PA2作为输入输出电压指示(高电平/输出/绿色)
  33.         } else {
  34.             // 切换到AIN3
  35.             ADC1->CSR = (ADC1->CSR & 0xF0) | 0x03; // 选择AIN3(输入电压)
  36.                                                 GPIOD->ODR &= (uint8_t)(0b11111011);PA2作为输入输出电压指示(低电平/输入/红色)
  37.         }
  38.                                                 // 启动ADC转换
  39.         //ADC1->CR1 |= 0x01; // 设置ADC启动位
  40.         //EXTI->SR = 0b00000001; // 清除中断标志
  41.                                 }
  42.                         }
  43.                 }
  44. }
  45. */
  46. typedef void @far (*interrupt_handler_t)(void);

  47. struct interrupt_vector {
  48.         unsigned char interrupt_instruction;
  49.         interrupt_handler_t interrupt_handler;
  50. };

  51. @far @interrupt void NonHandledInterrupt (void)
  52. {
  53.         /* in order to detect unexpected events during development,
  54.            it is recommended to set a breakpoint on the following instruction
  55.         */
  56.         return;
  57. }

  58. extern void _stext();     /* startup routine */

  59. struct interrupt_vector const _vectab[] = {
  60.         {0x82, (interrupt_handler_t)_stext}, /* reset */
  61.         {0x82, NonHandledInterrupt}, /* trap  */
  62.         {0x82, NonHandledInterrupt}, /* irq0  */
  63.         {0x82, NonHandledInterrupt}, /* irq1  */
  64.         {0x82, NonHandledInterrupt}, /* irq2  */
  65.         {0x82, (interrupt_handler_t)EXTI_PORTA_IRQHandler}, /* irq3  */
  66.         {0x82, NonHandledInterrupt}, /* irq4  */
  67.         {0x82, NonHandledInterrupt}, /* irq5  */
  68.         {0x82, NonHandledInterrupt}, /* irq6  */
  69.         {0x82, NonHandledInterrupt}, /* irq7  */
  70.         {0x82, NonHandledInterrupt}, /* irq8  */
  71.         {0x82, NonHandledInterrupt}, /* irq9  */
  72.         {0x82, NonHandledInterrupt}, /* irq10 */
  73.         {0x82, (interrupt_handler_t)TIM1_UPD_OVF_TRG_BRK_IRQHandler}, /* irq11 */
  74.         {0x82, NonHandledInterrupt}, /* irq12 */
  75.         {0x82, NonHandledInterrupt}, /* irq13 */
  76.         {0x82, NonHandledInterrupt}, /* irq14 */
  77.         {0x82, NonHandledInterrupt}, /* irq15 */
  78.         {0x82, NonHandledInterrupt}, /* irq16 */
  79.         {0x82, NonHandledInterrupt}, /* irq17 */
  80.         {0x82, NonHandledInterrupt}, /* irq18 */
  81.         {0x82, NonHandledInterrupt}, /* irq19 */
  82.         {0x82, NonHandledInterrupt}, /* irq20 */
  83.         {0x82, NonHandledInterrupt}, /* irq21 */
  84.         {0x82, NonHandledInterrupt}, /* irq22 */
  85.         {0x82, NonHandledInterrupt}, /* irq23 */
  86.         {0x82, NonHandledInterrupt}, /* irq24 */
  87.         {0x82, NonHandledInterrupt}, /* irq25 */
  88.         {0x82, NonHandledInterrupt}, /* irq26 */
  89.         {0x82, NonHandledInterrupt}, /* irq27 */
  90.         {0x82, NonHandledInterrupt}, /* irq28 */
  91.         {0x82, NonHandledInterrupt}, /* irq29 */
  92. };


複製代碼



这DC-DC模块之所以损坏,俺也下了点功夫查找了原因。
从上边的原理图中大家可以看到,数字电压表的供电由XL6009E输出的38V直流再经过Buck芯片XL1509-3.3降压后产生,而检查中发现,当生成的3.3V稍微带点负载就会让相关的这两个DC-DC芯片鬼上身,3.3V直接变≈10V的高电压,让其后边只能承受3.3V低电压的单片机重创,继而损坏XL1509-3.3。可能DC-DC电路之间存在着一些相互干扰?也可能是这个XL6009E电路输出不怎么理想的缘故——从后来的示波器查看,其38V输出在带载的情况下,显得很糟糕的样子,大家可以看下边的图。这需要接下来要去改善的地方。

而当俺把XL1509-3.3供电改成整个模块的电源输入端时,它工作得很好,即使带更大的负载也输出稳定的3.3V。这样也不错,因为在俺的使用境地,模块的输入电压不会很高,至多5.5V~12V的样子。相比38V的供电,万一XL1509-3.3损坏,高压直通低压,负载一击毙命,毫无回旋余地——这应该也是所有Buck电路的致命缺陷。

下边是示波器测到的各个输出情况(图中“1A负载”是指整个DC-DC模块输出带载)。


20250105-0001(XL6009输出_空载)

20250105-0001(XL6009输出_空载).png

20250105-0001(XL6009输出_1A负载)

20250105-0001(XL6009输出_1A负载).png

20250105-0001(LM2596S输出_空载)

20250105-0001(LM2596S输出_空载).png

20250105-0001(LM2596S输出_1A负载)

20250105-0001(LM2596S输出_1A负载).png


从图中可以看到,各个波形中存在很多尖峰脉冲,这是不是电压表数字跳动的元凶有待研究。
另外,我也想问问坛子里各位玩MCU的大咖,STM8S003F3P6的ADC线性是不是有问题?因为俺发现在测量个位数电压及两位数电压与实际电表测得的值相比,偏差不同。是俺程序中某些地方写得不恰当还是MCU体质就差?如何改善它呢?小弟非常期待您的答疑解惑,感恩!

谢谢各位大大的点赞,谢谢老大给予的修改机会。



評分

4

查看全部評分

發表於 4 天前 | 顯示全部樓層
這一篇好像是在電子電路版比較符合,若有其它意見請再提出。
 樓主| 發表於 3 天前 來自手機 | 顯示全部樓層
本帖最後由 fix2010 於 2025-1-7 07:13 AM 編輯

没问题,谢谢老大!
發表於 3 天前 | 顯示全部樓層
本帖最後由 阿明先生 於 2025-1-7 09:57 AM 編輯

請問版主大大,你這個是數字的字形變數嗎?或是其他用途, 為什麼要宣告成靜態變數?這樣不是會占用SRAM記憶體,不是Flash記憶體 ,使用ST Visual Develop (STVD) 編譯器嗎? static uint8_t PB_bits ,  static uint8_t PC_bits[18] static, uint8_t PD_bits[18]
 樓主| 發表於 3 天前 | 顯示全部樓層
阿明先生 發表於 2025-1-7 09:44 AM
請問版主大大,你這個是數字的字形變數嗎?或是其他用途, 為什麼要宣告成靜態變數?這樣不是會占用SRAM記 ...

阿明大,这三个静态数组变量(变数)的确是三个数码的字模存放处,字模表见LED.c的开头部分(打星号的注释行)。
至于静态变量、全局变量等的内存利用及存放规则,俺只约莫了解了下。因为修改的是人家的程序,所以也没改动它们,再则,您看本程序也并未太复杂,总体变量也不多,放哪里也不至于内存不够用。
当然,遵循分配规则,养成习惯,对精进之路非常重要。
感谢您的提问,彼此勉励。
發表於 3 天前 | 顯示全部樓層
本帖最後由 阿明先生 於 2025-1-7 07:39 PM 編輯

感覺這一程式的架構,也不是一般正常的寫法,用Timer中斷服務程序產生一計數值Global_time,然後主程式的迴圈,用Global_time計數值來控制每次 ADC的讀取,及LED的數字掃瞄函數,為什麼不直接在Timer中斷服務函數(把Timer時間設定好),直接執行LED的數字掃瞄函數(掃瞄時間更精準),用ADC中斷服務函數讀取ADC值(ADC讀取效率更高),這樣子主程式會很閒,可以做其他工作(當然你這個程式不需要),主程式也可以來讀取鍵盤,可以加入防鍵盤按鈕彈跳,鍵盤讀取延遲再次讀取  
 樓主| 發表於 前天 06:29 AM | 顯示全部樓層
本帖最後由 fix2010 於 2025-1-8 06:33 AM 編輯
阿明先生 發表於 2025-1-7 07:31 PM
感覺這一程式的架構,也不是一般正常的寫法,用Timer中斷服務程序產生一計數值Global_time,然後主程式的迴 ...


嗯,阿明大,这个思路俺是保持了原作者的。
他的意图估计是保持每次ADC及LED显示的间隔一样(即使中间插入了其它任务),并保证不要太忙。频率在main.c首部的相关变量设定。
因为程序运行正常,俺水平有限也没过多研究优化。
是的,判断外部中断那子程序也可以不要,直接在主程序中插入按键监控、消抖,然后改变输入输出电压通道、状态LED灯也是可以的,俺之前试过。之所以用中断方式,是俺为了学习如何写外部中断的方法——软件控制的好处是,能随时将自己的想法重新注入,待日后好好琢磨清楚再修改。
谢谢您的指正。
 樓主| 發表於 前天 07:33 AM 來自手機 | 顯示全部樓層
本帖最後由 fix2010 於 2025-1-8 07:35 AM 編輯

再说说这个模块的实际使用。


正如自媒体评测的那样,那些廉价的,基于XL6009、LM2596S的DC-DC模块,纹波、输出功率及发热都不理想。这次测试了12V/1A输入,输出8V/1A,效率只有67%的样子,XL6009发热烫手。看起来,模块整体10W的样子还能支撑下,再上去要加散热器的。
如下图,在两个转换芯片输出端加1000uF/50V电解电容,模块输出纹波显著下降,大约200mV,只剩个零头60~70mV。添加CBB 1uF到两个芯片输出端,那些尖峰只是略有下降,抑制效果一般。纹波与尖峰,俺更在意后者,因为它更容易让敏感负载出问题。大家有没好的方法呢?

20250108_071533.jpg

看起来,原电路使用的贴片铝电解电容ESR及容量根本满足不了要求的,找机会将它们换成容量更高或者钽电容来试试。


發表於 前天 09:19 AM | 顯示全部樓層
本帖最後由 阿明先生 於 2025-1-8 10:58 AM 編輯

不是很瞭解版主的想法,既然要重新製作STM8單晶片,STM8內建週邊的硬體也有PWM功能,可以產生PWM做升降壓,又可以同時顯示數字電壓,多個ADC可以做調壓及輸出電壓的回授比較,線路全部整合在單晶片,升降壓效率要高,要找功率用高Q值鐵粉芯,鐵粉芯有兩類,一種是訊號濾波及諧振用,訊號功率小有高的導磁係數,Q值很高,但是大功率很會磁飽和(Q值大降) ,要找大功率時Q值高不會磁飽和的磁心 ,還有MOS晶體的on/off切換時要快,推動MOS切換時,MOS晶體半導通時間要短,太長MOS會發熱 (MOS閘極有輸入電容,會對高阻抗的推動訊號積分,導致MOS半導通時間長,推動MOS的脈波訊號要降低阻抗),也可以使用STM32,ADC精度更高,

 樓主| 發表於 前天 02:36 PM | 顯示全部樓層
本帖最後由 fix2010 於 2025-1-8 02:38 PM 編輯
阿明先生 發表於 2025-1-8 09:19 AM
不是很瞭解版主的想法,既然要重新製作STM8單晶片,STM8內建週邊的硬體也有PWM功能,可以產生PWM做升降壓, ...


阿明先生您所说的是要重建电路了。
俺这只是修复损坏的电压显示部分,至于DC-DC部分并未损坏。
像这类XL6009、LM2569S组合的Boost-Bucker在淘宝之前卖得很多,俺也不知道为何设计者不用MCU直接控制MOSFET的方法建立电路。可能独立功能芯片价格便宜、用料少、线路布置简单的原因吧?
您说的没错,更换好点的电感线圈可以抗饱和;但开关单元是芯片集成的,没法去改变。
在原有基础上,硬件优化只能从滤波层面出发。如果增加诸如元器件,那肯定得重建PCB,工程有点大,不说费钱,俺目前手艺不精,出来的板子也不一定满意。
当然,有机会俺会尝试自建您所说的类似电路看看。
再次感谢您提供的思路。
您需要登錄後才可以回帖 登錄 | 立即註冊

本版積分規則

關閉

站長小叮嚀上一條 /1 下一條

禁閉室|手機版|連繫我們|痞酷網電子技術論壇

GMT+8, 2025-1-10 11:51 PM , Processed in 0.083439 second(s), 21 queries , Gzip On.

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.