Generating square wave in AVR Assembly without PWM

北慕城南 提交于 2021-02-08 08:06:11

问题


I'm using an ATmega328. The thing is that I want to generate a square wave of a given frequency and of a given amplitude. PWM can't be used because I was given a board that has already been soldered, so the wave has to be put at the output of an R2R resistor ladder that is connected to the B port of the processor. So, basically, the idea is that I have to put the pins of port B in 0 and VOLUME (VOLUME is a number that goes from 1 to 255) periodically, with a given frequency and a duty cycle of 50%. And remember: NO PWM. The frequency should be able to change every 100 ms, but I'm not being able to make this work so I'm just trying to generate a constant frequency and see what happens at first.

I'm running the clock at 1MHz. I wrote the following code:

.DSEG
.ORG 0x100

.CSEG
.ORG 0x100

;Initializing stack pointer
LDI R16,HIGH(RAMEND)
OUT SPH,R16
LDI R16,LOW(RAMEND)
OUT SPL,R16

MAIN:
CALL GENERATE ;Calling the generating routine
RJMP MAIN ;Repeat this forever

;I will generate a 440Hz frequency. It has an approximate period of 2273 microseconds
;This means that half a period stands for approximately 1136 clocks
GENERATE:
LDI R17, 0x70
LDI R18, 0x04   ;Half the period in hexadecimal is 0x0470
LDI R19, 243    ;Volume = 243 (arbitrary, it could be any number)
LDI R21, 88 ;The amount of half-periods in 100 ms (arbitrary election, too)

LDI R25, 0xFF
OUT DDRB, R25       ;Port B is an output port

LDI R24, 0xFF       ;R25R24 = 0xFFFF
CLC         ;Clean the carry
SBC R24, R17
SBC R25, R18        ;R25R24 = 0xFFFF - Halfperiod
ADIW R25:R24, 1     ;R25R24 = 0xFFFF - Halfperiod +1
OUT PORTB, R18      ;The wave starts at 0

BEGIN:
CALL LOOP_1
EOR R19, R19        ;It varies between 0 and volume
OUT PORTB, R19      ;It puts the output to the actual value of R19 (0 or volume)
CLZ         ;Clean Z flag
DEC R21
BREQ END        ;When 100ms have passed, generation is over
JMP BEGIN       ;If not, generation continues

LOOP_1: STS TCNT1H, R25
STS TCNT1L, R24     ;Loading the amount of clocks the timer has to count
LDI R16, 0x00
STS TCCR1A, R16
LDI R16, 0x01
STS TCCR1B, R16     ;Timer operating in normal mode, no prescaler
LOOP_2: IN R16, TIFR1
SBRS R16, TOV1      ;If timer's over, skip the next jump
JMP LOOP_2
LDI R16, 0x00
STS TCCR1B, R16     ;Stopping the timer
LDI R16, 0x04
OUT TIFR1, R16      ;Clean TOV1
RET         ;Back to BEGIN

END:
RET         ;Back to MAIN

It's one of my first approaches to Assembly, so this may be pretty ugly to read. The code is currently not working. Any ideas?

EDIT:

Thanks to Spektre who pointed this out to me, I corrected a piece of the code above. The code is the same except that

GENERATE:
.
.
.
LDI R21, 44 ;The amount of PERIODS (not half-periods as before) in 100 ms
.
.
.
BEGIN:
OUT PORTB, R18      ;This was before the BEGIN tag, now it is after it
CALL LOOP_1     ;It counts a halfperiod with output=0
OUT PORTB, R19      ;Now output=volume
CALL LOOP_1     ;It counts a halfperiod with output=volume
CLZ         ;Clean Z Flag
DEC R21
BREQ END        ;When 100ms have passed, generation is over
JMP BEGIN       ;If not, generation continues

回答1:


It is been years I did something for such architecture ... I am using UC3 (successor) and there on MCU 66 MHz clock I can achieve 2-5 MHz polling frequency. But I am afraid on your Target platform this would be a lot lower. So first measure polling frequency to see the limits of your HW.

1 MHz is the output clock or the MCU clock? What max frequency you need to have at output?

There are more ways to achieve this the obvious are:

  1. Timer/Counter

    You can set up Timer for example 1 us and do the timing and switching there. Or you can use this just to set the new frequency. But beware any interrupt can cause timing problems in the output signal like jitter ... These can be avoided if there is enough time but it usually limits max output frequency a lot.

  2. Main thread polling

    It looks like this is what you are doing. But you are forgetting all the instructions has their own timing. And conditionals can have different timing so you should compensate.

  3. exploiting interface (if any present)

    Look at the datasheet and see if any HW is connected to that port. MCUs has usually multiplexed more features on the pins sometimes allowing to exploit them. For example I once made an VGA image generator by exploiting serial interface, DMA + ABI/SD card interface and. Each method produced 16 colored VGA signal correctly but with completely different code and HW architecture on the same chip and pins... To compare with your platform on ATMega168 with 20 MHz clock (it is the same core as ATMega328) I was able to produce 1bit LCD 640x480 signal for LCD (many times lower then what is needed for colored VGA signal). Sometimes you can even change the meaning of pins during runtime... For example to achieve 100 000 rpm BLDC controller I change the internal circuitry few times and electric period of the motor just to be able to produce such high speeds (otherwise non-achievable by direct means.

You need to experiment which approach is fast/accurate/comfortable enough for you. From the first look at you code I stumble on this:

EOR R19, R19        ;It varies between 0 and volume
OUT PORTB, R19      ;It puts the output to the actual value of R19 (0 or volume)

If I see it right you are XORing R19 and R19 ... destroing the content of R19 after first pass. So you are outputting 0 to the B port all the time:

XOR Volume,Volume -> Volume = 0
XOR 0,0  -> 0

Instead I would:

  1. out port volume
  2. wait half period
  3. out port zero
  4. wait half period
  5. if 100ms reached change frequency
  6. goto #1

You should use the Port access as least often as you can. Not sure if it is also the case for ATMega but on newer architectures the Port is accessed by HW interface/API and is really slow for high speed switching if not used wisely.

PS.

Hope your R2R ladder has diodes before the input resistors or at least PORTB has open collector output otherwise it will not work because of the currents between neighbor pins if both set to different value.

[Edit1] Some clarification

  1. To be more clear about the R2R current problem

    The diodes should have the same PN barrier voltage !!!

  2. The 1MHz clock

    You wrote I'm running the clock at 1MHz but did not clarify if it is MCU/CPU clock, Timer Clock or output square wave signal frequency. Now it is clear your output signal is <130,523> Hz which should be easily achievable on your platform. I was not sure before ...I have generated signals even around 30 MHz on MCU's so 1 MHz was a possibility.

  3. Timing

    Conditional instructions like BREQ END on most platforms have different timing when the condition is true and when false so to maintain the synchronism you should account for that ... sometimes well placed nop can handle the problem ... Yu shoudl check the instruction timing in the documentation if it is the case or not.

    You stated that you are using counter/timer I do not see it in your code. For proper use you should

    1. configure the HW for mode and frequency you want to
    2. register the interrupt subroutine
    3. enable the interrupt and or trigger timer counter event...
    4. inside interrupt event handler

      reset the counter value if needed and clear the interrupt flag so another event can occur. Sometimes even correction of the counter value is needed for proper timings if you are near edge of chip capabilities.

    What I see is some configuration of timer/counter not sure if rightly done as I do not use that platform a long time. I see no interrupt handler subroutine anywhere... possibly you are polling the counter register but again not sure if it is the right way (not all HW registers are read able) you should confront the documentation. But anyway it is polling so timing of instruction matters ...

If it helps I dig one of my ancient generators (generate square wave on PortC) asm codes for ATMega168:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; generator ver: 0.00 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
CPU:    ATMega168 8MHz internal RC

OUT:    PC
*/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ATMega168 startup: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; fuses:BOOTRST=1 disabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        .equ stack  =0x04FF
        .equ cpuclk =8000000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        .device ATmega168   ; device selection
        .equ PINB  =0x03    ; IO reg adresses definition in/out
        .equ DDRB  =0x04
        .equ PORTB =0x05
        .equ PINC  =0x06
        .equ DDRC  =0x07
        .equ PORTC =0x08
        .equ PIND  =0x09
        .equ DDRD  =0x0A
        .equ PORTD =0x0B
        .equ TIFR0 =0x15
        .equ TIFR1 =0x16
        .equ TIFR2 =0x17
        .equ PCIFR =0x1B
        .equ EIFR  =0x1C
        .equ EIMSK =0x1D
        .equ GPIOR0=0x1E
        .equ EECR  =0x1F
        .equ EEDR  =0x20
        .equ EEARL =0x21
        .equ EEARH =0x22
        .equ GTCCR =0x23
        .equ TCCROA=0x24
        .equ TCCROB=0x25
        .equ TCNT0 =0x26
        .equ OCROA =0x27
        .equ OCROB =0x28
        .equ GPIOR1=0x2A
        .equ GPIOR2=0x2B
        .equ SPCR  =0x2C
        .equ SPSR  =0x2D
        .equ SPDR  =0x2E
        .equ ACSR  =0x30
        .equ SMCR  =0x33
        .equ MCUSR =0x34
        .equ MCUCR =0x35    
        .equ SPMCSR=0x37
        .equ SPL   =0x3D
        .equ SPH   =0x3E
        .equ SREG  =0x3F

        .equ WDTCSR=0x60    ; sts,lds
        .equ CLKPR =0x61
        .equ PRR   =0x64
        .equ OSCCAL=0x66
        .equ PCICR =0x68
        .equ EICRA =0x69
        .equ PCMSK0=0x6B
        .equ PCMSK1=0x6C
        .equ PCMSK2=0x6D
        .equ TIMSK0=0x6E
        .equ TIMSK1=0x6F
        .equ TIMSK2=0x70
        .equ ADCL  =0x78
        .equ ADCH  =0x79
        .equ ADCSRA=0x7A
        .equ ADCSRB=0x7B
        .equ ADMUX =0x7C
        .equ DIDR0 =0x7E
        .equ DIDR1 =0x7F
        .equ TCCR1A=0x80
        .equ TCCR1B=0x81
        .equ TCCR1C=0x82
        .equ TCNT1L=0x84
        .equ TCNT1H=0x85
        .equ ICR1L =0x86
        .equ ICR1H =0x87
        .equ OCR1AL=0x88
        .equ OCR1AH=0x89
        .equ OCR1BL=0x8A
        .equ OCR1BH=0x8B
        .equ TCCR2A=0xB0
        .equ TCCR2B=0xB1
        .equ TCNT2 =0xB2
        .equ OCR2A =0xB3
        .equ ASSR  =0xB6
        .equ TWBR  =0xB8
        .equ TWSR  =0xB9
        .equ TWAR  =0xBA
        .equ TWDR  =0xBB
        .equ TWCR  =0xBC
        .equ TWAMR =0xBD
        .equ UCSR0A=0xC0
        .equ UCSR0B=0xC1
        .equ UCSR0C=0xC2
        .equ UBRR0L=0xC4
        .equ UBRR0H=0xC5
        .equ UDR0  =0xC6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
        .cseg           ; Start of code segment
        .org 0x0000     ; interrupts, highest priority first
        jmp RESET       ; 3 Reset Handler
        jmp EXT_INT0    ; 3 IRQ0 Handler
        jmp EXT_INT1    ; 3 IRQ1 Handler
        jmp PCINT0      ; 3 PCINT0 Handler
        jmp PCINT1      ; 3 PCINT1 Handler
        jmp PCINT2      ; 3 PCINT2 Handler
        jmp WDT         ; 3 Watchdog Timer Handler
        jmp TIM2_COMPA  ; 3 Timer2 Compare A Handler
        jmp TIM2_COMPB  ; 3 Timer2 Compare B Handler
        jmp TIM2_OVF    ; 3 Timer2 Overflow Handler
        jmp TIM1_CAPT   ; 3 Timer1 Capture Handler
        jmp TIM1_COMPA  ; 3 Timer1 Compare A Handler
        jmp TIM1_COMPB  ; 3 Timer1 Compare B Handler
        jmp TIM1_OVF    ; 3 Timer1 Overflow Handler
        jmp TIM0_COMPA  ; 3 Timer0 Compare A Handler
        jmp TIM0_COMPB  ; 3 Timer0 Compare B Handler
        jmp TIM0_OVF    ; 3 Timer0 Overflow Handler
        jmp SPI_STC     ; 3 SPI Transfer Complete Handler
        jmp USART_RXC   ; 3 USART, RX Complete Handler
        jmp USART_UDRE  ; 3 USART, UDR Empty Handler
        jmp USART_TXC   ; 3 USART, TX Complete Handler
        jmp ADC_DONE    ; 3 ADC Conversion Complete Handler
        jmp EE_RDY      ; 3 EEPROM Ready Handler
        jmp ANA_COMP    ; 3 Analog Comparator Handler
        jmp TWI         ; 3 2-wire Serial Interface Handler
        jmp SPM_RDY     ; 3 Store Program Memory Ready Handler
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
RESET:  cli             ; Reset Handler
        ldi  r16,high(stack)
        out  SPH,r16
        ldi  r16,low(stack)
        out  SPL,r16

        ldi  r16, 0xFF  ; DDRB 1 - output, 0 - input direction
        out  DDRB, r16
        ldi  r16, 0xFF  ; DDRC 1 - output, 0 - input direction
        out  DDRC, r16
        ldi  r16, 0xFF  ; DDRD 1 - output, 0 - input direction
        out  DDRD, r16

        ldi  r16, 0xFF  ; all outputs high,and all inputs Pull Up to Ucc
        out  PORTB,r16
        out  PORTC,r16
        out  PORTD,r16
        jmp  main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
EXT_INT0:               ; IRQ0 Handler
        reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
EXT_INT1:               ; IRQ1 Handler
        reti
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
PCINT0:                 ; PCINT0 Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
PCINT1:                 ; PCINT1 Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
PCINT2:                 ; PCINT2 Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
WDT:                    ; Watchdog Timer Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM2_COMPA:             ; Timer2 Compare A Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM2_COMPB:             ; Timer2 Compare B Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM2_OVF:               ; Timer2 Overflow Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM1_CAPT:              ; Timer1 Capture Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM1_COMPA:             ; Timer1 Compare A Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM1_COMPB:             ; Timer1 Compare B Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM1_OVF:               ; Timer1 Overflow Handler 
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM0_COMPA:             ; Timer0 Compare A Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM0_COMPB:             ; Timer0 Compare B Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TIM0_OVF:               ; Timer0 Overflow Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
SPI_STC:                ; SPI Transfer Complete Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
USART_RXC:              ; USART, RX Complete Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
USART_UDRE:             ; USART, UDR Empty Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
USART_TXC:              ; USART, TX Complete Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
ADC_DONE:               ; ADC Conversion Complete Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
EE_RDY:                 ; EEPROM Ready Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
ANA_COMP:               ; Analog Comparator Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
TWI:                    ; 2-wire Serial Interface Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
SPM_RDY:                ; Store Program Memory Ready Handler
        reti            ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
main:   ldi  r16,0

main0:  out  PORTC,r16  ; 1 8T / 8MHz = 1MHz PC0, 0.5MHz PC1, ...
        inc  r16        ; 1
        nop             ; 1
        nop             ; 1
        nop             ; 1
        nop             ; 1
        rjmp main0      ; 2
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
waitms: push r31        ; 2 wait cca r31 [ms] +(15+r31*4)T
        push r30        ; 2
        push r29        ; 2
waitms2:ldi  r30,cpuclk/200000;1 1ms
waitms1:ldi  r29,49     ; 1     200T
waitms0:nop             ; 1
        dec  r29        ; 1
        brne waitms0    ; 2/1
        dec  r30        ; 1
        brne waitms1    ; 2/1
        dec  r31        ; 1
        brne waitms2    ; 2/1
        pop  r29        ; 2
        pop  r30        ; 2
        pop  r31        ; 2
        ret             ; 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ID: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; end. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

It is my standard template I used at that time for these devices ... Take a look at the waitms subroutine it waits as close to r31 [ms] as it can. The timings of instructions are present and yes the conditional instructions have different timing on that chip. As ATMega328 has the same core then it is the same for your chip as well.

Heh my ATMega emulator with Oscilloscope in C++ still works on Win7 ... (Written on w9x or w2k not sure now) (used with hex compiled from the posted source of mine).



来源:https://stackoverflow.com/questions/38005093/generating-square-wave-in-avr-assembly-without-pwm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!