Coded: Curiosity-Nano — Thermistor with four 7-segment display

Continuation from: Breadboarded: Curiosity-Nano — Thermistor with four 7-segment display

Next post for TLC5916 Library for ATTINY1627 can be found here: TLC5916 Driver Library

Ok, you’ve gotten your ATTINY1627, TLC5916, four digit-seven segment display, and NTC thermistor from the previous post all wired up and ready to program? I’ll be using Microchip Studio (Atmel Studio) and getting started with Atmel Start. It is assumed you are at least familiar with Atmel/Microchip Studio, how to flash your microcontroller, and enough C programming to be dangerous.

Create a new project and filter down using Attiny1627. I’m using the ‘Attiny1627 Curiousity Nano’, and I’m adding ADC to the project.

Add ADC to the project

Select which pin we want to use as our ADC input.

Click over to the PINMUX menu and setup the pins need to communicate with the TLC5916.

We need eight output pins total. Four pins to communicate with the TCL5916, one for each SDI (serial data in to the TCL5916), CLK (clock), LE (latch enable), and OE (output enable). And four pins to control the display’s L1, L2, L3, and L4 pins.

Export the project, download, and open in Microchip Studio. Now we are ready to start coding. But how does this chip work? It’s basically a 8-bit shift register, one bit for each output pin. You clock in and latch the data, then when you take the Output Enable (OE) pin low, it will sink current for those pins that had corresponding bits clocked in to the register. Take a look at this timing diagram:

TCL5916 Normal Mode Timing

So the clock pulses eight times and the SDI pin is either a 1 or 0. After clocking data, the Latch Enable (LE) pin pulses, latching serial data in the shift register to the output latch. And bringing the Output Enable (OE) pin low will enable the output drivers.

There are three parts to this, sending of the data to the TCL5916, pulsing the LE pin, and bringing OE low.

Let’s take a look at the the method to send data:

void send(char p) {
	uint8_t bitMask = 1;
	for(uint8_t i = 0; i < 8; i++) {
		SDI_set_level((p & bitMask) > 0);
		bitMask = bitMask << 1;
		CLK_set_level(true);
		CLK_set_level(false);
	}
	SDI_set_level(false);
}

We start off with setting our bitMask variable to 1. In binary, this would be represented as:

0000 0001

Now we loop eight times. Each time setting the SDI pin to either a 1 or 0, shifting the bitMask variable once to the left, and quickly pulse the CLK pin.

Setting the SDI pin to either a 1 or 0? Let’s say the number we wanted to send to the display was 2. The bits that create a ‘2’ character are represented in binary as:

1101 1010

We would ‘And’ that number with the bitMask:

  1101 1010
& 0000 0001
-----------
  0000 0000

The result would be 0, which is not larger than 0, so false is passed in to the SDI_set_level method.

On the next line, we shift bitMask one to the left:

// bitMask is equal to 0000 0001
bitMask = bitMask << 1
// bitMask is equal to 0000 0010

Then we pulse the CLK pin to ‘clock in’ the ‘0’ bit because the data pin was low when the clock pulsed.

Next iteration of the loop, the character we are sending is still equal to:

1101 1010

But bitMask is now equal to:

0000 0010

So when you ‘And’ those together:

  1101 1010
& 0000 0010
-----------
  0000 0010

The result would be 2, which is larger than 0, so true is passed in to the SDI_set_level method. And when the CLK pin pulses, it will ‘clock in’ a ‘1’ bit to the shift register.

And at the end, I have a call to bring the SDI pin low because if the last data bit was a 1, it was leaving it high.

The latch is simply pulsed from low to high, and back to low. There are two methods for Output Enable, enable and disable.

void latch() {
	LE_set_level(true);
	LE_set_level(false);
}
void enableOutput() {
	OE_set_level(false);
}
void disableOutput() {
	OE_set_level(true);
}

We can make a method named ‘load’ to automate the task of sending the 8-bit value to the LED Driver:

void load(char p) {
	disableOutput();
	send(p);
	latch();
	enableOutput();
}

The rest of the code involves how to multiplex the select pins and create characters on the display, which I’ve already covered in my post, Engineering Programming Final Project. The key difference here is, instead of assigning a 8-bit value to a port register, we are passing it to a method to be send to the LED driver.

As described in the previous post, Breadboarded: Curiosity-Nano — Thermistor with four 7-segment display, at room temperature, my Negative Temperature Coefficient (NTC) thermistor has about 50k of resistance. Paired with a 50k resistor in a voltage divider configuration with one end connected to 3.3v and the other end connected to ground, the divider output should be 1.65v, which is fed in to the ADC pin. Atmel Start provided a method to read a specific ADC channel. In this case, channel 2. I took one reading before the main loop started, and nth sample averaging to smooth out the drastic changes in the readings.

reading = (ADC_0_get_conversion(2) + reading) / 2;

Taking readings while the thermistor is touching ice (approx. 32°), and while holding it in my fingers (approx. 90°), I created a linear function of:

uint32_t temp = (0.9508 * reading) - 41.2131;

All that was left was to take that reading, convert that to (roughly) degrees in Fahrenheit, and break it down to individual digits to display:

reading = (ADC_0_get_conversion(2) + reading) / 2;
uint32_t temp = (0.9508 * reading) - 41.2131;

ones = temp % 10;
temp = temp - ones;

tens = temp % 100;
temp = temp - tens;
tens /= 10;

hundreds = temp % 1000;
temp = temp - hundreds;
hundreds /= 100;

thousands = temp / 1000;

Then just as before, the main loop will cycle over each digit. As the reading changes, so does the digit on the display.

load(characters[ones]);
L4_set_level(true);
_delay_us(REFRESH_RATE_DELAY);
L4_set_level(false);

load(characters[tens]);
L3_set_level(true);
_delay_us(REFRESH_RATE_DELAY);
L3_set_level(false);

load(characters[hundreds]);
L2_set_level(true);
_delay_us(REFRESH_RATE_DELAY);
L2_set_level(false);

load(characters[thousands]);
L1_set_level(true);
_delay_us(REFRESH_RATE_DELAY);
L1_set_level(false);

Thanks for following along and I hope this helps someone out!

If you wanted to see the whole Microchip Studio Solution, visit my GitHub repository ATTINY1627_TLC5916_7Seg.

And if liked this sof Please check out my recent posts to the right and thanks for following along!

Author:

Leave a Reply