Category Archives: Assembly

Digital Sawtooth Signal Generation with MSP430

Hello,

Sawtooth signals have various applications for example FMCW radars, audio applications, CRT screens, etc. By using an operational amplifier, a voltage comparator, and a transistor switch you can create your own sawtooth signal. The idea behind this analog circuit is to charging a capacitor linearly and rapidly discharging it. However, it is pretty difficult to get a rapid falling edge from an analog circuit, so the circuit with those component creates an asymmetrical  triangular wave.

Analog Sawtooth Signal at 10 kHz

Analog Sawtooth Signal at 10 kHz

To overcome this problem, you can create a digital sawtooth generator that falls to zero (or to the offset you set) in no time. What you need is a digital-to-analog converter (DAC) and a controlling unit, in this case an MSP430G2553. Most DAC ICs use serial SPI or I2C interface and this reduces the speed dramatically because you are sending whole DAC count bit by bit. What if you need a sawtooth signal around a MHz, or more? For this purpose, there are parallel input high-speed DAC ICs which can give update rates up to 2.5 GSPS. In this application, I used a 10-bit 165 MSPS digital to analog converter, DAC900, to produce a 10 kHz sawtooth signal.

The signal is continuously increasing, and then roll back to zero. Therefore, it is easy to figure out what kind of parallel input to give to the DAC, a binary counter. When it reaches its maximum value, it rolls back to zero. Now, we need to calculate the maximum number of steps to fit in 100us (10kHz).

MSP430G2553 has a DCO at maximum of 16MHz, but this is the speed of the CPU clock, not the frequency of counting, since each instruction takes several cpu cycles, effective counting frequency will be reduced. One other point is that C is a high level language and its instructions take longer than Assembly instructions. Therefore, coding with Assembly will be a more effective solution.  Let us first calculate the counting frequency with full resolution, 10-bits. For this, we need to use an 11-bit counter so we can use the LSB as the DAC clock. If use the LSB as the clock, its period will determine our counting frequency.

10 bit resolution means 1024 DAC steps. If we divide 100us to 1024 we will get 97ns counting/clock period, requiring a bit more than 10MHz. This is unfortunately is not possible to get as a clock frequency because to count with two ports (since one port has 8 DIO pins, we need two of them to make a 11-bit counter), two nested loops and a comparison function is required. This will load the CPU and slow it down. I tried this method, and got maximum of 822kHz of counting frequency. Then how about counting with this frequency, and reducing the number of steps? 822kHz gives us 1.22us of count period, which means 90 steps for 100us! If we used only one port, we would have 8-bit counter and 7-bit resolution, giving us 128 steps. In that case I got approximately 1.6MHz count, which results in 80us to reach to the top value with 128 steps. This is not a big problem because we can slow down our DCO and get the required frequency. In order to use 128 steps to get 100us period, required clock frequency is 1.28MHz (Not CPU clock but the DAC clock). This value can be reached by appropriate DCO setting, which you will see in the following Assembly code[1].

 

;-------------------------------------------------------------------------------
; MSP430 Assembler Code Template for use with TI Code Composer Studio;
;-------------------------------------------------------------------------------
            .cdecls C,LIST,"msp430.h"       ; Include device header file

;-------------------------------------------------------------------------------
            .text                           ; Assemble into program memory
            .retain                         ; Override ELF conditional linking
                                            ; and retain current section
            .retainrefs                     ; Additionally retain any sections
                                            ; that have references to current
                                            ; section
;-------------------------------------------------------------------------------
RESET       mov.w   #__STACK_END,SP         ; Initialize stackpointer
StopWDT     mov.w   #WDTPW|WDTHOLD,&WDTCTL  ; Stop watchdog timer

;-------------------------------------------------------------------------------
                                            ; Main loop here
;-------------------------------------------------------------------------------
;DCO settings to ~1.28MHz
DcoSetng	clr.b 	&DCOCTL ; Select lowest DCOx
			bis.b 	#RSEL3+RSEL2+RSEL1+RSEL0,&BCSCTL1
			bis.b 	#DCO2,&DCOCTL 

SetupP1     bis.b   #255,&P1DIR
Mainloop    inc     &P1OUT
            jmp     Mainloop

;-------------------------------------------------------------------------------
;           Stack Pointer definition
;-------------------------------------------------------------------------------
            .global __STACK_END
            .sect 	.stack

;-------------------------------------------------------------------------------
;           Interrupt Vectors
;-------------------------------------------------------------------------------
            .sect   ".reset"                ; MSP430 RESET Vector
            .short  RESET

With this settings our counter is ready to use and the following figure is the capture of the clock output:

Clock output from MSP430

Clock output from MSP430

Having our counter, now we need to connect it to the DAC. I directly used the basic connection instructions given in the datasheet of the DAC900. To read a value from oscilloscope, I connected a 100 ohm resistor between the current output pin and ground.

DAC900 Connections [2]

DAC900 Connections [2]

Here is the realization of this circuit on breadboard:

MSP430 and DAC900 Connections

MSP430 and DAC900 Connections

DAC900 does not come with DIP packages, so I got one SOIC-28, and soldered it on my homemade SOIC-to-DIP converter circuit. You can buy one of those coverters. I must admit that I am quite shocked about the price because similar products cost $2 in Turkey…

In the following two figures you can see the results:

Sawtooth Signal Obtained from the DAC

Sawtooth Signal Obtained from the DAC

 

A Close-up to the Falling Edge of the Sawtooth

A Close-up to the Falling Edge of the Sawtooth

You can see the rising is noisy due to the long thin cables, but a more important problem is that there isn’t the smooth increase of the ramp as I got in the analog circuit. However, the greatest advantage of this circuit is the real rapid falling edge.

To solve the problem with discrete increase of the ramp, I used an FPGA board, and created a sawtooth with 1250 steps. It gave much better results, and I will post that later…

Please feel free to contact me about any problems, suggestions, etc…

Thank you

Gunay TURAN

 

References:

[1] This code is taken from TI’s example codes for CCS and modified.

[2] http://www.ti.com/lit/ds/symlink/dac900.pdf

 

 

Kitchen Alarm with Assembly

Hi,
On this entry, I will present a simple group project we did during my microprocessors course with Atmel ATmega 16. This is my first assembly project, even though the idea is simple (This project can be developed in one or two hours with C) application on assembly is a bit complicated due to the fact that the language is really primitive compared to C.
So why we still use assembly? First, it gives a better understanding of the mechanisms working in the processor because you are closer to the MCU somehow, and second, it has much smaller code size compared to C. So if your flash is limited, and code is complicated it makes easier to fit the code in the flash memory in the expense of harder implementation of the idea.
Project Description:
Kitchen alarm basicly is a countdown timer with which you set a time, start the coundown and get an alarm sound when the time is up. In this application we have a limit of 9:59 minutes because we only used three 7-segment displays. And as an additional feature we have an time addition button which adds one extra minute anytime the button is pressed without interrupting the countdown.
How to Use:
There are three buttons to control the Kitchen Alarm.
Increment Button: Each time pressed, this button increases the number displayed on the active 7-segment display before the timer started to work. When the countdown is started the button gets deactivated. If the number displayed reaches to nine and the button is pressed again, display rolls back to zero again.
Set Button: This button is used to save the digit calibrated by the increment button and activates the more significant digit. When the program is run, the least significant (rightmost) digit is active initially. After choosing the value via increment button, pressing this button saves that value and activates the next one. Doing it second time sets the second digit and activates the minute segment and the third press of the button sets the value and starts the timer. After the third press countdown starts immidately, and there is no turning back after this point. User should reset the program to make a change in the top value.
Add Minute Button: As explained above, this button is always active during and the before coundown and increases the minute by one. If the value is nine at the minute segment it changes nothing.
Algorithm:
The flowchart of the program, excluding the display function, is the following:
Flowchart of the Program
The display function is excluded because it is done by using brute force using lots of comparisons of the same type, which will be more clear when the code is read.
Assembly Code:
;VUT v Brne FEKT
;CMPT Course 1st Project: Kitchen Alarm
;
;Authors:
;	Burak Dalogullari
;	Gunay Turan
;	Kerem Sennik
;	Serkan Ozatik
;	Alican Bor
;
;How to use:
;	Onboard Switches:	|o|		|o|		|o|
;				SW2		SW1		SW0
;				Time addition	Set	   Increment
;
;	7-Segment Displays:	A   	: 	 B		C
;				minute		   seconds
;
;	When the program is excecuted, the user will see 0:00 on
;the display. At that time the active display is display-"C".
;user can use increment button(SW0) to determine that value.
;pressing the set button(SW1) will set C value and activate
;the display-B. With the same procedure user can set A value
;and setting this will automatically start the countdown.
;During the countdown user can use SW2 to get an additional
;minute. When display shows 0:00 again, buzzer will give an
;alarm signal until a reset or powerdown is applied.
;

.include "m16def_mod.inc"		;include file

;register definitions
.def    delay_idx = r16
.def    temp = r17
.def    idxA = r18
.def    idxB = r19
.def    idxC = r20
.def    zero = r21
.def    one = r22
.def    two = r23
.def    three = r24
.def    four = r25
.def    five = r26
.def    six = r27
.def    seven = r28
.def    eight = r29
.def    nine = r30
.def    set_idx = r31

; interrupt vectors
.cseg
.org	    0x0000
	rjmp	reset
.org	    0x0002
    rjmp    increment_sw_debouncer
.org        0x0004
    rjmp    set_sw_debouncer
.org        0x0012
    rjmp    time
.org        0x0024
    rjmp    addminute_sw_debouncer

;main program
.org	    0x002a
reset:
	;Stack definition
	ldi		temp, LOW(RAMEND)
	out     	SPL, temp
	ldi		temp, HIGH(RAMEND)
	out		SPH, temp

	;Digital port directions
	ldi		temp, 255
	out		DDRA, temp
	ldi     	temp, 251
	out		DDRB, temp
	ldi  	   	temp, 255
	out     	DDRC, temp
	ldi     	temp, 243
	out		DDRD, temp

	;external input settings
	;interrupts 0,1, and 2 at falling edge
	ldi		temp, (1<<INT0) | (1<<INT1) | (1<<INT2)
	out		GICR, temp
	ldi		temp, (1<<ISC01) | (0<<ISC00) | (1<<ISC11) | (0<<ISC10)
	out		MCUCR, temp

	;Digital IO configurations for 7-segment display for each digit
	ldi     zero, 0x7B
	ldi     one, 0x0A
	ldi     two, 0xB3
	ldi     three, 0x9B
	ldi     four, 0xCA
	ldi     five, 0xD9
	ldi     six, 0xF9
	ldi     seven, 0x0B
	ldi     eight, 0xFB
	ldi     nine, 0xDB

	;index initializations
	ldi     set_idx, 0
	ldi     idxA, 0
	ldi     idxB, 0
	ldi     idxC, 0
	ldi     delay_idx, 3

	;output initialization
	out     PORTA, zero
	out     PORTB, zero
	out     PORTC, zero

	sei					;enable all interrupts
;infinite loop
loop:
    	sei	            		;reenable interrupts
	rjmp    displayA			;display function call
	rjmp	loop

;software debouncer function for increment button
increment_sw_debouncer:
    ldi     temp,255
delay_loop_1:
    dec     temp
    brne    delay_loop_1
    rjmp    increment

;increment button interrupt service routine
increment:
    ldi     temp, 0
    CPSE    set_idx, temp
    rjmp    check1
    rjmp    setC

check1:
    ldi     temp, 1
    CPSE    set_idx, temp
    rjmp    setA
    rjmp    setB

setC:
    inc     idxC
    ldi     temp, 10
    CPSE    idxC, temp
    rjmp    loop
    ldi     idxC, 0
    rjmp    loop

setB:
    inc     idxB
    ldi     temp, 6
    CPSE    idxB, temp
    rjmp    loop
    ldi     idxB, 0
    rjmp    loop

setA:
    inc     idxA
    ldi     temp, 10
    CPSE    idxA, temp
    rjmp    loop
    ldi     idxA, 0
    rjmp    loop

;timer enable after the time is set
timer_enable:

	;normal mode with prescaler 1024 - TCLK ~= 1kHz
	ldi 	temp, (1<<CS02) | (0<<CS01) | (1<<CS00)
	out 	TCCR0, temp
	ldi 	temp, (1<<TOIE0)
	out 	TIMSK, temp
	rjmp    loop

;Software debouncer for set button
set_sw_debouncer:
    ldi     temp,255
delay_loop_2:
    dec     temp
    brne    delay_loop_2
    rjmp    set

;Set button interrupt service routine
set:
    inc     set_idx
    ldi     temp, 3
    CPSE    set_idx, temp
    reti
    rjmp    timer_enable
    reti

;SW debouncer for time addition button
addminute_sw_debouncer:
    ldi     temp,255
delay_loop_3:
    dec     temp
    brne    delay_loop_3
    rjmp    add_minute

;Time addition button ISR
add_minute:
    inc     idxA
    ldi     temp, 10
    CPSE    idxA, temp
    reti
    ldi     idxA, 9
    reti

;Timer overflow ISR
time:
    dec     delay_idx
    ldi     temp, 0
    CPSE    delay_idx, temp
    reti
    ldi     delay_idx, 3
    rjmp    decC
reti

decC:
    dec     idxC
    ldi     temp, 255
    CPSE    idxC, temp
    rjmp    loop
    rjmp    decB

decB:
    ldi     idxC, 9
    dec     idxB
    ldi     temp, 255
    CPSE    idxB, temp
    rjmp    loop
    rjmp    decA

decA:
    ldi     idxB, 5
    dec     idxA
    ldi     temp, 255
    CPSE    idxA, temp
    rjmp    loop
    rjmp    alarm

;Display functions for each digit and each 7-segment display
displayA:
    ldi     temp, 0
    CPSE    temp, idxA
    rjmp    displayA1
    out     PORTA, zero
    rjmp    displayB

displayA1:
    ldi     temp, 1
    CPSE    temp, idxA
    rjmp    displayA2
    out     PORTA, one
    rjmp    displayB

displayA2:
    ldi     temp, 2
    CPSE    temp, idxA
    rjmp    displayA3
    out     PORTA, two
    rjmp    displayB

displayA3:
    ldi     temp, 3
    CPSE    temp, idxA
    rjmp    displayA4
    out     PORTA, three
    rjmp    displayB

displayA4:
    ldi     temp, 4
    CPSE    temp, idxA
    rjmp    displayA5
    out     PORTA, four
    rjmp    displayB

displayA5:
    ldi     temp, 5
    CPSE    temp, idxA
    rjmp    displayA6
    out     PORTA, five
    rjmp    displayB

displayA6:
    ldi     temp, 6
    CPSE    temp, idxA
    rjmp    displayA7
    out     PORTA, six
    rjmp    displayB

displayA7:
    ldi     temp, 7
    CPSE    temp, idxA
    rjmp    displayA8
    out     PORTA, seven
    rjmp    displayB

displayA8:
    ldi     temp, 8
    CPSE    temp, idxA
    rjmp    displayA9
    out     PORTA, eight
    rjmp    displayB

displayA9:
    out     PORTA, nine
    rjmp    displayB

displayB:
    ldi     temp, 0
    CPSE    temp, idxB
    rjmp    displayB1
    out     PORTB, zero
    rjmp    displayC

displayB1:
    ldi     temp, 1
    CPSE    temp, idxB
    rjmp    displayB2
    out     PORTB, one
    rjmp    displayC

displayB2:
    ldi     temp, 2
    CPSE    temp, idxB
    rjmp    displayB3
    out     PORTB, two
    rjmp    displayC

displayB3:
    ldi     temp, 3
    CPSE    temp, idxB
    rjmp    displayB4
    out     PORTB, three
    rjmp    displayC

displayB4:
    ldi     temp, 4
    CPSE    temp, idxB
    rjmp    displayB5
    out     PORTB, four
    rjmp    displayC

displayB5:
    out     PORTB, five
    rjmp    displayC

displayC:
    ldi     temp, 0
    CPSE    temp, idxC
    rjmp    displayC1
    out     PORTC, zero
    rjmp    loop

displayC1:
    ldi     temp, 1
    CPSE    temp, idxC
    rjmp    displayC2
    out     PORTC, one
    rjmp    loop

displayC2:
    ldi     temp, 2
    CPSE    temp, idxC
    rjmp    displayC3
    out     PORTC, two
    rjmp    loop

displayC3:
    ldi     temp, 3
    CPSE    temp, idxC
    rjmp    displayC4
    out     PORTC, three
    rjmp    loop

displayC4:
    ldi     temp, 4
    CPSE    temp, idxC
    rjmp    displayC5
    out     PORTC, four
    rjmp    loop

displayC5:
    ldi     temp, 5
    CPSE    temp, idxC
    rjmp    displayC6
    out     PORTC, five
    rjmp    loop

displayC6:
    ldi     temp, 6
    CPSE    temp, idxC
    rjmp    displayC7
    out     PORTC, six
    rjmp    loop

displayC7:
    ldi     temp, 7
    CPSE    temp, idxC
    rjmp    displayC8
    out     PORTC, seven
    rjmp    loop

displayC8:
    ldi     temp, 8
    CPSE    temp, idxC
    rjmp    displayC9
    out     PORTC, eight
    rjmp    loop

displayC9:
    out     PORTC, nine
    rjmp    loop

;Alarm function with infinite loop
alarm:
    ldi     temp, 127
    out     PORTA, temp
    rjmp    finish

finish:
    rjmp    alarm

Simulation:

To simulate the project before implementing it on hardware we used Proteus ISIS with which you can program MCU’s with provided hex files and observe the results. You can reach the simulation file from this link.

Schematics

Hardware:

The hardware part is simple. We used three seven-segments, each segment directly connected to a digital I/O port (no encoding/decoding). For interrupts we used the onboard switches on the development kit, and lastly for the alarm sound we used a DC buzzer of 5V, which is controlled by one digital I/O port.

Team:

Kerem Şennik, Burak Daloğulları, Me, Serkan Özatik

Please feel free to contact anything about the project. This project is open source that you can use freely but the authors have no responsibility for its misusage.

Günay Turan