Board: Using EasyPIC v7
MCU: PIC16F874
I recently started to try out some code and working on some microchips for counting up and down as well as reset, on 3 7-segment displays. So far I have gotten down the basics, i just lack some optimization and some problem fixing. You can see a hint of C# style in the code xD.
My main issue is that whenever i count up or count down, the segment display that is updating flickers, i know why its happening i just want to know where i can use the "Delay_ms()" where it would slow down the update rate without interrupting the multiplexing.
/*
a1 is for ones, a10 is for tens, and a100 for hundreds
char nums1 and nums2 are the binary codes for the 7-segment.
I was testing out wether to use 0 in the beginning or at the end.
*/
static int a1; static int a10; static int a100;
char nums1 [10] = {0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111, 0b00111111};
char nums2 [10] = {0b00111111, 0b00000110, 0b01011011, 0b01001111, 0b01100110, 0b01101101, 0b01111101, 0b00000111, 0b01111111, 0b01101111};
static int initMain()
{
a1 = 1; a10 = 1; a100 = 1;
}
void main()
{
initMain();
ADCON1 = 0x0F;
TRISA = 0;
TRISB = 0xFF;
TRISD = 0;
do
{
if (PORTB.B0) // Here is where the problem lies i guess
{
Delay_ms(100);
a1 = a1 + 2; // Counting up by 2
}
if (PORTB.B1)
{
Delay_ms(100);
a1 = a1 - 2; // Counting down by 2
}
if (PORTB.B2)
{
initMain(); // Reset
}
PORTA = 1;
PORTD = nums2[a1-1];
Delay_ms(5);
PORTA = 10;
PORTD = nums2[a10-1];
Delay_ms(5);
PORTA = 100;
PORTD = nums2[a100-1];
Delay_ms(5);
if (a1 < 1)
{ a1 = 9; a10--;}
if (a1 > 10)
{ a1 = 1; a10++;}
if (a10 > 10)
{ a10 = 1; a100++;}
if (a10 < 1)
{ a10 = 10; a100--;}
} while(1);
}
Beside the index ranges, that should be checked according to comments, you should have a display refresh function that is called w/o any delays. The best way to do that is using a timer.
For example: Let the timer set a flag every 100ms or so.
Your main loop can than look like this.
int TimerFlag = 0;
main()
{
init();
initTimer();
do{
if(TimerFlag){
TimerFlag = 0;
incrementCounter();
}
refreshDisplay();
} while(1);
}
/////////////////////////////
void timerInterrupt()
{
TimerFlag = 1;
}
With this pattern, the display is updated on every loop but the counter is incremented every 100ms (or the timer interval set by you).
Don't forget to enable the timer interrupt...
I'm trying to run the following code in c on a beaglebone black (microcontroller running Debian).
The code compiles but terminates right when the pwm_init() function is called.
No printf will execute after this line, even the first in the pwm_init() function.
I tried removing pwm_init(), and then wait_for_pwm() will run normally.
This is a code to setup an interrupt timer on the beaglebone in order to communicate with a DAC through SPI.
The code was running and could communicate before this adding.
/*
*Filename: mems.c
*SPI test program to communicate with AD5666 DAC on Micro Mirror Driver board */
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include "iolib.h"
#include <unistd.h>
#define DELAY_NS 62500 // Timer period in ns
// PRU Interrupt control registers
#define PRU_INTC 0x00020000 // Start of PRU INTC registers TRM 4.3.1.2
#define PRU_INTC_GER ((volatile uint32_t *)(PRU_INTC + 0x10)) // Global Interrupt Enable, TRM 4.5.3.3
#define PRU_INTC_SICR ((volatile uint32_t *)(PRU_INTC + 0x24)) // Interrupt, TRM 4.5.3.6
#define PRU_INTC_GPIR ((volatile uint32_t *)(PRU_INTC + 0x80)) // Interrupt, TRM 4.5.3.11
// PRU ECAP control registers (i.e. PWM used as a timer)
#define ECAP 0x00030000 // ECAP0 offset, TRM 4.3.1.2
// Using APWM mode (TRM 15.3.2.1) to get timer (TRM 15.3.3.5.1)
#define ECAP_TSCTR ((volatile uint32_t *)(ECAP + 0x00)) // 32-bit counter register, TRM 15.3.4.1.1
#define ECAP_APRD ((volatile uint32_t *)(ECAP + 0x10)) // Period shadow, TRM 15.3.4.1.5, aka CAP3
#define ECAP_ECCTL2 ((volatile uint32_t *)(ECAP + 0x2a)) // Control 2, TRM 15.3.4.1.8
#define ECAP_ECEINT ((volatile uint16_t *)(ECAP + 0x2c)) // Enable interrupt, TRM 15.3.4.1.9
#define ECAP_ECCLR ((volatile uint16_t *)(ECAP + 0x30)) // Clear flags, TRM 15.3.4.1.11
// R30 is the GPIO register
// R31 is the interrupt register
#define NUMBER_OF_COEFS 87
int xn[NUMBER_OF_COEFS] = { 0 };
int ynn[NUMBER_OF_COEFS] = { 0 };
int xy[2];
static double coefs[NUMBER_OF_COEFS] = { -0.003874396983162784,-0.0037425007502381417,0.0007168162935488041,-0.0015837981969284466,0.001324731958160302,0.000940030114550933,0.002909179571989647,0.002970492669088027,0.0037475240063036684,0.003135242276391628,0.002431551570668268,0.0007465565198417194,-0.0010918847362976609,-0.0032610680167253635,-0.0050886443383995035,-0.0064219306251743396,-0.0067757336585719885,-0.00603689840577871,-0.004073405037328031,-0.001084864753089533,0.002607744624181485,0.006446336960328277,0.009805149887731802,0.012005211009068262,0.01248315933178856,0.010855477027307714,0.007038206816858291,0.0013011753812607633,-0.005726736257811221,-0.013085733616184817,-0.019608024169477135,-0.024024160014903175,-0.025137566107801428,-0.022018671074884637,-0.01412798218138592,-0.0014477915111131118,0.015482420337480308,0.03556527369143834,0.057256428960766804,0.07871540989639365,0.09799912606296178,0.1132905004893123,0.12311069228747347,0.1265004803246064,0.12311069228747347,0.1132905004893123,0.09799912606296178,0.07871540989639365,0.057256428960766804,0.03556527369143834,0.015482420337480308,-0.0014477915111131118,-0.01412798218138592,-0.022018671074884637,-0.025137566107801428,-0.024024160014903175,-0.019608024169477135,-0.013085733616184817,-0.005726736257811221,0.0013011753812607633,0.007038206816858291,0.010855477027307714,0.01248315933178856,0.012005211009068262,0.009805149887731802,0.006446336960328277,0.002607744624181485,-0.001084864753089533,-0.004073405037328031,-0.00603689840577871,-0.0067757336585719885,-0.0064219306251743396,-0.0050886443383995035,-0.0032610680167253635,-0.0010918847362976609,0.0007465565198417194,0.002431551570668268,0.003135242276391628,0.0037475240063036684,0.002970492669088027,0.002909179571989647,0.000940030114550933,0.001324731958160302,-0.0015837981969284466,0.0007168162935488041,-0.0037425007502381417,-0.003874396983162784};
#define REF_ON 0x01000008 //command to turn on internal VREF
#define X_OFFSET 3
#define Y_OFFSET 0
#define X_DRIVE 1
#define Y_DRIVE 2
#define SIZEMAT 2
float tabx[SIZEMAT] = { -100, 100 };
float taby[SIZEMAT] = { -100, 100 };
// Forward definitions
int convert_spi(int dac_val,int channel); //formats bytes for write function
inline void pwm_init();
inline void wait_for_pwm_timer();
int* fir_filterXY(int x, int y);
int main(int argc, char **argv){
int i, fd, debug=0, loop=0, user_loop=0;
int x_off=-1, y_off=-1;
int x_val=-1, y_val=-1;
int freq = -1;
unsigned int buf = REF_ON;
unsigned int dac_value = 1; // 0 to 65535 value to set dac output
unsigned int spi_bytes = 0; // spi communication bytes to send
char direction = 0; // Direction of triangle wave ramp
fd = open("/dev/spidev1.0", O_RDWR);
if(fd < 0) printf("spi failed to open\n");
iolib_init();
//Set LDAC control pin to output
iolib_setdir(9,15,1);
//Tying LDAC low will update dac channel as soon as it has new data
pin_low(9,15); //sel0
write(fd,&buf,4); //set internal vref on
// User loop
int valx = 0;
int valy = 0;
int* ass;
int assx = 0;
int assy = 0;
int i = 0;
int freqcnt = 0;
int freqi = 0;
if (freq>1000)
{
freq = 1000;
}
if (freq<1)
{
freq = 1;
}
freqcnt = (int)((1000000000/DELAY_NS)/freq - 1);
spi_bytes = convert_spi(32000, X_OFFSET);//format bytes for write function
write(fd, &spi_bytes, 4);
spi_bytes = convert_spi(32000, Y_OFFSET);//format bytes for write function
write(fd, &spi_bytes, 4);
printf("In user loop with movement frequency of:%i\n", freq);
pwm_init();
valx = 32000;
valy = 32000;
assx = 32000;
assy = 32000;
printf("starting\n");
while (1){
wait_for_pwm_timer();
spi_bytes = convert_spi(assx, X_DRIVE);//format bytes for write function
write(fd, &spi_bytes, 4);
spi_bytes = convert_spi(assy, Y_DRIVE);//format bytes for write function
write(fd, &spi_bytes, 4);
freqi++;
if(freqi >= freqcnt){
valx = (int)((tabx[i]+100) * 320);
valy = (int)((taby[i]+100) * 320);
freqi = 0;
i++;
if (i >= SIZEMAT)
i = 0;
}
ass = fir_filterXY(valx, valy);
assx = *(ass);
assy = *(ass+1);
}
return 0;
}
/* Function: convert_spi
*
* Takes a dac value (0-65535) and a dac channel (or all channels) and generates the appropriate 32bits to send to AD5666 via SPI
*
* INPUTS
* dac_val: sets the voltage output with voltage output= 2.5*(dac_val/65535)
* channel: selects which dac channel to update with dac_val. 0=DACA ,1=DACB, 2=DACC, 3=DACD, 16=all
*
* RETURNS
* spi_data: integer value to send via spi using to update channel with new dac value
*/
int convert_spi(int dac_val,int channel){
int spi_data=0;
unsigned int nibble1;
unsigned int nibble2;
unsigned int nibble3;
unsigned int nibble4;
nibble1= dac_val & 0xF000;
nibble2= dac_val & 0x0F00;
nibble3= dac_val & 0x00F0;
nibble4= dac_val & 0x000F;
spi_data |=(nibble1>>4);
spi_data |=(nibble2<<12);
spi_data |=(nibble3<<12);
spi_data |=(nibble4<<28);
spi_data |=(channel<<12);
return spi_data;
}
// Initializes the PWM timer, used to control output transitions.
// Every DELAY_NS nanoseconds, interrupt 15 will fire
inline void pwm_init(){
printf("Intereupt setup");
*PRU_INTC_GER = 1; // Enable global interrupts
printf("1");
*ECAP_APRD = DELAY_NS / 5 - 1; // Set the period in cycles of 5 ns
printf("2");
*ECAP_ECCTL2 = (1<<9) /* APWM */ | (1<<4) /* counting */;
printf("3");
*ECAP_TSCTR = 0; // Clear counter
printf("4");
*ECAP_ECEINT = 0x80; // Enable compare equal interrupt
printf("5");
*ECAP_ECCLR = 0xff; // Clear interrupt flags
printf("done\n");
}
// Wait for the PWM timer to fire.
// see TRM 15.2.4.26x
inline void wait_for_pwm_timer() {
register unsigned int __R31;
while (!(__R31 & (1 << 30))) {} // Wait for timer compare interrupt
*PRU_INTC_SICR = 15; // Clear interrupt
*ECAP_ECCLR = 0xff; // Clear interrupt flags
}
int* fir_filterXY(int x, int y){
int i = 0;
double temp = 0;
for (i = 0; i < NUMBER_OF_COEFS - 1; i++)
{
xn[i] = xn[i + 1]; //xn est au bout
}
xn[NUMBER_OF_COEFS-1] = x;
//multiplier par les coef
for (i = 0; i < NUMBER_OF_COEFS; i++)
{
temp += xn[NUMBER_OF_COEFS - i] * coefs[i];
}
xy[0] = (int)(temp+0.5);
for (i = 0; i < NUMBER_OF_COEFS - 1; i++)
{
ynn[i] = ynn[i + 1];
}
ynn[NUMBER_OF_COEFS-1] = y;
temp = 0;
for (i = 0; i < NUMBER_OF_COEFS; i++)
{
temp += ynn[NUMBER_OF_COEFS - i] * coefs[i];
}
xy[1] = (int)(temp + 0.5);
return xy;
}
A couple of recommendations:
printf() can occasionally lag in its output. To be very sure that nothing is being printed, call fflush(stdout) after the print line. This will flush the stdout buffer-- if nothing is printed after the flush, then the println() really is not being executed.
How is your program terminating? If execution does not continue past the pwm_init() function, that almost certainly means that something bad is happening with the memory locations you are writing to. Double check that your register values are correct and that your program isn't being killed due to invalid memory access.
I'm trying to get myself started with the PIC 18F4550 using the C, MPLAB X (both IDE and IPE) and using the PICKit 3.
I've managed to blink one LED without any problems, but as I try to blink more than one LED at simultaneously, it doesn't work.
Please note that I will post my full code at the end of the question. Until then, I'll be writing pseudocode in hope of making my question a little bit clearer.
Assume I want to blink 4 LEDs, each attached to an output pin of the chip, you'd obviously type something like
loop{
output1 = 1;
output2 = 1;
output3 = 1;
output4 = 1;
delay();
output1 = 0;
output2 = 0;
output3 = 0;
output4 = 0;
delay();
}
You would expect that all of the LEDs would turn on and off simultaneously. However, I noticed that only the LED connected to output4 would blink and the rest would remain turned off.
So I tried flipping the order of the output pins as such
loop{
output1 = 1;
output2 = 1;
output4 = 1;
output3 = 1;
delay();
output1 = 0;
output2 = 0;
output4 = 0;
output3 = 0;
delay();
}
As a result, only the LED attached to output 3 would blink, and the rest would remain turned off.
So I figured, somehow, the code is not executing sequentially as I'd expected it to do so.
Can anyone please provide me with an explanation and a possible solution for this?
Thanks a lot!
Here's the full code
#include <xc.h>
#include <p18f4450.h>
#pragma config FOSC = HS
#define outRed PORTBbits.RB0
#define outBlue PORTBbits.RB1
#define outYellow PORTBbits.RB2
#define outGreen PORTBbits.RB3
#define _XTAL_FREQ 10000000
void delay(unsigned int);
void main(void) {
TRISBbits.TRISB0 = 0;
TRISBbits.TRISB1 = 0;
TRISBbits.TRISB2 = 0;
TRISBbits.TRISB3 = 0;
while(1) {
outRed = 1;
outGreen = 1;
outBlue = 1;
outYellow = 1;
delay(1000);
outRed = 0;
outGreen = 0;
outBlue = 0;
outYellow = 0;
delay(1000);
}
}
void delay(unsigned int delayInput) {
unsigned int mul = delayInput/50;
unsigned int count = 0;
for (count = 0; count <= mul; count ++)
__delay_ms(50);
}
This could be a LATCH issue. I have had this problem a few times when I started up. Try writing to the LATB (output latch) register instead of the PORTB register. I always use the LATx for output and the PORTx for input.
Always write to the output latches (in your case LATB) and read inputs from PORTx. Writing to PORTx has unpredictable behaviour.
Here im using the stm32l discovery microcontroller.
First i initialize the ports for the gpio ports for the leds and the user button.
I have it working now and the little program i have now makes the led blink.
Every time you press the button the waiting time gets longer making the led blink longer. the problem now is that as soon as the waiting time exeeds 4 seconds or more its to hard for the microcontroller to notice the button press.
Is there a way to make it that it always notices a button press
int main(void) {
char *RCCp = (char*) 0x40023800;
int *PAp = (int *) 0x40020000;
int *PBp = (int *) 0x40020400;
// RCC Config
*((int*) (RCCp + 28)) |= 0x3f;
*((int*) (RCCp + 32)) |= 1;
// Pin config
*PBp = 0x5000;
*PAp = 0x0000;
int speed = 100000;
int i = 0;
while (1) {
while (i++ < speed); // Waiting time
*(int*) (0x40020414) ^= 0xC0;
i = 0;
if ((*(int*) (0x40020010) & 0x0001) != 0x0) {
speed = speed * 2;
if (speed > 400000) {
speed = 100000;
}
}
}
return 0;
}
One small step forward is to poll the switch status within your busy-wait loop. Like this:
int i = 0;
while (1) {
bool wasSwitchClosedThisPeriod = false;
while (i++ < speed) {
// Poll the switch to see if it is closed.
if ((*(int*)(0x40020010) & 0x0001) != 0) {
wasSwitchClosedThisPeriod = true;
}
}
*(int*) (0x40020414) ^= 0xC0;
i = 0;
if (wasSwitchClosedThisPeriod) {
speed = speed * 2;
if (speed > 400000) {
speed = 100000;
}
}
}
But there is still a lot of room for improvement. For example, you may want to debounce the switch. The switch data register should be volatile, i.e. *(int volatile *)(0x40020010UL). And using interrupts could make the code more flexible, efficient and even allow you to put the microcontroller to sleep while it waits (conserving power). If the GPIO input pin can be configured as an interrupt then you wouldn't need to poll the switch. And if there's a hardware timer available then you could configure it to interrupt when it's time to change the LED.
I'm using C to write a program on an 8051 microcontroller. The compiler I'm using is Keil Microvision. I'm stuck and having trouble figuring out what is missing from my code. I know it's very basic code I just can't figure out what I'm supposed to do.
So pretty much what I am doing is taking sending a sentence out to the user and having them answer yes or no through the serial port and I used a serial interrupt. That part works fine. If I get a no from the person I want to generate a square wave 5kHz by a timer interrupt. I want this square wave to be controlled by an external interrupt turning it on and off when the external interrupt on pin P3.2 is either on or off.
Here is all my code
#include <REG52.H>
#include <stdio.h>
sbit WAVE = P1 ^ 7;
#define BIT(x) (1 << (x))
void timer0() interrupt 1 // timer is controlling square wave timer 0
{
WAVE = ~WAVE;
}
void interrupt0() interrupt 0
{
IE ^= BIT(1);
}
void serial0() interrupt 4
{
unsigned char x;
unsigned int i, z;
unsigned char yes[] = " YES ";
unsigned char no[] = " NO ";
unsigned char nvalid[] = " NOT VALID TRY AGAIN ";
while (RI == 1) {
x = SBUF;
RI = 0;
if (z < 1) {
if (x == 'n') {
for (i = 0; i < 4; i++) {
SBUF = no[i];
while (TI == 0) ; //wait for transmit
TI = 0;
z++;
}
}
} else {
return;
}
if (x == 'y') {
for (i = 0; i < 5; i++) {
SBUF = yes[i];
while (TI == 0) ;
TI = 0;
}
} else if (x != 'n') {
for (i = 0; i < 21; i++) {
SBUF = nvalid[i];
while (TI == 0) ;
TI = 0;
}
}
TI = 0;
return;
}
}
void main()
{
TMOD = 0x20;
TH1 = 0xF6; //baud rate
SCON = 0x50;
TH0 = 0xA4;
IE = 0x93; //enable interrupts
IP = 0x10; // propriety to serial interrupt
TR1 = 1; //start timer 1
TR0 = 1; //clear timer 0
TI = 1;
printf("Hello, Are you okay? Press y for yes and n for no ");
while (1) ;
}
The part I'm having trouble with is these two interrupt from the previous code
void timer0() interrupt 1 // timer is controlling square wave timer 0
{
WAVE=~WAVE;
}
void interrupt0() interrupt 0
{
IE ^= BIT(1);
}
Any hints in the right direction would be greatly appreciated! Thanks. Sorry about formatting
Variables that are modified by interrupts should be defined as volatile:
volatile sbit WAVE = P1 ^ 7;