Ground control to Major Tom. (Serial communication)

11/05/08 | by Jfkfhhfj | Categories: Informative, Code

One of the last modules you will come to know and love is that of the USART module. There are many communication modules that PIC MCUs offer, but USART is going to be one of the simplest and easiest to get off the ground, sending and receiving data.

Fundamentals of communication:

There are two predominate forms of communication serial and parallel. In serial you send bit after bit and parallel you send word after word. Parallel had an advantage before we got some fast transfer rates as it could transfer more data at lower speeds, but it required more infrastructure and it was more error prone as well as complicated. Serial communication is very simple and can be done with as little as two wires. Because of its simplicity we use serial communication. With the USART module there are two types of serial communication, they are synchronous and asynchronous. Either case we have a Baud Rate, or the clock speed of the transmission. We must declare the baud rate before transmission can be done so that the hardware is in sync and expecting results at the right time

Asynchronous:

Asynchronous is full duplex communication, which means you can send an receive data at the same time. We can do this because we have a dedicated wire for transmission(TX) and a dedicated wire for receiving data (RX) Data can be received and sent at the same time. This method is very simple to set up and you can have the same code on all your devices. It is primarily used for device to device communication where synchronization is not important.

Synchronous:

Synchronous is half duplex, which means you can only send data or receive it a given time. In this setup we use one wire as data and the other as clock. In this setup we have a master PIC that sets the clock rate and salve PICs that run off of the master's clock. This is very useful for multi device communication but it requires two sets of code, one for the master and one for the slaves. This however provides great control over your application where the master can control the show and the slaves collect and format data. Lets actually look at what we have to do to send data.

The Registers:

PIE1: (bank 1) Peripheral interrupt enable register 1. This is the register that controls what peripheral interrupts we have on. To use a peripheral interrupt we must first set INTCON bit 6 high. (INTCON,PEIE)

PIR1: (bank 0) Stores the flags for the peripheral interrupts.

SPBRG: (bank 1) Baud rate generator. We move a literal into here which correlates to a baud rate for transmission. The actual baud rate depends on what you send and what the settings for transmission are.

TXSTA: (bank 1)
Transmitter Status and Control register. This register controls weather we use asynchronous or synchronous communication as well as other functions related to the transmitter.

RCSTA: (bank 0)
receive Status and Control Register. This register controls various operation of the receiver and its associated properties.

TXREG: (bank 0) transmission register, we move values to here that we want to be transmitted.

RCREG: (bank 0)
receive register, bits received from transmission will be here. When we read this register the receive flag automatically clears.

Sample code:

#include 
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0 
cblock 0x20
TEMP
DISPLAY
DELAY1
DELAY2
W_TEMP
endc
goto          PORTS;
org 0x04
INTERUPT:
	movwf     W_TEMP     ;Store w to TEMP
	bcf       INTCON,7   ; Turn of interrupts
	btfss     PIR1,RCIF  ; Wait until RCREG is full
	goto      $-1
	bcf       PIR1,RCIF  ;Clear flag
	movf      RCREG,w    ; Move RCREG to w
	movwf     DISPLAY    ; Move w to DISPLAY
	call      ROUTINE    
	movf      DISPLAY,w  ; Move DISPLAY to w
	movwf     PORTC      ; Move it to PORTC
	movf      W_TEMP,w   ; Restore w
	bsf       INTCON,7   ; Turn on interrupts
	retfie               
ROUTINE:
	bcf      STATUS,0    ;Clear flag
	rlf      DISPLAY     ;Rotate the display
	btfss    STATUS,0    ; Check flag
	return               ; Exit if not set
	movlw    0x01        ; If it is, reset display
	movwf    DISPLAY
	return
PORTS:
	bsf       STATUS,RP1 ;Bank 2
	clrf      ANSEL   ; All pins are digital I/O
 	clrf 	  ANSELH   ; All pins are digital I/O
	bcf       STATUS,RP1 ;Bank 0
	bsf       INTCON,7  ;Set up global interrupts
	bsf       INTCON,6  ; Peripheral Interrupts
	bsf       STATUS,RP0  ;Bank 1
	bsf       PIE1,5      ; Interrupt on RX buffer full
	bcf       STATUS,RP0  ; Bank 0
	bcf       PIR1,RCIF   ; Clear the flag
	bsf       STATUS,RP0          ; Bank 1
	movlw     0x00           ; Move the hex value of 0 to multi purpose register W
            movwf     TRISC          ; make IO PortC all output
	movlw     0xff           ; 0xff to w
	movwf     TRISA          ;PORTA input
;serial comm transmit
	movlw     d'25' ; Move the value of 25 or 0x19, to the baud rate generator
	movwf     SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
	bsf       TXSTA,BRGH   ;baud rate high
	bsf       TXSTA,TXEN   ;Enable Transmission
	bcf       TXSTA,SYNC   ;asynchronous
	bcf       STATUS,RP0 ;Exit bank 1
	;Serial comm Receive
	bsf       RCSTA,SPEN ;Set serial port enable high
	bsf       RCSTA,CREN ; Enable receiver
	clrf      PORTB     ; Clear 
	clrf      PORTC     ; Clear display
	movlw     0x01      ; Initialize Display
	movwf     DISPLAY
MAIN:
	bsf       PORTC,0   ; Portc high
	btfsc     PORTA,3   ; Poll the button press
	goto      $-1       ; Goto instruction - 1 
	call 	  TX        ; Call transmission
	bcf       PORTC,0   ; Clear port c,0
	call      DELAY     ; Call delay
	goto      MAIN
TX:
	btfss     PIR1 , TXIF  ; TXIF is set if TXREG has no character
 	goto      $-1          ; Loop until TXIF is cleared
 	movf      DISPLAY,w    ; Data to be transmitted over serial port
 	movwf 	  TXREG        ; Copy data to transmit register
 	bsf       STATUS,RP0   ; RAM PAGE 1
	btfss 	  TXSTA , TRMT  ; Wait until transmission is completed
 	goto      $-1          ; Loop back until transmission completed
	bcf       STATUS,RP0
 	return
DELAY:                ; Generic Dela routine
	decfsz   DELAY1
	goto     DELAY
	decfsz   DELAY2
	goto     DELAY
	return
end

This code is fairly complicated but its not horrible. This is basic code for asynchronous transmission which transmits a value, which triggers an interrupt for the receiving PIC. That receiving PIC then takes the transmitted value and performs an RLF operation on the data then out puts it to PORTC.

The Routine:

1) Startup – Declare environmental variables and goto main port routine
2) Ports – Set up the ports for digital I/O, Enable Interrupts, Enable peripheral interrupts, Enable asynchronous transmitter and receiver. Initialize Display to 1
3) Pool the button and wait for press
4) Button is pressed, Transmit DISPLAY by loading it into the TXREG register and waiting for it to shift out.
5) Delay is called to De-bounce switch.
6) Other PIC receives value, interrupt triggers when RXREG fills
7) Move RXREG into DISPLAY and rotate the bit (if carry reset to 0x01)
8) Return from interrupt

The Breakdown:

        ;serial comm transmit
	movlw     d'25' ; Move the value of 25 or 0x19, to the baud rate generator
	movwf     SPBRG ; this will make the baud rate = to 9600 at a 4mhz internal clock in high speed mode
	bsf       TXSTA,BRGH   ;baud rate high
	bsf       TXSTA,TXEN   ;Enable Transmission
	bcf       TXSTA,SYNC   ;asynchronous
	bcf       STATUS,RP0 ;Exit bank 1
	;Serial comm Receive
	bsf       RCSTA,SPEN ;Set serial port enable high
	bsf       RCSTA,CREN ; Enable receiver

That is the bulk of the code, This section sets up our PIC for serial communication.

The most important part of this section of code is the baud rate. We primarily derive the baud rate from the data sheet and the frequency we run our PIC at. We generally want the least amount of error for our application. For 4MHZ 9600bps in high speed mode is most efficient and least error-prone. If we look at the data sheet to set that baud rate we send 0x19 to SPBRG.

Because we want high speed mode, we get BRGH high, We want to enable the transmitter so we enable TXEN and we want asynchronous communication so we set SYNC to 0.

For RCSTA we want to enable the serial port so we set SPEN high and we want to enable the receiver so we set CREN high.

This is the basic setup for the most efficient asynchronous data transmission with a 4MHZ internal oscillator .

For synchronous and other types of setups refer to the data sheet, its all very self explanatory. One thing you have to consider, there is going to be different setups for the MASTER PIC and the SLAVE PICs.

TX:
	btfss     PIR1 , TXIF  ; TXIF is set if TXREG has no character
 	goto      $-1          ; Loop until TXIF is cleared
 	movf      DISPLAY,w    ; Data to be transmitted over serial port
 	movwf 	  TXREG        ; Copy data to transmit register
 	bsf       STATUS,RP0   ; RAM PAGE 1
	btfss 	  TXSTA , TRMT  ; Wait until transmission is completed
 	goto      $-1          ; Loop back until transmission completed
	bcf       STATUS,RP0
 	return

This is the primary code that does the transmission. We first poll PIR1,TXIF to make sure it is clear, then we move the value we want into TXREG starting the transmission. We wait for the transmission to complete and then we leave.

Once the other PIC receives a value it triggers an interrupt and the following code gets executed.

	btfss     PIR1,RCIF  ; Wait until RCREG is full
	goto      $-1
	bcf       PIR1,RCIF  ;Clear flag
	movf      RCREG,w    ; Move RCREG to w
	movwf     DISPLAY    ; Move w to DISPLAY

We wait until RCREG is full, then we move the value to DISPLAY (or wherever we want).

There we have it. Asynchronous data transmission from one PIC to another. Source and hex are below. Feel free to take and modify it. This will not be the only article on data transmission. This will be a topic we will be visiting frequently and getting very in depth with.

Source Assembly

Assembled HEX

 

Pointers

11/05/08 | by Jfkfhhfj | Categories: Informative, General, Code

Pointers are a very fundamental part of programming, often you need to reference a memory location without directly addressing or calling it, pointers help us do that. With c/c++ pointers can be a little daunting because how they work isn't directly apparent. Luckily for us, assembly works directly with the ram of the MCU and using pointers is extremely easy and almost effortless.

The use of pointers with a PIC mcu is called indirect addressing. We use the FSR(File Select register) and INDF. Essentially we load the value we want to point to into FSR and we address it using INDF. Fairly simple, lets look at an example:

VALUE equ 0x20

movlw   0x03
movwf   VALUE
movlw   0x20
movwf   FSR
movf    INDF,w
movwf   PORTC

The value on port c will be 0x03

First, we set up our variable VALUE to the general purpose address of 0x20.
Then, we set it equal to 0x03
We move the value of 0x20 to FSR (the location of VALUE)
Then we move INDF (The value of the address we loaded into FSR in this case 0x03) to w
Then w to PORTC

Very simple, with this in place you can save memory and make your programs extremely efficient and quick. Using the FSR and INDF will be essential to any remotely complicated code such as communications and robotics and other advance topics.

 

Time

11/04/08 | by Jfkfhhfj | Categories: Informative, General, Code

As your code gets more complex and so does the tasks you wish to perform its often important to take advantage of the build in hardware and gain the ability to multitask. One of the last modules to go over is the timer module. There are several timers build in but we will be focusing on the TMR0 module.

Using the module is fairly straight forward you can turn it “on” with about 3 lines of code. The TMR0 module is a 8 bit timer, thus after 256 clocks it carries over. We can also set a prescaler that sets what the ratio of the timer to the clock is. This prescale value has a max of 256 as well. With max prescaler our timer will reach a max time before rollover of 65536 instructions or .06 seconds. If you need a timer that lasts longer, the other modules hold larger values and thus rollover at a later time.

The Registers:

OPTION_REG: (bank 1) Holds timer configuration information.

The Code:

  #include (p16F690.inc)
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0              ; Begginning of code
PORTS:
	bsf       STATUS,RP1
	clrf      ANSEL          ; All pins are digital I/O
 	clrf 	  ANSELH         ; All pins are digital I/O
	bcf       STATUS,RP1	
	bsf       STATUS,RP0
	movlw     b'0111'    	 ; Turn on 256 prescaler
	movwf     OPTION_REG
	bcf          OPTION_REG,3   ; Prescaler set to timer0
	bcf       OPTION_REG,5	 ; set timer for internal osc
	movlw     0x00           ; Move the hex value of 0 to multipurpose register W
              movwf     TRISC          ; make IO PortC all output
	bcf       STATUS,RP0
	clrf      PORTC
MAIN:
	movlw     0x01
	xorwf     PORTC
	btfss     INTCON,2  ; Check if timer overflow flag is set
	goto      $-1
	bcf       INTCON,2
	goto      MAIN
end


This code enables the timer and uses it to flash an LED when it rolls over.

Breaking it down:


movlw b'0111' ; Turn on 256 prescaler
movwf OPTION_REG
bcf OPTION_REG,3 ; Prescaler set to timer0
bcf OPTION_REG,5 ; set timer for internal osc

This is all the code we need for the timer module. It’s fairly straight forward. We mostly change OPTION_REG to set the timer for what we want.

OPTION_REG:

bit 7 RABPU: PORTA/PORTB Pull-up Enable bit

1 = PORTA/PORTB pull-ups are disabled
0 = PORTA/PORTB pull-ups are enabled by individual PORT latch values

bit 6 INTEDG: Interrupt Edge Select bit
1 = Interrupt on rising edge of RA2/INT pin
0 = Interrupt on falling edge of RA2/INT pin

bit 5 T0CS: Timer0 Clock Source Select bit
1 = Transition on RA2/T0CKI pin
0 = Internal instruction cycle clock (FOSC/4)

bit 4 T0SE: Timer0 Source Edge Select bit
1 = Increment on high-to-low transition on RA2/T0CKI pin
0 = Increment on low-to-high transition on RA2/T0CKI pin

bit 3 PSA: Prescaler Assignment bit
1 = Prescaler is assigned to the WDT
0 = Prescaler is assigned to the Timer0 module

bit 2-0 PS<2:0>: Prescaler Rate Select bits
(table in datasheet)

For the TMR0 module the bits we care about are 0,1,2,3 and 5. For basic operation we set bit 5 low, using the internal oscillator for the clock source. We assign the prescaler to the timer module and not the Watch Dog Timer. We also assign 111 to the prescaler which is the max value.

With that set, our basic code does a XOR operation with the current contents of PORTC, waits until the timer overflows by polling the TMR0 overflow flag bit (INTCON,2) and then returning to main and doing it all over again. In the end we get a blinking LED using highly efficient code.

There we have the TMR0 module in a nutshell. Most of the other timers work in a similar fashion. With this hardware under your belt the possibilities are endless.

Assembly Source

HEX

 

A2D

11/04/08 | by Jfkfhhfj | Categories: Informative, General, Code

It is often very useful to create a digital representation of an analog signal. The primary way we do this is through a analog to digital conversion. This is done by comparing Vin with a reference voltage (Vref) and converting their ratios to a binary number.

Fortunately for us the PIC microcontroller has analog to digital converters built into the hardware. Doing an analog to digital conversion does take time however, but it is an extremely valuable resource. We often use analog to digital conversions for sensor applications, or anything where voltage varies with conditions.
Doing an A2D conversion

As with everything in the PIC, the first thing we have to do is enable the hardware and then set up the port to do an A2D conversion.

The registers:

ADCON0: (bank 0) Stores the config information for the A2D module
ADCON1: (bank 1) Sets the clock for the A2D conversion
ANSEL: (bank 2) Determines whether a port is digital or analog, for A2D we obviously want whatever port were using to be analog
ADRESH: (bank 0 ) Results of conversion are here.

The Code:

    #include 
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)
org 0                                               ; Beginning of code
DISPLAY equ 0x22                        ; DISPLAY var stored at General purpose register 0x22
PORTS:
	bsf       STATUS,RP0        ; select BANK 1
	movlw     0x00               ; Move the hex value of 0 to multipurpose register W
        movwf     TRISC              ; make IO PortC all output
	movlw     0xff                 ; Move the hex value of FF(255) to multipurpose register W
	movwf     TRISA              ; Make  PortA all inputs
	movlw     0x10               
        movwf     ADCON1         ;set A2d Clock to FOSC/32
	bcf       STATUS,RP0       ;Exit bank 1
	bsf       STATUS,RP1       ; BANK 2
	movlw     0xff     
	movwf     ANSEL            ; Set PortA for all analog 
	bcf       STATUS,RP1      ; Exit bank 2, Enter bank 0
	movlw     0x01            
	movwf     ADCON0      ; Configure ADCON and turn it on {Left just, VDD=VREF, RA0,A2D ON}
	clrf      DISPLAY             ; Clear Display Var
MAIN:
	call      A2D                    ; Call subroutine
	movf      DISPLAY,w     ; Move Display to w	
	movwf     PORTC         ; Move W to portC
	goto 	  MAIN            ; Return to main
A2D:
	nop              
	nop
	nop
	nop
	nop                               ; wait 5 uS
	bsf       ADCON0,GO  ; Start the conversion 
	btfss     ADCON0,GO ; Bit will stay high until conversion is complete
	goto      $-1                 ; Until then keep going back
        movf      ADRESH,w  ; Move result to w
	movwf     DISPLAY    ;Then w to Display
	return
 end

The Breakdown:

Most of the code is fairly straight forward, we set up the A2D module in the ports routine and then we do a conversion on RA0 and display the results on PORTC.

movlw     0x10               
movwf     ADCON1         ;set A2d Clock to FOSC/32

These lines move a literal into ADCON1, ADCON1 is the register for the clock of the conversion module, this one sets the time to the oscillator by 32, which is fairly slow. The higher the impedance of your input, the longer you should let the conversion take. The longer the clock, the more data you acquire and the more accurate your results are. However set this to what best fits your application


movlw     0xff     
movwf     ANSEL            ; Set PortA for all analog 

This merely tells the PIC to set porta up for analog signals, if you’re doing a purely digital circuit it’s wise to set these low

movlw     0x01            
movwf     ADCON0      ; Configure ADCON and turn it on {Left just, VDD=VREF, RA0,A2D ON}

ADCON0 is the configuration register of the A2D module, here you set the VREF source, how the data is outputted, and what port to run the conversion on and weather to start or stop the conversion. The datasheet explains what each bit does. This setup, we have our data left justified (A2D is a 10 bit operation, bit we only want 8, so we have the data start with the first 8 bits rather than the first 2), Our VREF is VDD, channel 000 is set (ra0) and we turned the module on.

A2D:
	nop              
	nop
	nop
	nop
	nop                               ; wait 5 uS
	bsf       ADCON0,GO  ; Start the conversion 
	btfss     ADCON0,GO ; Bit will stay high until conversion is complete
	goto      $-1                 ; Until then keep going back
        movf      ADRESH,w  ; Move result to w
	movwf     DISPLAY    ;Then w to Display

This is the main A2D subroutine. The module works by charging an internal capacitor; this takes time to charge so it’s always good practice to kill some time before the conversion starts. Once you set the ADCON0 GO bit high the conversion starts. After the conversion finishes the bit will go low, so we continue to poll that bit until it does so, and then we move the contents of ADRESH to wherever we want. In this case we move it to DISPLAY.
There we have it, a basic Analog to digital conversion. It is worth your time to check out the chapter on A2D conversion in the PIC hand guide. Good luck on your future code, the source and compiled hex are located below

Assembly Source A2D.asm

HEX

 

S vs C

10/21/08 | by Jfkfhhfj | Categories: Informative, Tips

This post is going to start off a new category: Tips.

Posts in the tips category are going to be generally short and they wont be chronologically significant. They are just meant to demonstrate common techniques and/or common errors.

There are two letters that will absolutely devastate your code if your not careful, they are s and c. s and c are used in most bit orientated instructions (bcf, bsf, btfss, btfsc). They stand for Set and Clear or 1 and 0 respectively. When making your code and going over your logic, make sure you note what you are setting high and what needs to be set low. Reversing these two letter will utterly change your code, often for the worst. I've had code that went from useless to perfectly working by changing only one letter. A tip for debugging code, ALWAYS simplify your code first and then always check your bit orientated operations and make sure you're using the right instruction. With that in mind you will spend more time programming and testing and less time debugging.

 

Pages: 1 2 4 6

February 2017
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28        

Ads by Google

Mcuplace.com Store!

This blog is dedicated to working with digital circuitry and the use of microcontrollers, small compact computers on a chip. I will be encompassing many techniques to develop projects, tools to use to write and assemble code and i will be sharing any projects i am currently working on. User feedback is a must! I do not know it all, hell im not even that experienced, but without a general place to get all the info needed i find it very hard to get into the world of microcontrollers without pursing a CE degree. So come one come all and enter the world of mystery and creativity!

Search

  XML Feeds

multiblog engine