STM32 array to control individual GPIO - c

I'm new to embedded systems and C programming. I am currently trying to program a PCB using an STM32 microcontroller to control an array of 8 fans upon receipt of a single commenad. i.e 00001011 will switch on fans 5, 7 and 8. There are a total of 256 possible combinations and it wouldn't be very efficient to program each individual one.
I am thinking of using an array to achieve this using something like;
fan_array[8] = {fan1, fan2, fan3, fan4, fan5, fan6, fan7, fan8};
printf ("Input fan state"); // user would input binary number as shown above
scanf (%d, fan_array);
Would this set the GPIO pins controlling each fan high or low according to the binary values input into the array?

If you think about it, there are 256 possible combinations, but you're only interested in 8 fans, so all that you need to check is 8 bits:
#include <stdio.h>
#define STATE_ON 0x01
#define STATE_OFF 0x00
void enable_relay(unsigned char relay, unsigned char state)
{
/* This is a brute force approach that enables any relay/port combination:
* relay 8: PB2
* relay 7: PB4
* (...)
* relay 1: PA1
*/
switch(relay)
{
case 8:
if(state == STATE_ON)
GPIOB->ODR |= 0x0004;
else
GPIOB->ODR &= ~0x0004;
break;
case 7:
if(state == STATE_ON)
GPIOB->ODR |= 0x0010;
else
GPIOB->ODR &= ~0x0010;
break;
case 1:
if(state == STATE_ON)
GPIOA->ODR |= 0x0002;
else
GPIOA->ODR &= ~0x0002;
break;
}
}
void check_relay(unsigned char fan_map)
{
int i;
unsigned char bit;
unsigned char state;
for(i=0; i < 8; i++) {
bit = (fan_map&(0x01<<i));
state = ((bit != 0)? STATE_ON : STATE_OFF);
enable_relay( (8-i), state);
}
}
int main(void)
{
unsigned char fan_map = 0x0B; /* 0x0B = 00001011 */
check_relay(fan_map);
}
You need the 8-i part as your bit order is in reverse (fan1 as the leftmost bit) with the value order (MSB is the leftmost bit).

Related

How can the average of the ADC Readings be calculated?

The aim is to store the newest 10 ADC readings in an array and then calculate the average of them to be used elsewhere. Removing the oldest each time it is updated.
Regarding the LED timing, it must switch timing from 1s to 0.25s if ADC reading is within boundaries as written below, how can this be implemented correctly? I know my method works but can be done better.
As for the LED's they must change patterns if a switch is pressed as you can see, which they do, but yet again I'm sure it can be done another simpler way!
Below is my code, Also I'm sure there are many an error and plenty of room for optimization, I will gladly accept it all!
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned int timecount0;
unsigned int adc_reading;
volatile uint32_t timing = 1;
volatile uint32_t accumulator = 0;
volatile uint16_t average = 0;
volatile uint16_t samples = 0;
#define LED_RED PORTB = ((PORTB & ~0b00001110)|(0b00000010 & 0b00001110))
#define LED_GREEN PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110))
#define LED_BLUE PORTB = ((PORTB & ~0b00001110)|(0b00000100 & 0b00001110))
#define LED_RGB PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110))
#define DELAY_COUNT 6
volatile uint8_t portdhistory = 0xFF;
void Timer0_init(void)
{
timecount0 = 0; // Initialize the overflow count. Note its scope
TCCR0B = (5<<CS00); // Set T0 Source = Clock (16MHz)/1024 and put Timer in Normal mode
TCCR0A = 0; // Not strictly necessary as these are the reset states but it's good
// practice to show what you're doing
TCNT0 = 61; // Recall: 256-61 = 195 & 195*64us = 12.48ms, approx 12.5ms
TIMSK0 = (1<<TOIE0); // Enable Timer 0 interrupt
PCICR |= (1<<PCIE0);
PCMSK0 |= (1<<PCINT0);
sei(); // Global interrupt enable (I=1)
}
void ADC_init(void)
{
ADMUX = ((1<<REFS0) | (0<<ADLAR) | (0<<MUX0)); /* AVCC selected for VREF,ADLAR set to 0, ADC0 as ADC input (A0) */
ADCSRA = ((1<<ADEN)|(1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(7<<ADPS0));
/* Enable ADC, Start Conversion, Auto Trigger enabled,
Interrupt enabled, Prescale = 32 */
ADCSRB = (0<<ADTS0); /* Select AutoTrigger Source to Free Running Mode
Strictly speaking - this is already 0, so we could omit the write to
ADCSRB, but included here so the intent is clear */
sei(); //global interrupt enable
}
int main(void)
{
ADC_init();
Timer0_init();
DDRD = 0b00100000; /* set PORTD bit 5 to output */
DDRB = 0b00111110; /* set PORTB bit 1,2,3,4,5 to output */
sei(); // Global interrupt enable (I=1)
while(1)
{
if(!(PIND & (1<<PIND2)))
{
PORTD = PORTD |= (1<<PORTD5);
PORTB = PORTB |= (1<<PORTB4);
if(average>512)
{
PORTB = PORTB |= (1<<PORTB5);
}
}
else
{
PORTD = PORTD &= ~(1<<PORTD5);
PORTB = PORTB &= ~(1<<PORTB4);
}
}
}
ISR(TIMER0_OVF_vect)
{
TCNT0 = 61; //TCNT0 needs to be set to the start point each time
++timecount0; // count the number of times the interrupt has been reached
if(!(PIND & (1<<PIND3)))
{
if (timecount0 >= 0) // 40 * 12.5ms = 500ms
{
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110));
}
if (timecount0 >= 8*timing)
{
LED_RED;
}
if (timecount0 >= 16*timing)
{
LED_GREEN;
}
if (timecount0 >= 24*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00000110 & 0b00001110));
}
if (timecount0 >= 32*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001000 & 0b00001110));
}
if (timecount0 >= 40*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001010 & 0b00001110));
}
if (timecount0 >= 48*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001100 & 0b00001110));
}
if (timecount0 >= 56*timing)
{
PORTB = ((PORTB & ~0b00001110)|(0b00001110 & 0b00001110));
}
if (timecount0 >= 64*timing)
{
timecount0 = 0;
}
}
else
{
if (timecount0 >= 0)
{
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110)); //ALL OFF
}
if (timecount0 >= 8*timing)
{
LED_RED;
//PORTB = ((PORTB & ~0b00001110)|(0b00000010 & 0b00001110)); //RED
}
if (timecount0 >= 16*timing)
{
LED_GREEN;
}
if (timecount0 >= 24*timing)
{
LED_BLUE;
}
if (timecount0 >= 32*timing)
{
timecount0 = 0;
}
}
}
ISR (ADC_vect) //handles ADC interrupts
{
adc_reading = ADC; //ADC is in Free Running Mode
accumulator+= adc_reading;
if ((adc_reading > 768) & (adc_reading <= 1024))
{
timing = 10;
}
if ((adc_reading >= 0) & (adc_reading<= 768) )
{
timing = 2.5;
}
samples++;
if(samples == 10)
{
average = accumulator/10;
accumulator = 0;
samples = 0;
}
}
Depending on your processors, you may way to keep ISR() speedy and avoid expensive /,%.
The LED stuff, I'd handle in a timer interrupt.
#define N 10
volatile unsigned sample[N];
volatile unsigned count = 0;
volatile unsigned index = 0;
volatile unsigned sum = 0;
ISR (ADC_vect) {
if (count >= N) {
sum -= sample[index];
} else {
count++;
}
sample[index] = ADC;
sum += sample[index];
index++;
if (index >= N) {
index = 0;
}
}
unsigned ADC_GetAvg(void) {
block_interrupts();
unsigned s = sum;
unsigned n = count;
restore_interrupts();
if (n == 0) {
return 0; //ADC ISR never called
}
return (s + n/2)/n; // return rounded average
}
I'd recommend an integer version of a low pass filter than the average of the last N.
In terms of the moving averaging w/ N = 10, chux - Reinstate Monica has provided the solution. Chux - Reinstate Monica also recommends looking at an integer version of a low pass filter. I personally like the Exponentially Weighted Moving Average (EWMA) because it's fairly simple to code and only requires a few values to do the averaging. This is compared to having to hold 10 in array in your case. I would recommend Elliot Williams's Make: AVR Programming Chapter 12 for this. In case you don't have access to this readily, the EWMA, as explained in Make AVR, starts with
y_current = (1/16)*x_current + (15/16)*y_previous
where in our case, y_current is the updated EWMA value, x_current is the newest sample from your ADC, and y_previous is the last EWMA value. The choice of 16 can also be changed along with the weights, 1 and 15. Keeping it a power of 2 is important though, as you will see. As shown in Elliot Williams book, you multiply by 16 and compensate for rounding problems and get the following,
16*y_current = x_current + 16*y_previous - (16*y_previous - 8)/16.
Now, I know this looks ugly but what we have is scaled by 16 average value that's an integer and only relies on integer addition (the 16*y_previous is stored as one value so you don't do the multiplication) and a bit shift; that's the reason why a power of 2 was chosen in the EWMA, dividing by 16 is the same as a right bit shift of 4. Ok, so what does this average look like in code:
// Snippet from Make: AVR Programming
uint16_t x_current; // ADC value.
uint16_t y_current; // Average ADC value.
// Get the EWMA.
y_current = x_current + y_current - ((y_current - 8) >> 4);
// Send the value over USART (assuming it's wired up). Remember that
// y_current is scaled by 16.
printf("%d\n",(y_current>>4));
The above is just the EWMA that you can use in your code and an example of sending it, which is just a reminder that the value if scaled. Remember, this is just the averaged ADC value. Likely you will be wanting to use the ADC value as an input to a function to get the value of some measured quantity. Rather than actually using a function and calculating values, you can create a look-up table where the index is the ADC value and the array entry at that index is the precalculated value.
In terms of your other code, the things that could be corrected/streamlined are in your ISRs. In ISR(TIMER0_OVF_vect) you have some bit operations that are constant and can be precalculated so that your not doing it everytime the ISR(TIMER0_OVF_vect) fires.
PORTB = ((PORTB & ~0b00001110)|(0b00000000 & 0b00001110));
becomes
PORTB = ((PORTB & 0b11110001)|(0b00000000)); // Saves a bit inversion and '&'
which shows that your ORing, |, doesn't affect the result, because you're ORing against all zeros.
Finally, in your ISR (ADC_vect) you are using the bitwise, &, and not the logical and, &&. You get the same result but it's like using a wrench to hammer in a nail. I know this is a lot but I hope it helps and let me know if you need clarification.

Terminal Emulator Receiving Bytes from Unknown Source via UART ATmega328p

I am playing around with a DFRobot Romeo board that has an ATmega328p MCU on it.
I wrote a program to transmit the binary and decimal representation of 4 DIP switches wired as inputs into PORTD (PD2 -> PD5). UART is wired to PORTD, PD0 (Rx) and PD1 (Tx).
The program works as expected except for when the program first runs the terminal emulator I am using on my Mac appears to receive and display the following binary/decimal number: 00000011 3, then 00000000 0 appears, or whatever number is set by the DIP switches.
If I reset the controller with the UART connection maintained, I get the same result over and over. The terminal emulator I am using is CoolTerm. I get the same result even if I flush the buffer before reconnecting. The same result is occurring if I try another terminal emulator (i.e. Serial, SerialTools).
I reviewed my code, and do not see any obvious errors. I contacted a peer who says he has seen this behaviour as well, but has no suggestions for a solution other than clearing the terminal buffer. I have also tried a few different physical configurations such as powering the board by USB only, powering the board from a separate power supply without the USB programming cable connected, and with the USB cable connected. I get the same results on the Terminal.
Can someone take a look at this and offer a suggestion as to why this behaviour is occurring?
Here is the C code for the program.
/*
Connection Diagram:
Serial USART:
Atmega328p Romeo Board FTDI Cable
PD0 -> D0 -> Orange Rx
PD1 -> D1 -> Yellow Tx
GND -> GND -> Black Ground
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Digital I/O:
Atmega328p Romeo Board IO Board Jumper Component
PD2 -> D2 -> JP2_7 DS1 Dip Switch 1 - MSB
PD3 -> D3 -> JP2_6 DS2 Dip Switch 2
PD4 -> D4 -> JP3_2 DS3 Dip Switch 3
PD5 -> D5 -> JP2_5 DS4 Dip Switch 4 - LSB
*/
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <string.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 0
#define DECIMAL 10
//functions
void initUART(unsigned int baud, unsigned int speed);
void initDIO(void);
void printDec(int sum);
void printByte(uint8_t num);
void printCR(void);
void delay_ms (uint16_t ms);
int main(void)
{
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
//initialize DIO (Digital I/O)
initDIO();
//value to be determined
uint8_t value = 0;
//previous value
uint8_t previous = 0;
while(1)
{
previous = value;
//Set or Clear LSB
if((PIND & 0b00100000) == 0)
{
value |= (1 << 0);
}
else
{
value &= ~(1 << 0);
}
//Set or Clear 2nd bit
if((PIND & 0b00010000) == 0)
{
value |= (1 << 1);
}
else
{
value &= ~(1 << 1);
}
//Set or Clear 3rd bit
if((PIND & 0b00001000) == 0)
{
value |= (1 << 2);
}
else
{
value &= ~(1 << 2);
}
//Set or Clear MSB
if((PIND & 0b00000100) == 0)
{
value |= (1 << 3);
}
else
{
value &= ~(1 << 3);
}
//if value has changed since previous, print the result
if(value != previous)
{
printByte(value);
printDec(value);
printCR();
}
//add small delay to loop in attempt to stablize the button state (debounce buttons)
delay_ms(80);
}
}
void initUART(unsigned int baud, unsigned int speed)
{
unsigned int ubrr;
//double speed is OFF in this lab (it is 0)
if(speed)
{
//double rate mode
ubrr = F_CPU/8/baud-1;
//set double speed mode
UCSR0A = (speed << U2X0);
}
else
{
//normal rate mode
ubrr = F_CPU/16/baud-1;
}
//set the baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)(ubrr);
//enable Tx and Rx pins on MCU
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//set control bits, 8 bit char, 0 stop, no parity
UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}
void initDIO(void)
{
//set inputs for Port B
//PD2 (D2) DS1
DDRD &= ~(1 << PD2);
//PD3 (D3) DS2
DDRD &= ~(1 << PD3);
//PD4 (D4) DS3
DDRD &= ~(1 << PD4);
//PD5 (D5) DS4
DDRD &= ~(1 << PD5);
//Set PORTD PD2 & PD3 & PD4 & PD5 Pull-Up Resistors
PORTD |= ((1 << PD2) | (1 << PD3) | (1 << PD4) | (1 << PD5));
}
void printDec(int sum)
{
//character buffer for integer to string converstion
char buffer[sizeof(int)*8+1];
//convert integer to decimal represented string
itoa(sum, buffer, DECIMAL);
//transmit character string via UART
for(int i = 0; i < strlen(buffer); i++)
{
// Wait for empty transmit buffer
while( !(UCSR0A & (1 << UDRE0)) ) {};
//start transmission of character string
UDR0 = buffer[i];
}
}
void printByte(uint8_t num)
{
//transmit binary characters via UART
for(int i = 7; i >= 0; i--)
{
// Wait for empty transmit buffer
while( !(UCSR0A & (1 << UDRE0)) ) {};
//start transmission of character string.
//can add character 'O' or integer 48 to statement below,
//both result in the correct character being transmitted.
UDR0 = ((num >> i) & 1) + '0';
}
//transmit one white space
//Wait for empty transmit buffer
while( !(UCSR0A & (1 << UDRE0)) ) {};
UDR0 = 32;
}
void printCR(void)
{
//transmit carriage return
//Wait for empty transmit buffer
while( !(UCSR0A & (1 << UDRE0)) ) {};
UDR0 = 13;
//transmit line feed
//Wait for empty transmit buffer
while( !(UCSR0A & (1 << UDRE0)) ) {};
UDR0 = 10;
}
void delay_ms (uint16_t ms)
{
uint16_t i;
for (i = 0; i < ms; i++)
_delay_ms(1);
}
Here is a screen shot of what I see on the program first scan.
Maybe there is not enough time between enabling the weak pull-ups and the first sampling.
Try to put a delay before the loop!

Code for 4-Digit 7-Segment Display: Why doesn't code work for 2 of 4 digits?

I have written 2 functions (gpio_write & gpio_set_function) to be able to control the digits and segments of a 7-Segment Display (Common Anode LED), that is connected to my RPi via Bipolar Junction Transistors: Collector to common power, base to RPi pins via 1 kOhm resistor, and emitter to digit nodes on the display (image here).
The segments are connected directly to pins on the RPi.
The code successfully lights up digits 1 & 3, (and all segments) but digits 2 and 4 do not light up. I need some help figuring out why that is.
I have double & triple checked the wiring, made sure the digits are connected to pins 10 thru 13, and the segments to pins 20 thru 26 (period to pin 27). I have checked the code as well, and couldn't find a problem with it. I have also made sure that each of the digits actually works by disconnecting from the RPi and lighting up each of the segments on each digit independently (Segment B of Digit 3 is burned, all the others work).
What could be another issue that would cause digits 2 and 4 not to light up? Or perhaps something IS wrong with the code?
#include "timer.h"
#include "gpio.h"
void gpio_set_function(unsigned int pin, unsigned int function) {
unsigned int mask = 7;
if ( (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) || (function < GPIO_FUNC_INPUT || function > GPIO_FUNC_ALT3) )
;
else{
switch (pin/10){
case 0:
*GP_FSEL0 &= ~((mask)<<(3*pin));
*GP_FSEL0 |= function<<(3*pin);
break;
case 1:
pin = pin%10;
*GP_FSEL1 &= ~((mask)<<(3*pin));
*GP_FSEL1 |= function<<(3*pin);
break;
case 2:
pin = pin%20;
*GP_FSEL2 &= ~((mask)<<(3*pin));
*GP_FSEL2 |= (function<<(3*pin));
break;
case 3:
pin = pin%30;
*GP_FSEL3 &= ~((mask)<<(3*pin));
*GP_FSEL3 |= function<<(3*pin);
break;
case 4:
pin = pin%40;
*GP_FSEL4 &= ~((mask)<<(3*pin));
*GP_FSEL4 |= function<<(3*pin);
break;
default:
;
}
}
}
void gpio_write(unsigned int pin, unsigned int value) {
if ( (pin < GPIO_PIN_FIRST || pin > GPIO_PIN_LAST) || (value < 0 || value > 1) ) ;
else {
switch (pin/32){
case 0:
if (value==1){
*GP_FSET0 &= ~(1<<pin);
*GP_FSET0 |= 1<<pin;
}
else
*GP_CLR0 |= 1<<pin;
break;
case 1:
if (value==1){
*GP_FSET1 &= ~(1<<(pin-32));
*GP_FSET1 |= 1<<(pin-32);
}
else
*GP_CLR1 |= 1<<(pin-32);
break;
default:
;
}
}
}
void main(void) {
volatile int i;
for(i = 10; i < 14 ; i++)
gpio_set_function(i, GPIO_FUNC_OUTPUT);
for(i = 19; i < 27 ; i++)
gpio_set_function(i, GPIO_FUNC_OUTPUT);
while (1){
for(i = 10; i < 14 ; i++)
gpio_write(i, 1);
for(i = 19; i < 27; i++)
gpio_write(i, 0);
for (volatile int delay = 0xF0000; delay != 0; delay--);
for(i = 10; i < 14 ; i++)
gpio_write(i, 0);
for (volatile int delay = 0x0000F; delay != 0; delay--);
}
}
ADDITIONAL ATTEMPT:
I tried simplifying the code by not using any functions. Still getting the same result. All segments work on digits 1 and 3. Digits 2 and 4 do not light up.
void main(void) {
// set functions
unsigned int mask = 7;
*GP_FSEL1 &= ~((mask)<<(3*1)); // reset pin 11
*GP_FSEL1 |= GPIO_FUNC_OUTPUT<<(3*1); // set pin 11 to OUTPUT
*GP_FSEL2 &= ~((mask)<<(3*0)); // reset pin 20
*GP_FSEL2 |= GPIO_FUNC_OUTPUT<<(3*0); // set pin 20 to OUTPUT
*GP_FSET0 &= ~(1<<11); // reset SET Reg of pin 11
*GP_FSET0 |= 1<<11; // set SET Reg of pin 11 to high
*GP_CLR0 &= ~(1<<20); // reset CLR Reg of pin 20
*GP_CLR0 |= 1<<20; // set CLR Reg of pin 20 to high
}

MPLAB XC8 Compiler PIC18F452 Multiplexed Seven Segment Display Code working properly

I am currently working on a code involving the MPLAB XC8 Compiler, PIC18F452 with a Multiplexed Seven Segment Display. I want to use two pushbuttons connected to pins RB2 and RB3 of PORTB of the PIC18F452 to increment and decrement a variable "count" and display this number from 0 to 99 on this display. Schematic and code is show below.
This code relatively functions as it is, and I do not believe the schematic is to blame for the issues I am seeing, nor is the byte array not correct as I am able to see each number when using the array with a 1 segment display.
The issue arrises when trying to use this multiplexing scheme shown in the below figure. I can successfully display two numbers on the seven segment displays, but there are strange anomalies present when executing this code. For one, I seem to not be able to display the number 1, 4 and occasionally 7 on either display, but when this digit does not show the display is blank, and when the button is again pushed the next number is shown as expected.
for example:
The display shows the numbers as follows for number sequences:
9... 10... 11... 12 13... 14... ect...
or
34.... 35... 36... 37....
Not sure where the issues lies, and debugging is not going well... any help would be appreciated.
Schematic for Multiplexed 7 Segment Display
#define _XTAL_FREQ 10000000
#include <xc.h>
#include <stdlib.h>
#define Digit1 PORTBbits.RB1 //variable to sink current to PNP base
#define Digit2 PORTBbits.RB2 //variable to sink current to PNP base
#define Switch1 PORTBbits.RB4 //switch decrement variable
#define Switch2 PORTBbits.RB3 //switch increment variable
#define Pressed1 1 //pressed is high
#define Pressed2 1 //pressed is high
void initialize();
void segment1 (void);
void segment2 (void);
void buttonPress(void);
void delay_ms(unsigned int);
void sendPattern1(unsigned char pattern);
void sendPattern2(unsigned char pattern3);
unsigned char rotateLeft(unsigned char pattern, int no);
unsigned char MSD, LSD, count=0;
Main code
void main(void){
initialize();
Digit1 = 0;
Digit2 = 0;
while(1){
buttonPress();
MSD = count/10 ;
segment1();
Digit2 = 1;
delay_ms(10); // Delay for 10 ms
Digit2 = 0;
LSD = count%10;
segment2();
Digit1 = 1;
delay_ms(10); // Delay for 10 ms
Digit1 = 0;
}
return;
}
Functions to index Most Significant Digit and Least Significant Digit from array to be sent to the ports to sink current low for common annode display.
void segment1(void){
unsigned char segArrayC[]={0b11000000,0b11111001,0b00100100,
0b00110000,0b00011001,0b00010010,
0b00000010,0b11111000,0b00000000,0b00011000};
unsigned char pattern;
pattern = segArrayC[MSD];
sendPattern1(pattern);
return;
}
void segment2(void){
unsigned char segArrayD[]= {0b11000000,0b11111001,0b00100100,
0b00110000,0b00011001,0b00010010,0b00000010,
0b11111000,0b00000000,0b00011000};
unsigned char pattern3;
pattern3 = segArrayD[LSD];
sendPattern2(pattern3);
return;
}
Button Press Code
void buttonPress(void){
if (Switch1 == Pressed1) {
++count;
delay_ms(100);
}
if (Switch2 == Pressed2) {
--count;
delay_ms(100);
}
if(count>=99||count<0)
{
count=0;
delay_ms(100);
}
return;
}
Function to rotate bytes in array two places to left to be displayed on PORTs
/** Rotate pattern to the left 'no' number of bits
*/
unsigned char rotateLeft(unsigned char pattern, int no) {
return (((pattern << no) & 0xFF) | (pattern >> (8-no)));
}
Functions to output indexed array char to PORTC and PORTB pins
void sendPattern1(unsigned char pattern) {
// Send pattern to appropriate port pins
unsigned char pattern2;
PORTC = pattern;
pattern2=rotateLeft(pattern, 2);
PORTB = pattern2;
return;
}
void sendPattern2(unsigned char pattern3) {
unsigned char pattern4;
PORTC = pattern3;
pattern4=rotateLeft(pattern3, 2);
PORTB = pattern4;
return;
}
Delay Function
void delay_ms(unsigned int n){
while (--n) _delay(2500);
}
Initialize pins to be used (0 output, 1 input)
void initialize() {
TRISC = 0;
TRISBbits.TRISB0 = 0;
TRISBbits.TRISB1 = 0;
TRISBbits.TRISB2 = 0;
TRISBbits.TRISB4 = 1;
TRISBbits.TRISB3 = 1;
PORTC = 0x00;
PORTB = 0x00;
}
In sendPattern() you write a rotated bit pattern to PORTB.
This interferes with the setting the common anode control. So you see both digits only, if both right hand segments are turned on. According your schematic you should write a 0 to turn on the common anode. Try this:
void main()
{
static const unsigned char segArray[]=
{ 0b11000000, 0b11111001, 0b00100100, 0b00110000, 0b00011001,
0b00010010, 0b00000010, 0b11111000, 0b00000000, 0b00011000
};
TRISC = 0; //PortC all OUTPUT
PORTC = 0xFF; //PortC all HIGH = IDLE = LED_OFF
TRISBbits.TRISB0 = 0; //Output unused
TRISBbits.TRISB1 = 0; //Output Digit1
TRISBbits.TRISB2 = 0; //Output Digit2
TRISBbits.TRISB4 = 1; //Input: Switch PLUS
TRISBbits.TRISB3 = 1; //Input: Switch MINUS
PORTB = 0x00;
unsigned char count=0;
for(;;)
{
//Handle buttons
if (Switch1 && count<99)
{
++count;
delay_ms(100);
}
if (Switch2 && count > 0)
{
--count;
delay_ms(100);
}
//Write high digit
PORTC = segArray[count/10];
Digit2 = 0;
delay_ms(10); // Delay for 10 ms
Digit2 = 1;
//Write low digit
PORTC = segArray[count%10];
Digit1 = 0;
delay_ms(10); // Delay for 10 ms
Digit1 = 1;
}
}

Converting ASCII to Decimal C microcontroller bluetooth

I'm simply trying to convert ASCII to Decimal on my microcontroller (in C) before it is sent over bluetooth.
Control + F and find ASCII in this library.
http://ww1.microchip.com/downloads/en/DeviceDoc/MPLAB_C18_Libraries_51297f.pdf
This is the relevant section of my code.
PIR1bits.ADIF=0;
while(x ==1)
{
ConvertADC(); //ADCONbits.GO/DONE=1 , Starts the A/D conversion process.
while(BusyADC()){} // Wait for completion
Delay10KTCYx(0);
Delay10KTCYx(0);
Delay10KTCYx(0);
Delay10KTCYx(0);
while(BusyUSART());
putcUSART(ADRESH); //prints or places the character into bluetooth
while(BusyUSART());
putcUSART(ADRESL); //prints or places the character into bluetooth
PIR1bits.ADIF=0;
}
//CloseADC(); // Disable A/D converter
}
What I have tried.
1) atob(ADRESH) atob(ADRESL)
2) atof(ADRESH) atof(ADRESL)
3) atoi(ADRESH) atoi(ADRESL)
4) atol(ADRESH) atol(ADRESL)
5) putCUSART (atob/f/i/l(ADRESH)) putCUSART(atob/f/i/l(ADRESL)
This is my Full code
/*********************************************************************
*
* Example User Program to be used with resident Bootloader
*
*********************************************************************
* FileName: main.c
* File Version: 2.0, 15-February
* Dependencies: See INCLUDES section below
* Processor: PIC18
* Compiler: C18 2.30.01+
* Company: emxys Ltd.
*
*
*
* Copyright: 2009 emxys Ltd. ALL RIGHTS RESERVED
*
* Notes: This is a basic template for PIC18F4550 running
* uIceBlue module.
*
********************************************************************/
#define PROGRAMMABLE_WITH_USB_HID_BOOTLOADER
/** INCLUDES *******************************************************/
#include <p18cxxx.h>
#include <portb.h>
#include <usart.h>
#include <delays.h>
#include <adc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
/** CONFIGURATION **************************************************/
/** V A R I A B L E S **********************************************/
#pragma idata
int i=0; //number of characters of command
int x=1;
int y=1;
/** PRIVATE PROTOTYPES *********************************************/
void YourHighPriorityISRCode();
void YourLowPriorityISRCode();
/** VECTOR REMAPPING ***********************************************/
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
#define REMAPPED_RESET_VECTOR_ADDRESS 0x1000
#define REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS 0x1008
#define REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS 0x1018
#endif
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
extern void _startup (void); // See c018i.c in your C18 compiler dir
#pragma code REMAPPED_RESET_VECTOR = REMAPPED_RESET_VECTOR_ADDRESS
void _reset (void)
{
_asm goto _startup _endasm
}
#endif
#pragma code REMAPPED_HIGH_INTERRUPT_VECTOR = REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS
void Remapped_High_ISR (void)
{
_asm goto YourHighPriorityISRCode _endasm
}
#pragma code REMAPPED_LOW_INTERRUPT_VECTOR = REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS
void Remapped_Low_ISR (void)
{
_asm goto YourLowPriorityISRCode _endasm
}
#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)
//Note: If this project is built while one of the bootloaders has
//been defined, but then the output hex file is not programmed with
//the bootloader, addresses 0x08 and 0x18 would end up programmed with 0xFFFF.
//As a result, if an actual interrupt was enabled and occured, the PC would jump
//to 0x08 (or 0x18) and would begin executing "0xFFFF" (unprogrammed space). This
//executes as nop instructions, but the PC would eventually reach the REMAPPED_RESET_VECTOR_ADDRESS
//(0x1000 or 0x800, depending upon bootloader), and would execute the "goto _startup". This
//would effective reset the application.
//To fix this situation, we should always deliberately place a
//"goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS" at address 0x08, and a
//"goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS" at address 0x18. When the output
//hex file of this project is programmed with the bootloader, these sections do not
//get bootloaded (as they overlap the bootloader space). If the output hex file is not
//programmed using the bootloader, then the below goto instructions do get programmed,
//and the hex file still works like normal. The below section is only required to fix this
//scenario.
#pragma code HIGH_INTERRUPT_VECTOR = 0x08
void High_ISR (void)
{
_asm goto REMAPPED_HIGH_INTERRUPT_VECTOR_ADDRESS _endasm
}
#pragma code LOW_INTERRUPT_VECTOR = 0x18
void Low_ISR (void)
{
_asm goto REMAPPED_LOW_INTERRUPT_VECTOR_ADDRESS _endasm
}
#endif //end of "#if defined(PROGRAMMABLE_WITH_USB_HID_BOOTLOADER)||defined(PROGRAMMABLE_WITH_USB_LEGACY_CUSTOM_CLASS_BOOTLOADER)"
#pragma code
//These are your actual interrupt handling routines.
#pragma interrupt YourHighPriorityISRCode
void YourHighPriorityISRCode()
{
unsigned char buffer[40];
if (PIR1bits.RCIF == 1) //USART Interruption
{
buffer[i]= getcUSART(); //characters of the commands.
if (buffer[i]=='\n')
buffer[i]='\r';
if (i<4 & buffer[i]=='\r')
{
RCREG=0; // clear buffer
TXREG=0;
i=0;
PIR1bits.RCIF =0;
_asm RETFIE 0x0 _endasm
}
else if (i==4 & buffer[4]=='\r')
{
/** PORTD commands *************************************************************/
//COMMAND: 0xhi or 0xHI : set bit x from PORTD
if ((buffer [0] == '0' & buffer[2] == 'h' & buffer[3]== 'i') || (buffer[0] == '0' & buffer[2] == 'H' & buffer[3]== 'I'))
{
switch (buffer[1])
{
case '0':
PORTDbits.RD0 = 1;
break;
case '1':
PORTDbits.RD1 = 1;
break;
case '2':
PORTDbits.RD2 = 1;
break;
case '3':
PORTDbits.RD3 = 1;
break;
case '4':
PORTDbits.RD4 = 1;
break;
case '5':
PORTDbits.RD5 = 1;
break;
case '6':
PORTDbits.RD6 = 1;
break;
case '7':
PORTDbits.RD7 = 1;
break;
}
putcUSART('#');
WriteUSART(buffer[1]);
putrsUSART("HI");
}
//COMMAND: 0xlo or 0xLO : clear bit x from PORTD
if ((buffer[0] == '0' & buffer[2] == 'l' & buffer[3]== 'o') || (buffer [0] == '0' & buffer[2] == 'L' & buffer[3]== 'O'))
{
switch (buffer[1])
{
case '0':
PORTDbits.RD0 = 0;
break;
case '1':
PORTDbits.RD1 = 0;
break;
case '2':
PORTDbits.RD2 = 0;
break;
case '3':
PORTDbits.RD3 = 0;
break;
case '4':
PORTDbits.RD4 = 0;
break;
case '5':
PORTDbits.RD5 = 0;
break;
case '6':
PORTDbits.RD6 = 0;
break;
case '7':
PORTDbits.RD7 = 0;
break;
}
putcUSART('#');
WriteUSART(buffer[1]);
putrsUSART("LO");
}
//COMMAND: aclr or ACLR : clear PORTD
if ((buffer[0] == 'a' & buffer[1] == 'c' & buffer[2]== 'l' & buffer[3] == 'r') || ( buffer[0] == 'A' & buffer[1] == 'C' & buffer[2]== 'L' & buffer[3] == 'R'))
{
PORTD= 0x00;
}
//COMMAND: aset or ASET : set PORTD
if ((buffer[0] == 'a' & buffer[1] == 's' & buffer[2]== 'e' & buffer[3] == 't') || ( buffer[0] == 'A' & buffer[1] == 'S' & buffer[2]== 'E' & buffer[3] == 'T'))
{
PORTD= 0xFF;
}
//COMMAND: dqst or DQST : query state of PORTD
if ((buffer[0] == 'd' & buffer[1] == 'q' & buffer[2]== 's' & buffer[3] == 't') || ( buffer[0] == 'D' & buffer[1] == 'Q' & buffer[2]== 'S' & buffer[3] == 'T'))
{
putcUSART('#');
WriteUSART(PORTD);
}
//COMMAND: dldx or DLDx : load the value x on PORTD
if ((buffer[0] == 'd' & buffer[1]== 'l' & buffer[2] == 'd') || ( buffer[0] == 'D' & buffer[1]== 'L' & buffer[2] == 'D'))
{
PORTD= buffer[3];
}
/** PORTB commands *************************************************************/
//COMMAND: bqst or BQST : query state of PORTB
if ((buffer[0] == 'b' & buffer[1] == 'q' & buffer[2]== 's' & buffer[3] == 't') || ( buffer[0] == 'B' & buffer[1] == 'Q' & buffer[2]== 'S' & buffer[3] == 'T'))
{
putcUSART('#');
WriteUSART(PORTB);
}
//COMMAND: 0xst or 0xST : show the state of bit x from PORTB
if ((buffer[0] == '0' & buffer[2]== 's' & buffer[3] == 't') || ( buffer[0] == '0' & buffer[2]== 'S' & buffer[3] == 'T'))
{
unsigned status;
switch (buffer[1])
{
case '0':
status = PORTBbits.RB0;
break;
case '1':
status = PORTBbits.RB1;
break;
case '2':
status = PORTBbits.RB2;
break;
case '3':
status = PORTBbits.RB3;
break;
case '4':
status = PORTBbits.RB4;
break;
case '5':
status = PORTBbits.RB5;
break;
case '6':
status = PORTBbits.RB6;
break;
case '7':
status = PORTBbits.RB7;
break;
}
putcUSART('#');
WriteUSART(buffer[1]);
if (status)
putrsUSART("HI");
else
putrsUSART("LO");
}
/** AD converter commands *************************************************************/
//COMMAND: 0xad or 0xAD : x will be the input for the AD converter
if ((buffer[0] == '0' & buffer[2]== 'a' & buffer[3] == 'd') || ( buffer[0] == '0' & buffer[2]== 'A' & buffer[3] == 'D'))
{
switch (buffer[1])
{
case '0':
SetChanADC( ADC_CH0 );
break;
case '1':
SetChanADC( ADC_CH1 );
break;
case '2':
SetChanADC( ADC_CH2 );
break;
case '3':
SetChanADC( ADC_CH3 );
break;
case '4':
SetChanADC( ADC_CH4 );
break;
case '5':
SetChanADC( ADC_CH5 );
break;
case '6':
SetChanADC( ADC_CH6 );
break;
case '7':
SetChanADC( ADC_CH7 );
break;
}
PIR1bits.ADIF=0;
while(x ==1)
{
ConvertADC();
//ADCONbits.GO/DONE=1 , Starts the A/D conversion process.
while(BusyADC())
{} // Wait for completion
//putcUSART('\n');
Delay10KTCYx(0);
Delay10KTCYx(0);
Delay10KTCYx(0);
Delay10KTCYx(0);
while(BusyUSART());
putcUSART(ADRESH);
while(BusyUSART());
putcUSART(ADRESL);
PIR1bits.ADIF=0;
}
//CloseADC(); // Disable A/D converter
}
i=-1;
PIR1bits.RCIF =0;
}
else if (i>4 & buffer[i]=='\r')
{
RCREG=0; //Clear the buffer
TXREG=0;
i=0;
PIR1bits.RCIF =0;
_asm RETFIE 0x0 _endasm
}
i++;
}
} //This return will be a "retfie fast", since this is in a #pragma interrupt section
#pragma interruptlow YourLowPriorityISRCode
void YourLowPriorityISRCode()
{
//Check which interrupt flag caused the interrupt.
//Service the interrupt
//Clear the interrupt flag
//Etc.
} //This return will be a "retfie", since this is in a #pragma interruptlow section
/** DECLARATIONS ***************************************************/
#pragma code
/******************************************************************************
* Function: void main(void)
*
* PreCondition: None
*
* Input: None
*
* Output: None
*
* Side Effects: None
*
* Overview: Main program entry point.
*
* Note: None
*****************************************************************************/
void main(void)
{
//Configure Ports : 1 is input, 0 is output
TRISA=1;
TRISB=1;
PORTB=0;
TRISD=0;
PORTD=0;
TRISE=1;
//Configure Interruptions
INTCONbits.GIE=1; // Global Interruption Enable bit
INTCONbits.PEIE=1; // Peripheral Interruption Enable bit
//PIE1bits.TXIE=1; // EUSART Transmit Interruption Enable bit
PIE1bits.RCIE=1; // EUSART Receive Interruption Enable bit
IPR1bits.RCIP=1; // EUSART Receive Interruption Priority bit
//Configure USART
//configure RC6/TX/CK and RC7/RX/DT/SDO as an EUSART
TRISCbits.TRISC6=1;
TRISCbits.TRISC7=1;
RCSTAbits.SPEN=1; //Serial Port Enable bit
BAUDCONbits.BRG16=0;//16-Bit Baud Rate Register Enable bit
/* EUSART BAUD RATE CONFIGURATION ***********************************
;OpenUSART(...,SPBRG) configure the USART
;
; SPBRG value => 312 # 9600 bauds
; 155 # 19200 bauds
; 77 # 38400 bauds
; 51 # 57600 bauds
; 25 # 115200 bauds
; 12 # 230400 bauds
; 6 # 460800 bauds
; 2 # 921600 bauds
;
; Check the MPLAB C18 Libraries and the 18F4550 datasheet for more
; information
********************************************************************/
OpenUSART(USART_TX_INT_OFF & //Interruption on Transmission
USART_RX_INT_ON & //Interruption on Receipt
USART_ASYNCH_MODE & //USART Mode
USART_EIGHT_BIT & //Transmission Width
USART_CONT_RX & //Reception mode
USART_BRGH_HIGH, 155); //High Baud Rate Select bit, Baud rate at which the USART operates
//Configure AD converter
ADCON0bits.ADON=1; //Enable AD converter
/* AD converter configuration *******************************************
;OpenADC(...,portconfig) configure the AD converter
;
; portconfig value => A/D Port Configuration Control bits in decimal
;
; The channel for the A/D conversion is selected by a command
;
; Default Vref+ = Vdd, default Vref- = Vss
;
; Check the MPLAB C18 Libraries and the 18F4550 datasheet for more
; information
************************************************************************/
OpenADC( ADC_FOSC_64 & //clock source
ADC_RIGHT_JUST & //result justification
ADC_20_TAD, //acquisition time selected must be TAD>0,7us.
ADC_INT_OFF & //interruptions off
ADC_VREFPLUS_VDD & //Voltage reference
ADC_VREFMINUS_VSS, 7 ); //Port configuration is any value from 0 to 15 inclusive
while(1)
{
//INSERT YOUR MAIN CODE HERE
}
}//end main
/** EOF main.c *************************************************************/
ADRESH and ADRESL are both 8-bit byte variables. They need to be converted to an ASCII string.
atob() / atof() / atoi() / atol() convert an ASCII string to a byte/float/integer/long value. You pass them a string and they return a number.
char atob(const char * s); // convert string to 8-bit signed character
double atof(const char * s); // convert string to floating point value
int atoi(const char * s); // convert string to 16-bit signed integer
long atol(const char * s); // convert string to long integer representation
You want to use the btoa() / itoa() / ltoa() (byte/integer/long to ASCII) functions. You pass them the value to convert along with a character buffer for storing the string. They return the string representation of the passed value.
char * btoa(char value, char * string); // convert byte value to string
char * itoa( int value, char * string); // convert integer value to string
char * ltoa(long value, char * string); // convert long value to string
In your case, you'll want to use btoa() (or itoa() if you combine ADRESH and ADRESL) and putsUSART().
char string[5];
while(BusyUSART());
putsUSART(btoa(ADRESH, string)); // prints high byte to bluetooth
while(BusyUSART());
putsUSART(btoa(ADRESL, string)); // prints low byte to bluetooth
or
char string[7];
unsigned int value = (unsigned int) ADRESH * 256 + ADRESL; // reassemble reading
while(BusyUSART());
putsUSART(itoa(value, string)); // prints ADC reading to bluetooth
Since you are using the string library (#include <string.h>), another option is to print a formatted output to a string using (sprintf()). The code would now be:
char string[9];
sprintf(string, "%d %d", ADRESH, ADRESL); // convert values to string
while(BusyUSART());
putsUSART(string); // prints ADC readings to bluetooth
or
char string[7];
unsigned int value = (unsigned int) ADRESH * 256 + ADRESL; // reassemble reading
sprintf(string, "%d", value); // convert value to string
while(BusyUSART());
putsUSART(string); // prints ADC reading to bluetooth

Resources