Monday, July 21, 2014

PIC16F1708 Macros

So Easy, a Caveman can PIC it

The PIC processors have a small and primitive instruction set.  This makes it easy to learn, but doing anything remotely useful usually requires four or more instructions, liberally sprinkled with BANKSEL instructions, which all make the resulting code hard to read and error prone.

Your Own Development Kit



As you can see above, it is very easy to make a little development kit of your own, using Vero board and a DIP size PIC.  Once things are working, then you can use Eagle schematic and PCB designer to make a tiny surface mount board, or you can just spray the Vero model with conformal coating and stick it into a soap box with a blob of RVT...

Your Own High Level Language

The solution is to make your own high level language of sorts with macros.  The upshot of macros is that if you find a bug in one, then you can fix that bug in many places all at once.  The downside is that debugging complex macros can be hard, since the PICKit3 debugger has a very limited ability to step into a macro.  The best method is to write the code and debug it in a little test program, before turning it into a macro.

Goto in Macros

All high level languages have Goto statements.  Even Ada has it.  The PIC assembly language makes heavy use of Gotos in conditional branch instructions.

One of the annoying things with a macro language is that you cannot use a label inside a macro, since the second time you use the macro, the assembler will complain about a double defined label.  This makes it hard to do loops in a macro, but not impossible.  The trick is to use the goto $ operator:

;loop
        MOVIW FSR0++                ; Copy buffers from bottom upwards
        MOVWI FSR1++
        DECFSZ varlength, F
        GOTO $-3                    ; loop till zero


BASIC Macros

Possibly the most useful statements in BASIC were the PEEK and POKE instructions.  A typical embedded program is full of them.  So if you feel nostalgic, then Macros are an ideal way to get your revenge on Kernighan, Ritchie and Stroustrup.

Here are a few useful macros to get you going:

; Peek and Poke
_PEEK MACRO #register
        BANKSEL #register
        MOVF #register, W
        ENDM

_POKE MACRO #register, #literal
        BANKSEL #register
        MOVLW #literal
        MOVWF #register
        ENDM
       
_SAVE MACRO #register
BANKSEL #register
MOVWF #register
ENDM

_COPY MACRO #regfrom, #regto
        BANKSEL #regfrom
        MOVF #regfrom, W
        BANKSEL #regto
        MOVWF #regto
        ENDM

_ZAP MACRO #register
        BANKSEL #register
        CLRF #register
        ENDM
        
_INC MACRO #register
BANKSEL #register
INCF #register, F
ENDM

_DEC MACRO #register
BANKSEL #register
DECF #register, F
ENDM

; Turn one or more port pins on
_PORTON MACRO   #port, #state, #mask1
        BANKSEL #state
        MOVLW #mask1         ; get the mask
        IORWF #state, W      ; Set bit(s): OR with 1 mask
        MOVWF #state         ; save the state
        BANKSEL #port
        MOVWF #port          ; set the port
        ENDM

; Turn one or more port pins off
_PORTOFF MACRO   #port, #state, #mask0
        BANKSEL #state
        MOVLW #mask0         ; get state
        ANDWF #state, W      ; Clear bit(s): AND with 0 mask
        MOVWF #state         ; save state
        BANKSEL #port
        MOVWF #port          ; set the port
        ENDM

; Toggle one or more port pins
_PORTTOGGLE MACRO    #port, #state, #mask1
        BANKSEL #state
        MOVLW #mask1         ; get 1 mask
        XORWF #state, W      ; toggle bit(s): XOR with 1 mask
        MOVWF #state         ; Save state
        BANKSEL #port
        MOVWF #port          ; Set the port
        ENDM


; Parameter Call Stack
_PUSHALL MACRO #param1, #param2, #param3
        _POKE varstack1, #param1
        _POKE varstack2, #param2
        _POKE varstack3, #param3
        ENDM

_PUSH1 MACRO #param1
        _POKE varstack1, #param1
        ENDM

_PUSH2 MACRO #param2
        _POKE varstack2, #param2
        ENDM

_PUSH3 MACRO #param3
        _POKE varstack3, #param3
        ENDM

_POP1 MACRO
        _PEEK varstack1
        ENDM

_POP2 MACRO
        _PEEK varstack2
        ENDM

_POP3 MACRO
        _PEEK varstack3
        ENDM


; Function Call
_CALL   MACRO #name, #param1, #param2, #param3
        _PUSHALL #param1, #param2, #param3
        PAGESEL #name
        call #name
        ENDM

; Conditional Branches
; Branch if not equal
; Note: Negative statements are harder to understand
_BNEWR  MACRO   #register, #destination
        BANKSEL #register
        SUBWF #register, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

_BNERR  MACRO   #register1, #register2, #destination
        BANKSEL #register1
        MOVF #register1, W
        SUBWF #register2, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

_BNEWL  MACRO   #literal, #destination
        BANKSEL #destination
        SUBLW #literal
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination

        ENDM

_BNERL  MACRO   #register, #literal, #destination
        BANKSEL #register
        MOVLW #literal
        SUBWF #register, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

; Branch if equal
; Note: Positive statements are easier to understand
_BEQWR  MACRO   #register, #destination
        BANKSEL #register
        SUBWF #register, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQRR  MACRO   #register1, #register2, #destination
        BANKSEL #register1
        MOVF #register1, W
        SUBWF #register2, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQWL  MACRO   #literal, #destination
        BANKSEL #destination
        SUBLW #literal
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQRL  MACRO   #register, #literal, #destination
        BANKSEL #register
        MOVLW #literal
        SUBWF #register, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

; Branch if Bit equal
_BBEQRL MACRO #register, #literal, #label
        BANKSEL #register       ; select bank 0
        BTFSC #register, #literal     ; test RCIF receive interrupt
        GOTO #label
        ENDM

; Function call parameter 'stack'
; Obviously this only works one call deep!
group1      UDATA 0x0020
varstack1   res 1               
varstack2   res 1
varstack3   res 1

La Voila!

Sunday, July 6, 2014

PIC16F1708 a Dilly

I always wondered what is a dilly, why would you pick it and what could you possibly do with it in a circus, but those English are crazy - it must be something in the water.

The last time I used a Microchip PIC processor - some time before the dinosaurs in the previous century - there were only a handful of different chips.  Nowadays there are over 300.  A problem with this proliferation is that it is now much more difficult to find relevant information on the exact chip that you want to use and getting started with the infernal things can be very time consuming.

I wanted to use a PIC16F1708, which includes an ADC, DAC and even a couple of Op Amps for anti-aliasing filters, an all in one CODEC chip for an audio interface widget.

Here are a few tips on things that I discovered along the way.

No Power

When trying out the evaluation board I got with the PICkit3, I kept getting the error: 
Target device was not found. You must connect to a target device to use PICkit 3. 

The problem is that there is no power supply on the evaluation target board. The PICkit3 needs to supply the power and it is turned off somewhere, despite the power LED on the probe lighting up.

Try this:
    1. Go to Run > Set Project Configuration > Customize...
    2. In "Categories" window click once on PICkit 3.
    3. In the right side you have "Option Categories" drop down box, click on "Power".
    4. Now check the box for "Power target circuit from PICkit 3".
    5. Choose the "voltage level" from the drop down menu. (My PIC18F45K20 devkit is 3V)

And your target should now power up and be happy.

MPASM Configuration Registers

You must set the Configuration bits in the assembler code at the top of the main file. 

First configure the processor type PIC16F1708 in MPLabX then:
Change Configuration bits in MPLabX in the Configuration Bits window (Window>PIC Memory Views>Configuration Bits). 

Then you can export the settings by right clicking in the window and selecting “Generate Source Code to Output” and copy this code from the Output Window into your code.

First I would put a List command:
 LIST   P=PIC16F1708

Then copy and paste the generated code:

; PIC16F1708 Configuration Bit Settings

; ASM source line config statements

 #include "p16f1708.inc"

; CONFIG1
; __config 0xFFE0
 __CONFIG _CONFIG1, _FOSC_LP & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_ON & _FCMEN_ON
; CONFIG2
; __config 0xFFFF
 __CONFIG _CONFIG2, _WRT_OFF & _PPS1WAY_ON & _ZCDDIS_ON & _PLLEN_ON & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_ON

Read the PIC data sheet for details, since each of these devices is rather different.

An interesting page layout annoyance to note is that only labels, comments and #include may start in the left-most column.  The __CONFIG macro for example, is indented by one character.

PIC18F45K20 MPASM Example

Here is a LED flasher for the PICkit3 with the PIC18F45K20 development board in MPASM.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; LED flasher
; PIC18F45K20
; Herman Oosthuysen, Copyright Reserved, General Public License (GPL) v2, 2014
; Version 1.00, 7 July 2014
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    LIST   P=PIC18F45K20

; PIC18F45K20 Configuration Bit Settings

#include "p18f45k20.inc"

; CONFIG1H
  CONFIG  FOSC = INTIO7         ; Oscillator Selection bits (Internal oscillator block, port function on RA6 and RA7)
  CONFIG  FCMEN = OFF           ; Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
  CONFIG  IESO = OFF            ; Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

; CONFIG2L
  CONFIG  PWRT = OFF            ; Power-up Timer Enable bit (PWRT disabled)
  CONFIG  BOREN = SBORDIS       ; Brown-out Reset Enable bits (Brown-out Reset enabled in hardware only (SBOREN is disabled))
  CONFIG  BORV = 18             ; Brown Out Reset Voltage bits (VBOR set to 1.8 V nominal)

; CONFIG2H
  CONFIG  WDTEN = OFF           ; Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
  CONFIG  WDTPS = 32768         ; Watchdog Timer Postscale Select bits (1:32768)

; CONFIG3H
  CONFIG  CCP2MX = PORTC        ; CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
  CONFIG  PBADEN = ON           ; PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
  CONFIG  LPT1OSC = OFF         ; Low-Power Timer1 Oscillator Enable bit (Timer1 configured for higher power operation)
  CONFIG  HFOFST = ON           ; HFINTOSC Fast Start-up (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
  CONFIG  MCLRE = ON            ; MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)

; CONFIG4L
  CONFIG  STVREN = ON           ; Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
  CONFIG  LVP = OFF             ; Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
  CONFIG  XINST = OFF           ; Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

; CONFIG5L
  CONFIG  CP0 = OFF             ; Code Protection Block 0 (Block 0 (000800-001FFFh) not code-protected)
  CONFIG  CP1 = OFF             ; Code Protection Block 1 (Block 1 (002000-003FFFh) not code-protected)
  CONFIG  CP2 = OFF             ; Code Protection Block 2 (Block 2 (004000-005FFFh) not code-protected)
  CONFIG  CP3 = OFF             ; Code Protection Block 3 (Block 3 (006000-007FFFh) not code-protected)

; CONFIG5H
  CONFIG  CPB = OFF             ; Boot Block Code Protection bit (Boot block (000000-0007FFh) not code-protected)
  CONFIG  CPD = OFF             ; Data EEPROM Code Protection bit (Data EEPROM not code-protected)

; CONFIG6L
  CONFIG  WRT0 = OFF            ; Write Protection Block 0 (Block 0 (000800-001FFFh) not write-protected)
  CONFIG  WRT1 = OFF            ; Write Protection Block 1 (Block 1 (002000-003FFFh) not write-protected)
  CONFIG  WRT2 = OFF            ; Write Protection Block 2 (Block 2 (004000-005FFFh) not write-protected)
  CONFIG  WRT3 = OFF            ; Write Protection Block 3 (Block 3 (006000-007FFFh) not write-protected)

; CONFIG6H
  CONFIG  WRTC = OFF            ; Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) not write-protected)
  CONFIG  WRTB = OFF            ; Boot Block Write Protection bit (Boot Block (000000-0007FFh) not write-protected)
  CONFIG  WRTD = OFF            ; Data EEPROM Write Protection bit (Data EEPROM not write-protected)

; CONFIG7L
  CONFIG  EBTR0 = OFF           ; Table Read Protection Block 0 (Block 0 (000800-001FFFh) not protected from table reads executed in other blocks)
  CONFIG  EBTR1 = OFF           ; Table Read Protection Block 1 (Block 1 (002000-003FFFh) not protected from table reads executed in other blocks)
  CONFIG  EBTR2 = OFF           ; Table Read Protection Block 2 (Block 2 (004000-005FFFh) not protected from table reads executed in other blocks)
  CONFIG  EBTR3 = OFF           ; Table Read Protection Block 3 (Block 3 (006000-007FFFh) not protected from table reads executed in other blocks)

; CONFIG7H
  CONFIG  EBTRB = OFF           ; Boot Block Table Read Protection bit (Boot Block (000000-0007FFh) not protected from table reads executed in other blocks)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Compare file register to constant and jump to destination
;
; if register >= constant
_BGE    MACRO reg, const, dest
        MOVLW const & 0xff
        SUBWF reg, W
        BTFSC STATUS, CARRY
        GOTO dest
        ENDM

; If register == constant
_BEQ    MACRO reg, const, dest
        MOVLW  const & 0xff
        SUBWF  reg, W
        BTFSC  STATUS, Z
        GOTO   dest
        ENDM

; If register <= constant
_BLT    MACRO reg, const, dest
        MOVLW  const & 0xff
        SUBWF  reg, W
        BTFSS  STATUS, C
        GOTO   dest
        ENDM

; If register != constant
_BNE    MACRO reg, const, dest
        MOVLW  const & 0xff
        SUBWF  reg, W
        BTFSS  STATUS, Z
        GOTO   dest
        ENDM


; Swap the accumulator and a file register
_SWAPWF MACRO  reg
        XORFW  reg,F
        XORFW  reg,W
        XORFW  reg,F
        ENDM

; Decrement the accumulator (a readability macro)
_DECW   MACRO
        ADDLW  0xFF       ; Add W to -1
        ENDM




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        UDATA 0x0020

delay1  res 1
delay2  res 1


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Vector Table
; Code space = 4kW (0000 - 0FFF)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

START   CODE 0x0000
start
        GLOBAL start
        PAGESEL main
        GOTO main

ISR     CODE 0x0004
isr
        GLOBAL isr
        ; ISR code goes here
        RETFIE

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Main
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MAIN    CODE            ; Linker to place immediately after ISR
main
        GLOBAL    main

        CLRF PORTD
        CLRF TRISD
        CLRF delay1
        CLRF delay2

mainloop:
        BTG PORTD,RD1   ; Toggle PORT D PIN 1 (20)
delay:
        DECFSZ delay1,1 ; Roughly 1 Hz with internal clock
        GOTO delay
        DECFSZ delay2,1
        GOTO delay
        GOTO mainloop

        END
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

This should be a good starting template for assembler jobs with this processor.

PIC16F1708 MPASM Example

Annoyingly, each PIC has a somewhat different instruction set and whatever works on one processor, may not work at all on another.  So when developing something, you got to keep the data sheet open on one screen - a dual screen computer setup is pretty much required to get anything done with the least amount of pain.  A virtual desktop system as in Linux and Mac helps a lot, but two screens are better.

On a PIC, doing anything remotely useful, requires at least 2 instructions and usually more like 4 and each instruction takes at least 2 cycles.   Therefore a machine running at 20 MHz is not as fast as one would think at first glance, but even with a 1 MHz effective 'do something' rate, it is still fast enough to make my head hurt thinking about it.

We used to build frequency hopping radios with 256 kHz processors - the advantage of a low clock speed is low power consumption, which is critical on battery powered equipment.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PIC16F1708
; Herman Oosthuysen, Copyright Reserved, 2014
; General Public License (GPL) version 2
; Version 0.01, 5 July 2014
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Power
; The PIC16F1708 uses 4 to 6V
; The PICkit3 can power the target, but 5V may not be available on a laptop PC
; Go to Run > Set Project Configuration > Customize...
; In "Categories" window click once on PICkit 3.
; In the right side you have "Option Categories" drop down box, click on "Power".
; Now check the box for "Power target circuit from PICkit 3".
; Choose the "voltage level" from the drop down menu and set it to 4.5V
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Include Files
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    LIST        P=PIC16F1708
    #include    "p16f1708.inc"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Configuration
; First configure the processor type then do:
; Window>PIC Memory Views>Configuration Bits
; Edit then click "Generate Source Code to Output"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; CONFIG1
; __config 0x17C4
 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_ON & _CLKOUTEN_ON & _IESO_ON & _FCMEN_OFF
; CONFIG2
; __config 0x37FB
 __CONFIG _CONFIG2, _WRT_OFF & _PPS1WAY_OFF & _ZCDDIS_ON & _PLLEN_ON & _STVREN_ON & _BORV_LO & _LPBOR_ON & _LVP_ON


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Macros
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Initialize Clock
_INITOSC MACRO
        BANKSEL OSCCON
        MOVLW B'11111010'   ; Internal oscillator, 16 MHz
        MOVWF OSCCON
        ENDM

; Initialize PORTB
_INITB  MACRO
        BANKSEL PORTB
        CLRF PORTB
        BANKSEL LATB
        CLRF LATB
        BANKSEL ANSELB
        CLRF ANSELB         ; Analogue functions disable = 0
        BANKSEL TRISB       ; Tristate = 1 = Input
        MOVLW B'01110000'   ; Out: B7, In: B6-4
        MOVWF TRISB
        BANKSEL RB7PPS
        MOVLW B'00000'      ; Peripheral Pin Select
        MOVWF RB7PPS        ; LatchB7 = Out
        ENDM


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
        UDATA   0x20
stateb  res 1                ; LATB status
delay1  res 1                ; Delay LSB
delay2  res 1                ; Delay MSB
        

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Vector Table
; Code space = 4kW (0000 - 0FFF)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
START   CODE 0x0000             ; Reset Vector
start
        GLOBAL start
        PAGESEL main
        GOTO main

ISR     CODE 0x0004             ; Interrupt Service Routine
isr
        GLOBAL isr
        ; ISR code goes here
        RETFIE

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Main Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MAIN    CODE              ; Main program start vector
main
        GLOBAL    main

        _INITOSC                ; Configure the Clocks
        _INITB                  ; Configure PortB

        BANKSEL stateb          ; Zap the latch variable
        CLRF stateb
        CLRF delay1
        CLRF delay2

flashled
        BANKSEL PORTB           ; Toggle PORT B bit 7 (pin 10)
        MOVLW 0x80              ; set bit 7 in W
        XORWF stateb, 1         ; XOR W with port & store result
        MOVFW stateb            ; get the result again
        MOVWF PORTB             ; Output the latch state to the port latch

delay
        DECFSZ delay1,1         ; Roughly 4 Hz with internal 16 MHz clock
        GOTO delay
        DECFSZ delay2,1
        GOTO delay
        GOTO flashled

        END
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;



La voila!

Saturday, July 5, 2014

Fletcher Checksum Calculator in Bash

Bash is quite a powerful scripting language and all UNIX machines have it.   The main problem with it is that it was initially designed to work in text mode, making it awkward to process numbers, which tend to be unexpectedly converted back to text strings.

If you need to debug a serial RS232/RS422 avionics device with control messages that end in a checksum or CRC, then it can be hard to generate the checksums.  This example calculates the Fletcher checksum of a hexadecimal message and prints the message out, so that you can copy and paste it into a serial terminal such as cutecom, minicom or screen.

Alternatively, you could print the message directly to the serial device, provided that you initialized it with stty as shown in here: http://www.aeronetworks.ca/2014/01/crcs-and-serial-ports.html

The calculator bc and the printf statement can be used to overcome these limitations.

#! /bin/bash
echo -en "Message = "

# Calculate the Hexadecimal message checksum using bc
MESSAGE="11 22 33 44 55 66"
SUM=0
FLETCHER=0
j=0

for i in $MESSAGE
do
 j=$(echo "ibase=16;$i" | bc)
 printf "%x " "$j"

 SUM=$(echo "$SUM + $j" | bc)
 SUM=$(echo "$SUM%256" | bc)

 FLETCHER=$(echo "$FLETCHER + $SUM" | bc)
 FLETCHER=$(echo "$FLETCHER%256" | bc)
done
printf "%x " "$SUM"
printf "%x\n" "$FLETCHER"


La voila!

Monday, June 30, 2014

Mouse Wiggler

Have you ever watched a movie and got annoyed when the screen saver kicked in?

Some video players interact with the screen savers and will send a dbus command every once in a while to kick the screen saver, but some don't.

The standard fix is to get a child to wiggle the mouse every once in a while.  At least that way they get some exercise!

However, a computer is supposed to automate things, so here is a mouse wiggler script using the xdotool:

# yum install xdotool -y

#! /bin/bash
STATUS=1
while [ "${STATUS:-null}" != null ]; do
  STATUS=`pgrep firefox`
  xdotool mousemove_relative 1 1
  sleep 1
  xdotool mousemove_relative -- -1 -1
  sleep 1
done


It will keep wiggling the mouse pointer a tiny little bit, while Firefox is running.

Modify at your peril...

Sunday, June 29, 2014

Replicating Fedora Machines using Kickstart

Sometimes one wants to install more machines the same as one that is already working.  Replication is one point where Linux is light years ahead of the competition.

Using Kickstart, you can roll out thousands of identically configured machines (with hardware that may be completely different) in a jiffy - Kickstart will automatically install the correct device drivers.

The Skinny

I usually do it manually, since it is too simple to bother scripting:
  • Save file /root/anaconda-ks.cfg
  • Save /etc/yum.repos.d with tar -zcvf yum.repos.d.tgz /etc/yum.repos.d
  • Save the packages list with yum list installed > packages
Use the saved kickstart file to install the new machine, untar the repos and run yum -y install $(cat packages)

Booting Kickstart

Kickstart is the remote control system of the Red Hat Anaconda installer.

A Kickstart installation requires access to the Linux installation media on a local CD-ROM drive or hard drive, or over the network using HTTP or NFS.

If you need just one or two new machines, then do it manually.  If you need hundreds or thousands, then it is worth modifying the Kickstart file to make installation completely hands free.

To use a Kickstart file to install a system:
  1. Boot the system from a Live CD or USB schtick. If you need to modify the boot command, press Esc to access the command line.
  2. If you have not customized the boot medium to use Kickstart, use the ks option to specify the location of the Kickstart file.

    For example, the following boot command specifies that the Kickstart file is on the boot CD:
    boot: linux ks=cdrom:/ks.cfg
    If the Kickstart file is located on an NFS server, you might use a boot command such as the following:
    boot: linux ksdevice=em1 ip=dhcp ks=nfs:192.168.1.100:/ksfiles/ks.cfg
    where ksdevice=em1 specifies the network interface and ip=dhcp specifies that DHCP should be used to configure this interface.

    La Voila!

Friday, June 27, 2014

Ramadan Kareem

Fortunately, it only hits 50 degrees Celsius a few days in the year.


BTW, Lulu Hypermarket seems to be the only place that sells big black umbrellas in summer.

Actually even hotter than John Fogerty rambled on about:
https://www.youtube.com/watch?v=jYguNCDfnCY

Wednesday, June 18, 2014

Franz Kafka strikes again

In a case seemingly from the pen of the late Czech writer Franz Kafka, the United States Court of Appeals for the Seventh Circuit has ruled against terrorism suspect Adel Daoud, saying that he and his attorneys cannot access the evidence gathered against him.

Daoud, an American citizen, was arrested in a FBI sting operation in Chicago, for attempting to bomb a bar.

When Daoud’s lawyers discovered that this case involved secret evidence that they had not been privy to, they eventually asked the court to notify them if any evidence gathered had been done so under a Foreign Intelligence Surveillance Court (FISC) order.

I commend the FBI for catching idiots like this, but this is not how the justice system is supposed to work.  Secret trials are a ridiculous perversion.