I’ve returned from vacation and brought back a ton of tidbits to share over the next few weeks.
I left the beautiful Pacific Northwest to work on my ranch house in Meade County, South Dakota. I had a number of projects I hoped to complete while there and got through a fair portion of my list that was mostly filled with “wishful thinking” mini-projects. Not all of these are electronics based but bear with me, there are some items of interest.. no guarantee it’s you who is interested though 🙂 I also hit up an AM site with one of my best friends and scored some goodies!
Water: My artesian well has low flow, this isn’t crippling but it can be a hassle. I’ve installed a water tank with a float that allows the well to fill a tank through a 3/4″ in-ground sprinkler solenoid. When pressure drops and the well booster pump kicks on it sucks every bit of water it can including any air it can find out of a contentiously flowing drain into a stock tank a couple hundred feet down the line. Now I’ll have a reservoir to provide extra water on the fly and filter some slight sand in the water supply.
Sewer: I had a collapsed sewer line… talk about the shits! 😀 I thought this was about all I would get done on my vacation as I was going to have to hand dig the line.. it was about 12 wide, 5 foot down. Luck day, I had a good friend stop by and helped me out a bit… that took off a few minutes of rockin’ the shovel.
With all that work out of the way I had some time to meet up with a friend at an AM broadcast site while he was performing some maintenance. I used to apprentice under him but ended up going a different career route. Not sure I made the right choice some days?
I grabbed my Rigol spectrum analyzer (you bring yours with you on vaca as well, right?) and threw a piece of wire into the input about a hundred feet away from the tower… the station is on 810KHz AM.
marker was obviously not on the peak on the reading above.
I took a ton of readings, compared mine to a much more expensive SA (very satisfied with the results) as well as taking some readings on a 948MHz STL transmitter.
I scored a nice directional sampling line and a bunch of SMA connectors with coax, a 20 watt digital OTA cable transmitter and a ton of other little goodies while I was at it. I great score. I got a VCXO, some filters, attenuators and just a ton of stuff I’ll check out here and there.
Finally, I’ve got some video to edit and drop on the YouTube channel as well but I’ll get to that in the coming days.
In the Sept – Oct 13 ARRL publication of QEX, W0PCE has a nice article on ESR (Equivalent Series Resistance) meters. I won’t go into details because I did on a blog post I have saved in drafts for when I finish my own ESR meter but the one sentence version; An ESR meter tests electrolytic capacitors. This meter is a nice analog ESR meter that works well if you are just looking for a good/no-good meter reading.
A friend of mine when in on a buy from FAR circuits to get the board. I believe I reviewed this earlier? If not it’s in my ESR post.. so that’ll come. The board was dirt cheap but if you’re particular you might be better off using protoboard. I don’t know where they got the parts package, but it all worked and came with everything except the connector, meter and battery pack.
Shown is a 33 Ohm resistor for testing to see that 33 Ohms is nearly-full scale which is about perfect for me. I went with a BNC connector and cable… just because I have a ton of them. It’ll be hard to finish my updated ESR meter now that I have this but I’ll find the inspiration some time. 😀
Took a while to clear off the bench tonight. I got a little happy with the ORDER button last week through weekend. I’ve filled two more parts storage containers and luckily one of my new packages with another parts storage container from Amazon; I couldn’t find any locally, I hope they’re still making them.
The last thing I got to was this “Gauge Stepper Breakout” I got off The Rengineer (The Renaissance Engineer)’s Tindie store. Adam took this very nice stepper motor, put a gauge needle on it (which I believe he 3D printed, or at least it looks like it) and some nice diodes for voltage protection. What’s great about this board is you can drive it right from your microcontroller. I’m sure I don’t need to tell you how weird that is.. I worry about big LEDs.. but here this stepper was happy as a clam being powered by my PIC. I still might consider at least buffering this if I was to place it in a permanent circuit.
Adam’s provides links to some Arduino libraries but he was saved me a ton of time and just happened to have some PIC sample code! Not before I had about 75% of a ECCP program completed. (I’ll link some of that code if you’re looking for an example of Enhanced PWM output). Once I had Adam’s code I ported it to the TAUTIC 18F26K22 dev board because I already had one on the breadboard. That was mostly changes in the TMR0 (timer 0) code. The only thing of note is check out my IO comments for wiring. You have to cook up 5V/GND to this board then I drove RC3 –> A1 RC2 — A2, RC1 –> B1 and RC0 –> B2. When I had it flipped around the stepper moved in the opposite direction of how I wanted.
A little video of the code in action:
This code is a little sloppy but I just testing this out and it did the trick:
/*
* File: main.c
* Author: Charles M Douvier Contact at: http://iradan.com
* Core Driver Code by Adam F. of http://www.therengineer.com/
*
* Created on April 4th, 2014
*
* Target Device:
* TAUTIC PIC 18F26K22 Dev Board
*
* Project:
*
*
* Version:
* 1.0
*
*/
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif
#include
#include
#include
#include
//config bits
#pragma config FOSC=INTIO67, WDTEN=OFF, PWRTEN=OFF, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF
//WRT=OFF, FOSC=INTOSC, MCLRE=ON
#define _XTAL_FREQ 4000000 //defined for delay
#define MIN_STEPS_LEFT 23
#define MAX_ACCEL_INDEX 6
#define MAX_STEP 945 /* motor can move 945 steps from stop to stop*/
int an8_value, an9_value; //value for a/d
char buf[10]; //buff for iota
long int fvar; //long for format math
long int tens; //left of decm
long int decm; //decimal places
int tempi; //to add leadign zeros..
int vtxdata; //volts int for TX
int itxdata;
unsigned short defaultAccelTable[][2] =
{
{ 1750, 3},
{ 1149, 3},
{ 926, 3},
{ 794, 3},
{ 709, 3},
{ 666, 4},
{ /*629*/450, 4},
};
unsigned int currentStep;
unsigned char currentState;
unsigned char stateMap[] = {0x09, 0x01, 0x07, 0x06, 0x0E, 0x08};
unsigned char serialBuffer[10];
unsigned char serialByteCount;
static const unsigned char stateCount = 6;
volatile unsigned int uart_data; // use 'volatile' qualifer as this is changed in ISR
/*
*
*/
void interrupt ISR() {
if (PIR1bits.RCIF) // see if interrupt caused by incoming data
{
uart_data = RCREG; // read the incoming data
PIR1bits.RCIF = 0; // clear interrupt flag
}
}
void init_io(void) {
TRISAbits.TRISA0 = 0; // output
TRISAbits.TRISA1 = 0; // output
TRISAbits.TRISA2 = 0; // output
TRISAbits.TRISA3 = 0; // output
TRISAbits.TRISA4 = 0; // output
TRISAbits.TRISA5 = 0; // output
TRISAbits.TRISA6 = 0; // output
TRISAbits.TRISA7 = 0; // output
ANSELA = 0x00; // all port A pins are digital I/O
LATAbits.LATA0 = 0;
PORTAbits.RA0 = 0;
TRISBbits.TRISB1 = 0; //P1C output
TRISBbits.TRISB2 = 0; // P1B output
TRISBbits.TRISB3 = 1; // AN9 speed control 0-5V
TRISBbits.TRISB4 = 0; // P1D output
TRISBbits.TRISB5 = 1; // RB5 = nc
TRISBbits.TRISB6 = 0; // RB6 = nc
TRISBbits.TRISB7 = 0; // RB7 = nc
ANSELB = 0b00001000; //RB3, AN9
TRISCbits.TRISC0 = 0; // output to B2 .. reversed to stoke the right direction
TRISCbits.TRISC1 = 0; // output to B1
TRISCbits.TRISC2 = 0; // output to A2
TRISCbits.TRISC3 = 0; // output to A1
TRISCbits.TRISC4 = 0; // output
TRISCbits.TRISC5 = 0; // output
TRISCbits.TRISC6 = 0; // output
TRISCbits.TRISC7 = 0; // output
ANSELC = 0x00; // all port B pins are digital I/O
}
void uart_xmit(unsigned int mydata_byte) {
while(!TXSTA1bits.TRMT); // make sure buffer full bit is high before transmitting
TXREG = mydata_byte; // transmit data
}
void serial_init(void)
{
//9600 8N1
// calculate values of SPBRGL and SPBRGH based on the desired baud rate
//
// For 8 bit Async mode with BRGH=0: Desired Baud rate = Fosc/64([SPBRGH:SPBRGL]+1)
// For 8 bit Async mode with BRGH=1: Desired Baud rate = Fosc/16([SPBRGH:SPBRGL]+1)
TXSTA1bits.BRGH=1; // select low speed Baud Rate (see baud rate calcs below)
TXSTA1bits.TX9=0; // select 8 data bits
TXSTA1bits.TXEN = 1; // enable transmit
RCSTA1bits.SPEN=1; // serial port is enabled
RCSTA1bits.RX9=0; // select 8 data bits
RCSTA1bits.CREN=1; // receive enabled
SPBRG1=25; // here is calculated value of SPBRGH and SPBRGL
SPBRGH1=0;
PIR1bits.RCIF=0; // make sure receive interrupt flag is clear
PIE1bits.RCIE=1; // enable UART Receive interrupt
INTCONbits.PEIE = 1; // Enable peripheral interrupt
INTCONbits.GIE = 1; // enable global interrupt
__delay_ms(50); // give time for voltage levels on board to settle
uart_xmit('R'); // transmit some data
}
// All this motor and timer code is from Adam with very minor changes to fit the processor
void t0Delay(unsigned int usec)
{
unsigned int t0ticks; //16 microsecond timer0 ticks
unsigned char t0Preload;
if(usec<16)
{
t0ticks=1;
}
else
{
t0ticks = usec/16;
}
t0Preload = 0xFF - t0ticks;
INTCONbits.TMR0IF=0; //clear the flag
TMR0 = t0Preload;
while(INTCONbits.TMR0IF==0)
{
;
}
}
void zeroMotor()
{
unsigned int i;
for (i=0; i < MAX_STEP; i++)
{
LATC=stateMap[currentState];
currentState = (currentState + 5) % stateCount;
t0Delay(1900); //2200 in datasheet
}
//now the motor is zeroed, reset our state variables.
currentStep = 0;
currentState = 0;
LATC=0; //turn off coils
}
void moveMotor(unsigned int targetStep)
{
unsigned int dir;
unsigned int curDelay;
unsigned char speedIndex=0;
unsigned char stepsAtThisSpeed=0;
unsigned int stepsLeft;
if(currentStep<targetStep) { dir = 1; stepsLeft = targetStep-currentStep; } else { dir = -1; stepsLeft = currentStep - targetStep; } while(stepsLeft>0)
{
if(stepsLeft<=MIN_STEPS_LEFT) { //decellerating if(stepsAtThisSpeed==0) { if(speedIndex>0)
speedIndex--;
curDelay=defaultAccelTable[speedIndex][0];
stepsAtThisSpeed=defaultAccelTable[speedIndex][1];
}
}
else
{
//accellerating or steady state
if(stepsAtThisSpeed==0)
{
if(speedIndex<MAX_ACCEL_INDEX) { speedIndex++; curDelay=defaultAccelTable[speedIndex][0]; stepsAtThisSpeed=defaultAccelTable[speedIndex][1]; } //else we're at steady state - do nothing. } } //write step LATC=stateMap[currentState]; if(dir==1) { currentState = (currentState + 1) % stateCount; } else { currentState = (currentState + 5) % stateCount; } t0Delay(curDelay); if(stepsAtThisSpeed>0)
{
stepsAtThisSpeed--;
}
stepsLeft--;
currentStep+=dir;
}
}
int main(void) {
init_io();
serial_init();
// set up oscillator control register, using internal OSC at 4MHz.
OSCCONbits.IRCF = 0x05; //set OSCCON IRCF bits to select OSC frequency 4MHz
OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
ADCON0 = 0b00100101; //select AN9 and enable
ADCON1 = 0b00000000; //speed Vref=AVdd, VssRef=AVss
ADCON2 = 0b00111011; //ledft justified, 20RAD, FRC
INTCONbits.TMR0IE = 0;
TMR0=0;
T0CONbits.T08BIT = 1;
T0CONbits.T0CS = 0;
T0CONbits.PSA = 0;
T0CONbits.T0PS = 0x04;
INTCONbits.TMR0IF = 0;
T0CONbits.TMR0ON = 1;
__delay_us(5);
currentStep = 0;
currentState = 0;
zeroMotor();
__delay_ms(149); //this could be less messy
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
moveMotor(20);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
__delay_ms(149);
moveMotor(940);
moveMotor(5);
while (1) {
//PORTAbits.RA0 = 1; //heart beat
//__delay_ms(50);
//PORTAbits.RA0 = 0;
//__delay_ms(50);
ADCON0 = 0b00100101; //select AN9 and enable
__delay_us(5);
GO = 1;
while (GO) continue; //wait for conversion
an9_value = ADRESH; //AN9 value
fvar = an9_value;
fvar = fvar * 10749; //calibration.. change to meet your needs
fvar = fvar / 256;
tens = fvar / 100;
//tens = tens % 10;
decm = fvar % 100;
vtxdata = fvar / 20;
uart_xmit(vtxdata); // -->RS232
moveMotor(vtxdata);
//moveMotor(5); //from sample code
}
return (EXIT_SUCCESS);
}
Some ECCP ( Enchanced PWM ) code written for the PIC 18F26K22 code I wrote following the screen shot of the output:
@JohnS_AZ shared his beautiful schematic of a tone sequencer [PDF] he designed with me a few weeks ago. I think he designed it for the @tymkrs ? I watched the video and it looked like a fun project, I wasn’t wrong!
I was shooting for sticking this project into an Altoids tin .. I had low expectations which is good because I’m about 1 cm t0 tall on the trimmers. If I found some lower profile parts and removed my DIP sockets I could pull this off. I’m not that motivated to take care of it but if you want to try you know what to do 😉 I used the Adafruit Altoids tin PCB… I purchased a couple of these a while back; they’re good stuff. The oscillator is up on the top board and then ‘LS90, 4051, VCO and the audio amp are below. I am particularly impressed with John’s use of the Pin 5 input on the LM555 to use it as a VCO; I haven’t seen this done much, usually you’re sticking a cap to ground on this input or letting it float (gasp!). I just have to add some stand-offs and a couple connectors and this project is wrapped up.
My version of his circuit:
I was inspired by Alan Wolke’s ( @W2AEW ) video on measuring capacitors and inductors with an oscilloscope. I tried it out and it works pretty reasonably; yeah why wouldn’t it? Anyhow, if you can do it on an oscilloscope it can be done by a microcontroller right?  I have been trying to keep myself from buying this $220 eBay LCR meter on eBay.. it looks nice enough (model: MCH2811C). I needed to make this a project or I was going to pull the trigger on some Chinese garbage!
I went with simple and cheap, I don’t know how well it’ll work out yet but I bread-boarded a proof of concept design. It tested okay after some modification. The first road bump was I had found the PIC output pins had unacceptable rise time compared to 74HC14. The first change was using the PIC output to drive the 74HC14 HEX inverter to get the quick rise time needed. Â I’m throwing a fast edge at the tank circuit that includes an “unknown” inductor and then I do my measurement, just as Alan used his homebrew TDR circuit. I went really low tech on my measurement circuit, I may change this. I used an LM339 comparator and a trimmer as a voltage divider. The first couple waves in the tank “ring” trigger the comparator and I measure the frequency by figuring out the time between the positive pulses by timer. Pretty simple no? It works fine as it turns out. I will have to get a better capacitor and ensure I measure it very accurately to do my math in the PIC and get a reasonable result.
Instead of the usual photos I made another YouTube video. It wasn’t a great one, one take, no editing.. it’s gets my point across (kind of).
My project proof of concept for the PIC L-meter. With the addition of a known inductor and a rotary switch and a little more code you can turn this into a PIC LC-meter in no time.
Nuts and Volts, January 2014 had an article named “The (Nearly) Universal Process Monitor”. The whole magazine had a number of Microchip PIC based articles which is rare these days, as the articles win out to other platforms such as MSP430, Propeller, and Arduino. The article is a pretty nice project but I went to download the code and it’s just a HEX file. Lame! I’ve been guilty of this and I promise never to do it again.
If you’re as disenchanted with building something without modifiable code as I am here is a starting block! I don’t normally program in C but I’m trying to force myself so I can get better. You can trim down many timing delays, and certainly shouldn’t be a problem to optimize my code as I made no effort to do so myself.
When writing the A/D math for this I found some interesting issues; here is one of those: (in pseudo code)
int_Var1 = 8-bit A/D conversion
long_int_Var2 = int_Var1 * 5000
given int_Var1 = 17 Â long_int_Var2 should equal 85000 but it didn’t; It came out 19,464 (roll over from 65,536).
However:
long_int_Var2 = int_Var1
long_int_Var2 = long_int_Var2 * 5000 Â //works like a charm.
Weird? I tried this in an ANSI C compiler and the first equation worked okay; I guess this is just a quirk about XC8. I somewhat understand why it would act that way and caught it quick but it could easily have been a difficult bug to hunt down.
There is obviously not the greatest amount of resolution in an 8-bit conversion but I was really just testing an LCD I just got off eBay and through this would be a fun way to do so, work on my C skills, and help out anyone wanting a head-start on building their own metering application. If you don’t want to do the math yourself you’ll find you get about 20mV/step resolution (5000mV/256) at 8-bit.
Notes: Refresh is slow, clearing the LCD line #2 is slow because I didn’t like the flash of clearing the whole display… it could have also been made more quick had I not written 16 spaces and just written enough to cover my 7 digits of output. I also should have dropped the LSB of my number because it’s a worthless bit considering my resolution. I will if this gets used on anything other than a breadboard test.
I used a standard 44780 16×2 LCD, a Tautic development board, and a PIC 16F1509 (happens to come with the dev board). For programming I used MPLAB X IDE v1.95 and XC8 v1.21 (free version) and the PICkit 3 programmer. The code is commented enough to figure out the hardware setup.
/*
* File: main.c
* Author: Charles M Douvier
* Contact at: http://iradan.com
*
* Created on January 18, 2014, 9:42 AM
*
* Target Device:
* 16F1509 on Tautic 20 pin dev board
*
* Project:
* A/D --> LCD Test
* 8-bit resolution across Vdd to Vref (0-5V)
* for 3.3V operation adjust A/D math
*
* LCD (44780 type) Test with XC8 compiler
* LCD code ported from Mike Pearce's 2001 LCD code for HI-TECH C
* as found on http://www.microchipc.com/
*/
int an9_value; //value for a/d
char buf[10]; //buff for iota
long int fvar; //long for format math
long int ones; //left of decm
long int decm; //decimal places
int tempi; //to add leadign zeros..
void lcd_init(void)
{
LATAbits.LATA5 = 0; // write control bytes
LATC = 0x03;
__delay_ms(150); //power on delay
lcd_strobe();
__delay_ms(5);
lcd_strobe();
__delay_ms(5);
lcd_strobe();
__delay_ms(5);
LATC = 0x02; // set 4 bit mode
__delay_ms(5);
lcd_strobe();
__delay_ms(5);
lcd_write(0x28); // 4 bit mode, 1/16 duty, 5x8 font
lcd_write(0x08); // display off
lcd_write(0x0C); // display on cursor+blink off
lcd_write(0x06); // entry mode
}
int main(void) {
// set up oscillator control register, using internal OSC at 4MHz.
OSCCONbits.IRCF = 0x0d; //set OSCCON IRCF bits to select OSC frequency 4MHz
OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
//display test message
lcd_puts("Testing the LCD.");
lcd_goto(40);
ADCON0 = 0b00100101; //select AN9 and enable
/* ADCON1
* bit 7 ADFM: ADC Result Format Select bit
* 0 = Left justified. Six Least Significant bits of ADRESL are set to ?0? when the conversion result is loaded.
* bit 6-4 ADCS<2:0>: ADC Conversion Clock Select bits
* 110 = FOSC/64
* bit 3-2 Unimplemented: Read as ?0?
* bit 1-0 ADPREF<1:0>: ADC Positive Voltage Reference Configuration bits
* 00 = VREF+ is connected to VDD
*/
ADCON1 = 0b01100000; //left justified, FOSC/64 speed Vref=Vdd
while(1)
{
LATAbits.LATA0 = 0; //debugging
lcd_clrline2(); //clear LCD line 2 by writting " " and return
__delay_us(5);
GO = 1;
while (GO) continue; //wait for conversion
an9_value = ADRESH; //AN9 value
//format value for LCD read out
//value = AD_value * 5000 (because 5000mV is Vref)
//value = value / 256 (8 bit number)
fvar = an9_value;
fvar = fvar * 5000;
fvar = fvar / 256;
ones = fvar / 1000;
ones = ones % 10;
decm = fvar % 1000;
LATAbits.LATA0 = 1; //LED Im-Alive test. I made it through conversion
//page 366 of XC8 user guide
itoa(buf,ones,10); //int conv to buffer
lcd_puts(buf); //outputs "1s" place to LCD.
lcd_puts(".");
//page 374 of XC8 user guide
ltoa(buf,decm,10); //long conversion to buffer
tempi=strlen(buf); //uh, adding leading zeros..
tempi=3-tempi; //probably a better way of doing thing
while (tempi) //first figure out how many zeros
{
lcd_puts("0"); //missed 3-string length
tempi=tempi-1; //then send them until done
}
lcd_puts(buf); //output buffer to LCD
lcd_puts(" V"); //attach some units for display
//delay
__delay_ms(999); //LCD refresh rate
}
return (EXIT_SUCCESS);
}