PIC16 XC8 7 seg cathode multiplexing - Page 1
Forum section: electronics / general

author: nexus
author_rank: Regular Contributor
timestamp: January 08, 2021, 09:43:17 pm
content: Hi All,I am a noob with microcontrollers and embedded programming in general. I just made a simple board with a PIC16F1788 that I want to use as a controller for a standard 4 wire PC fan. This is my first PIC project ever.The schematic is attached.Essentially, the end game function is intended to be this:Turning on, I want the PIC16 to generate 25 kHz PWM out to the fan to control the speed. The user can adjust fan speed by changing the PWM duty cycle using a rotary encoder. Turning the encoder one way will increase PWM duty cycle, turning the other way will decrease.When the user is not adjusting the PWM, the PIC will display the fan speed (in RPM) as read from the fan's tach signal, which is an open collector that pulls low twice per fan revolution.When the user adjusts the PWM, the display will change to show PWM duty between 0-100. After 2 or 3 seconds of not using the encoder, it will go back to the fan tachometer display.I have fabricated one board and have been experimenting with driving the display. This is where I am falling short; before dealing with anything else, I need to solve the display multiplexing problem.As you can see from the schematic, the cathodes are multiplexed as opposed to the anodes. This requires less resistors than conventional anode multiplexing, but the complexity in software is increased. If I drive the display using regular anode multiplexing, it gets very dim as you increase the multiplex speed.I am struggling greatly to write firmware using microchips free XC8 compiler in MPLABX IDE v5.40. Just cannot wrap my head around how to implement cathode multiplexing in software. The digit masking routines in C seems rather challenging, especially for someone like myself with mediocre hobbyist-level programming knowledge.If anyone is an XC8 wizard and/or has dealt with a similar display driving challenge in this ecosystem, any pointers on how to accomplish this in software would be greatly appreciated!

author: ggchab
author_rank: Frequent Contributor
timestamp: January 08, 2021, 10:23:25 pm
content: With this type of configuration, I think you must drive more than one segment at a time. I you drive a full digit, the brightness will be too much dependent on the number of segments turned on.So, if I am not wrong, the average current per segment will be something like 1/32 x 3.5 / 390. A bit less than 0.3mA ! Not that much...I think you should experiment with the 390 ohm resistors to avoid burning the LED. Once you are sure everything is working, you will have to reduce them (or even, remove them). But if your code crashes while a LED is on, you will probably destroy it

author: nexus
author_rank: Regular Contributor
timestamp: January 08, 2021, 11:12:54 pm
content: Yes, the way this this type of arrangement is driven must be as follows:Both cathodes and anodes require a mask. Lets say I want to display an integer "i". If i = 1, then the anodes for display positions 1,2,3 must be off. Only the last digit anode must be on. Then, I would cycle each segment individually for the number "1".If i = 11, then the only difference is that both anodes for digits 3 and 4 will be turned on. Nothing else differs from i = 1.However, lets say I wanted to display i = 12. Now, when I multiplex the segments (cathodes), only 1 segment is in common between "1" and 2". So when I cycle through segments 1 through 7, the anodes need to be masked appropriately as well. I'm just having trouble combining the two masks in software because my understanding of C and XC8 are barely functional.

author: Dabbot
author_rank: Regular Contributor
timestamp: January 09, 2021, 12:39:44 am
content: You multiplex the display in the same manner regardless of which value you're displaying.Set yourself up 4 variables for each digit, then do the following:Start of loopSet RA3 low (turn digit 4 off)Set RC to digit 1's variableSet RA6 high (turn digit 1 on)Wait 5msSet RA6 low (turn digit 1 off)Set RC to digit 2's variableSet RA7 high (turn digit 2 on)Wait 5msSet RA7 low (turn digit 2 off)Set RC to digit 3's variableSet RA4 high (turn digit 3 on)Wait 5msSet RA4 low (turn digit 3 off)Set RC to digit 4's variableSet RA3 high (turn digit 4 on)Wait 5msGo to start of loopHow you do this in code is up to you. Ideally you will want to handle this in an interrupt, but give yourself some time to get it going in a way you will most easily understand first.

author: Ian.M
author_rank: Super Contributor
timestamp: January 09, 2021, 12:42:56 am
content: Its really no different to normal digit multiplexing. You activate one segment at a time, and *all* the digit cathodes that have that segment lit. To do so, you have a buffer array with a byte for each segment, containing bits for each digit. The multiplexing should run in a timer ISR, for one segment at a time. Simply deactivate the previous segment, increment the segment index (with wraparound when it goes out of range), output the digit bits from the array at the current segment index, and activate the segment. The segment index should be static and local to the ISR, and the buffer array needs to be volatile and have file scope or be global.The complexity comes when you need to write numbers to the buffer array, e.g. to clear a digit, you need to go through the whole array, masking that digit's bit to 0. To display a single digit of a number on a specific digit position, again you loop through the whole array masking that digit's bit, but each time masking it to either 1 or 0 depending on whether or not the number needs that segment illuminated. Repeat sequentially with other digits till the whole number is displayed. That's all in the main program, though if you've got any savvy, you'll write a function for it to encapsulate the mess and keep it all in one place.Incidentally, its a poor design choice - resistors are far cheaper than ultrabright 7 segment LEDs vs standard ones, and by multiplexing the segments, at best you can get a 1/8 duty cycle, but for four more resistors you could have multiplexed the digits for a 1/4 duty cycle and double the brightness, + made the code much simpler