I'm learning embedded c for microcontrollers using a PICKIT. When I send the voltage readings from an LDR via serial communication to RealTerm I end up with the voltages just continuing on the line (see pic) and not starting a new line every reading, how would I change it to look like this? Thanks in advance.
0.9V
0.89V
0.9V
RealTerm data
#pragma config FEXTOSC = HS // External Oscillator mode Selection bits (HS (crystal oscillator)above 8 MHz; PFM set to high power)
#pragma config RSTOSC = EXTOSC_4PLL// Power-up default value for COSC bits (EXTOSC with 4x PLL, with EXTOSC operating per FEXTOSC bits)
// CONFIG3L
#pragma config WDTE = OFF // WDT operating mode (WDT enabled regardless of sleep)
#include <xc.h>
#include <stdio.h>
#include "LCD.h"`
#include "serial.h"
#include ADC.h
define _XTAL_FREQ 64000000 //note intrinsic _delay function is 62.5ns at 64,000,000Hz
void main(void) {
LCD_Init();
initUSART4(); //Initialise EUSART4
ADC_init();
char LDR_arr[16];
unsigned int x;
unsigned int int_part; //initialise int part of voltage
unsigned int frac_part; // initialise fraction part of voltage
//char buf = getCharSerial4();
//Clear Screen
LCD_sendbyte(0b00000001, 0);
while (1)
{
x = ADC_getval();
//ADC2String(LDR_arr, x);
/* max voltage = 3.3V, max LDR value = 255
* 255/3.3 = 77.3 so use 77 for division
* int_part = LDR value/77
* frac_part = (LDRvalue * 100)/77 - int_part*100, giving first 2 DP as integer
*/
int_part = x/77;
frac_part = (x*100)/77 - int_part * 100;
// and format as a string using sprintf (see GitHub readme)
// %02d ensures that 2 numbers are always displayed frac_part
// e.g. frac_part = 5, get 0.05 after decimal instead of 0.5
// which would get if used %01d or 0.005 if used %03d
sprintf(LDR_arr,"%d.%02dV",int_part,frac_part);
sendStringSerial4(LDR_arr); // send voltage to RealTerm
__delay_ms(1000);
LCD_sendbyte(0b00000001, 0); //clear screen
__delay_ms(1.53);
}
}
void sendCharSerial4(char charToSend) {
while (!PIR4bits.TX4IF); // wait for flag to be set
TX4REG = charToSend; //transfer char to transmitter
void sendStringSerial4(char *string){
while(*string!=0){
sendCharSerial4(*string++);
}
}
Edit if I do
sprintf(LDR_arr,"%d.%02dV \n",int_part,frac_part);
or
sprintf(LDR_arr,"%d.%02dV '\n'",int_part,frac_part);
I end up with what the voltages going along a diagonal
RealTerm with '\n' inside
If you look at your ascii table 0x0D is a carriage return CR, 0x0A is a newline. The unix world where C came from or where they developed together tends to be limited to just the new line, before during and after serial terminals default to the separate characters of carriage return (start on a new column) and new line (move down to a new line) the combination is the preferred way to move down and left. \r is carriage return 0x0D and \n is newline 0x0A.
While you can change some dumb terminals to turn a newline into both it is more useful and portable to just do it right in the program. So as pointed out in a comment add \r\n to the end of the string to be printed (or the beginning, your choice).
You need to do one of perhaps three things:
Configure your terminal for "LF-only" line ends, so it implicitly inserted a CR before each LF - all terminal emulation software supports such a configuration,
explicitly output CR+LF line end: "\r\n" in your sprintf() calls.
Modify your low level serial I/O layer to insert a \r before every \n ("text mode").
Of those I'd recommend the first one, since for either of the other two you'd still have to configure the terminal software for CR+LF in any case. It is a coin toss what any particular terminal might be configured for by default - though largely determined by whether it was originally developed for Windows or POSIX perhaps.
In TeraTerm for example:
Related
I have been playing around with SparkFun 16x2 SerLCD LCD from SparkFun and controlling it via the Tiva C EK-TM4C123GXL board. I have managed to connect the LCD via SPI communication and written code to display strings on the board. However I was having trouble writing code that would clear the screen for me until I came across this code online:
#include <stdint.h>
#include <stdlib.h>
#include "inc/tm4c123gh6pm.h"
void spi_master_ini(void){ //Setup SPI
SYSCTL_RCGCSSI_R|=(1<<2);
//SYSCTL_RCGCGPIO_R |=(1<<1);
SYSCTL_RCGC2_R |=(1<<1);
GPIO_PORTB_AFSEL_R|=(1<<4)|(1<<5)|(1<<6)|(1<<7);
GPIO_PORTB_PCTL_R=0x22220000;
GPIO_PORTB_DEN_R|=(1<<4)|(1<<5)|(1<<6)|(1<<7);
GPIO_PORTB_PUR_R|=(1<<4)|(1<<5)|(1<<6)|(1<<7);
SSI2_CR1_R=0;
SSI2_CC_R=0;
SSI2_CR1_R=64;
SSI2_CR0_R=0x7;
SSI2_CR1_R|=(1<<1);
}
void send_byte(char data){
SSI2_DR_R=data;
while((SSI2_SR_R&(1<<0))==0);
}
void send_str(char *buffer){
while(*buffer!=0){
send_byte(*buffer);
buffer++;
}
}
int main(){
spi_master_ini();
SSI2_DR_R=0x7C; //Put into setting mode.
SSI2_DR_R=0x2D; //Clear screen, move cursor to home position.
send_str("Testing");
}
Specifically the 2 lines of code that are puzzling me:
SSI2_DR_R=0x7C; //Put into setting mode.
SSI2_DR_R=0x2D; //Clear screen, move cursor to home position.
After reading through the HD44780U datasheet I wasn't able to see how sending those HEX values to the data lines would do anything other than print "|" and "-" to the LCD. However, to my surprise when I ran the code it works and clears my LCD screen.
The data sheet for the HD44780U is irrelevant - you are not talking directly to the display controller. On the SparkFun 16x2 SerLCD, the SPI communication is with the ATmega328P which in turn communicates with the display controller.
This simplifies the interface to the display, since you only need an SPI or I2C link and do not need a 4/8 bit data bus and additional control lines required by the display controller.
The software running on the ATMega328P interprets and translates commands independently of the display controller. The source code at https://github.com/sparkfun/OpenLCD applies.
settings.h has:
#define SPECIAL_SETTING '|' //124, 0x7C, the pipe character: The command to do special settings: baud, lines, width, backlight, splash, etc
Then in OpenLCD.ino void updateDisplay() there is:
//Check to see if the incoming byte is special
if (incoming == SPECIAL_SETTING) //SPECIAL_SETTING is 127
{
currentMode = MODE_SETTING;
}
...
note the comment is erroneous, it is 124 not 127 (perhaps says something about the quality of this code).
Then later:
else if (currentMode == MODE_SETTING)
{
currentMode = MODE_NORMAL; //In general, return to normal mode
...
//Clear screen and buffer
else if (incoming == 45) //- character
{
SerLCD.clear();
SerLCD.setCursor(0, 0);
clearFrameBuffer(); //Get rid of all characters in our buffer
}
...
Then clearFrameBuffer() simply fills the buffer with spaces rather then using the HD44780 "Clear Display" instruction:
//Flushes all characters from the frame buffer
void clearFrameBuffer()
{
//Clear the frame buffer
characterCount = 0;
for (byte x = 0 ; x < (settingLCDwidth * settingLCDlines) ; x++)
currentFrame[x] = ' ';
}
Much of the documentaton is for Arduino, but the command table at https://learn.sparkfun.com/tutorials/avr-based-serial-enabled-lcds-hookup-guide/firmware-overview is valid regardless. It is interms of characters rather then integer codes so has:
...
...
And many more commands you might find useful.
I am currently working on a project where we have to use an AVR ATMEGA328 micro-controller, specifically the USART peripheral, to control 8 LED's. We have to send commands to the micro-controller that will turn on, off, and blink the LED's at different rates. I have written a program in C that I think will do the job, but I would like someone to look at it and help me fix any mistakes that I may have. Your help will be greatly appreciated!
*P.S. Each command in the commands array associates with its corresponding LED state in the LED array. The LED's are connected to PORTB of the micro-controller.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
/* Arrays that contain all input commands and LED states */
const char *commands[] = {"ON0","ON1","ON2","ON3","ON4","ON5","ON6","ON7","ON8","OFF0","OFF1","OFF2","OFF3","OFF4","OFF5","OFF6","OFF7","OFF8","BLINK0","BLINK1","BLINK2","BLINK3","BLINK4","BLINK5","BLINK6","BLINK7","BLINK8","STOP"\0}
int LEDs[28] = {0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80,0XFF,0XFE,0XFD,0XFB,0XF7,0XEF,0XDF,0XBF,0X7F,0,0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80,0XFF,0}
int i;
int j;
int Blinky(int j); // Function to execute blinking commands where j is the argument
{
PORTB = LEDs[j];
_delay_ms[250 + (j-18) * 50]; /* Calculates blinking delay times */
PORTB = 0;
_delay_ms[250 + (j-18) * 50];
}
int main(void)
{
DDRB=0XFF; // PORTB is set to output
DDRD=0X02; // Turns on the transmit pin for the USART in PORTD
/* Setup USART for 9600,N,8,1 */
USCR0B = 0X18;
USCR0C = 0X06;
UBRR0 = 51;
sei(); // Enable Global Interrupts
char input;
if(UCSR0A & 0X80) /* Reads data from the USART and assigns the contents to the character input */
input = UDR0;
j=28;
char cmd;
cmd = *commands[i];
for(i=0; i<28; i++)
{
if(input==cmd) /* If the contents of UDR0 are equal to one of the commands */
j = i;
}
while(1)
{
if(j<18)
PORTB=LEDs[j]; // Executes the "ON" and "OFF" commands
else if(j<27)
Blinky(j); // Executes the blinking command by calling the Blinky function
else if(j=27)
PORTB=0; // Executes the "STOP" command
else
PORTB=0; // Accounts for typing errors
}
return(0);
}
There is a lot wrong with this program, but code review is not the purpose of Stack Overflow. See the FAQ for how to ask an appropriate question.
That said, some of the obvious problems are:
The _delay_ms() function needs to be called with a compile-time constant. It won't work correctly if the parameter needs to be calculated at run-time.
if you don't read any char from USART, then you still go through the rest of the loop.
char cmd declares a character variable, but then you assign a pointer to it.
i is used before it is set to a meaningful value.
input== cmd will likely never be true as one side is a character and the other is a pointer.
This question will likely close soon. Good luck, and come back if you have a question better suited to Stack Overflow.
I'm trying to write a code in C (Using Keil µVision 5, device: AT89C51AC3) that lets me enter 2 Integer numbers, add them and then print them out. The problem is that I'm limited to a byte code size of max. 2048.
My actual code needs 2099 Bytes to run.
Any idea how I could do the same thing using less memory?
#include <stdio.h>
#include <REG52.H>
int main()
{
int a, b;
/*------------------------------------------------
Setup the serial port for 1200 baud at 16MHz.
------------------------------------------------*/
#ifndef MONITOR51
SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr */
TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */
TH1 = 221; /* TH1: reload value for 1200 baud # 16MHz */
TR1 = 1; /* TR1: timer 1 run */
TI = 1; /* TI: set TI to send first char of UART */
#endif
printf("Enter 2 numbers\n");
scanf("%d%d",&a,&b);
printf("%d\n",a+b);
return 0;
}
You should hiccup when you see this simple code take up 2k+ of memory. That's a lot! The reason for this is that the stdio functions are terribly inefficient.
If you need to save memory and execution speed, you need to code these yourself. Which is not so hard, since you probably just need to read integers and not everything else those function can handle (float numbers, strings etc).
Also get rid of the int type, use the fixed size types from stdint.h instead. (If this is a 8 bit MCU, you should also avoid 16 bit numbers unless they are necessary.)
In addition, you will have to code the I/O part as well. On a microcontroller this would probably mean writing your own UART driver.
You should be able to reduce the code size to a couple of hundred bytes, depending on how code (in)efficient your microcontroller is.
If you just want to print the sum of int a and int b, you should be able to get rid of the
/------------------------------------------------
Setup the serial port for 1200 baud at 16MHz.
------------------------------------------------/
#ifndef MONITOR51
SCON = 0x50; /* SCON: mode 1, 8-bit UART, enable rcvr /
TMOD |= 0x20; / TMOD: timer 1, mode 2, 8-bit reload /
TH1 = 221; / TH1: reload value for 1200 baud # 16MHz /
TR1 = 1; / TR1: timer 1 run /
TI = 1; / TI: set TI to send first char of UART */
#endif:
`
code. Just keep the printf()... and scarf()... functions.
i've used an Atmega328P µC to get a string over UART and convert it to an int.
I've tried to use atoi() function or sscanf() to convert it but they are taking to long to convert so that they are blocking the interrupt.
If I stop converting and just receiving over UART all symbols are transmitted but if I convert after receiving some characters of transmission are missed.
Is there any way to speed up conversion to stop blocking RX interrupt?
If you need to convert multicharacter numbers to integer you need to use buffer. There example of cycle buffer uses:
#define BUF_LEN 128
#define BUF_MSK (BUF_LEN-1)
uint8_t buf[BUF_LEN], b_in = 0, b_out = 0;
void usart_irq_handler(void)
{
buf[b_in&BUF_MSK] = USART_DATA_REG;
b_in++;
}
int main(void)
{
uint8_t tmp;
while (b_in!=b_out) {
tmp = buf[b_out&BUF_MSK];
b_out++;
// parse here
}
}
If you need to convert single character numbers you may not use buffer (but if USART frequency not much less than CPU frequency it's not recommended). For ASCII encoded received characters each number will have values 0x30..0x39. If received characters encoded with another charset you need refer to their tables.
uint8_t num = USART_DATA_REG - 0x30;
if (num >= 0 && num <= 9) {
// is number
}
EDIT 1 [due to new information from OP] For convert decimal number from string to integer I use this function:
uint32_t parse_num(uint8_t * ptr)
{
uint32_t res = (uint32_t)0x0;
uint8_t chr = 0x0, pchr = 0xA;
while (*ptr != (uint8_t)0x0) {
chr = (uint8_t)(*ptr - (uint8_t)0x30);
if (chr < 0xA) {
res *= (uint32_t) 0xA;
res += (uint32_t) chr;
} else {
if (pchr < 0xA) break;
}
pchr = chr;
ptr++;
}
return res;
}
It skip non-numbers chars and convert first founded number, but doesn't return final position of parse buffer. You can modify it as you need.
EDIT 2 [due to chat with OP] about good approach to processor time management:
Pictures below (from this answer) illustrate normal processor timing in program:
So, the smaller the interrupt handler time - the more likely success of the other handlers, and more time to perform main process.
By increasing of MCU clock is reduced run time code and increases idle time.
By decreasing of periferal clock is reduced frequency of interrupts by perifery and increases idle time.
In conclusion idle time must be used for power save.
Yes, the function runs in main loop and copies the chars out of buffer which is filled in the interrupt routine. After detecting the \n in string, it converts the string into values using sscanf or char by char. I've first used a very slow baud rate and then a very fast one. There are also two more interrupt routines (for timing of 100us and one for a pwm driver). I've tried to take out code of these routines to speed them up and it was successful. Now the interrupt routine gets every character of the uart transmission. Now I am implementing an algorithm to convert the strings to values without using sscanf because of performance issues. Using the algorithm of imbearr with -0x30 should work properly.
I am trying to read analogic signal for a sort of mouse with a pic18f14k50 controller. Here the simple circuit: http://dl.dropbox.com/u/14663091/schematiconew.pdf . I have to read analogic signal from AN9 circuit port. Main function reads from the port, and blinks 30 time if threshold is reached:
void main(void) {
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1) {
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
if(!HIDTxHandleBusy(lastTransmission))
{
int readed = myReadADC2(); //Here i tried both myReadADC2() or myReadADC1()
if(readed>40) { //If read threshold > 40, blink led 30 times
int i;
for(i=0; i<30; i++) {
Delay1KTCYx(0);
mLED_1_On();
Delay1KTCYx(0);
mLED_1_Off();
}
}
lastTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
}//end while
}//end main
I used two method to read from the AN9 port, myReadADC() that uses OpenADC() API method:
int myReadADC(void) {
#define ADC_REF_VDD_VDD_X 0b11110011
OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH9 & ADC_INT_OFF, ADC_REF_VDD_VDD_X & ADC_REF_VDD_VSS, 0b00000010); // channel 9
SetChanADC(ADC_CH9);
ConvertADC(); // Start conversion
while(BusyADC()); // Wait for completion
return ReadADC(); // Read result
}
and myReadADC2(), that implements manual read from the port.
int myReadADC2() {
int iRet;
OSCCON=0x70; // Select 16 MHz internal clock
ANSEL = 0b00000010; // Set PORT AN9 to analog input
ANSELH = 0; // Set other PORTS as Digital I/O
/* Init ADC */
ADCON0=0b00100101; // ADC port channel 9 (AN9), Enable ADC
ADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b10101011; // Right justify result, 12 TAD, Select the FRC for 16 MHz
iRet=100;
ADCON0bits.GO=1;
while (ADCON0bits.GO); // Wait conversion done
iRet=ADRESL; // Get the 8 bit LSB result
iRet += (ADRESH << 8); // Get the 2 bit MSB result
return iDelay;
}
Both cases doesn't works, i touch (sending analogic signal) port AN9 but when I set high threshold (~50) led don't blinks, with low threshold (~0) it blinks immidiatly when i provide power to the PIC. Maybe i'm using wrong port? I'm actually passing AN9 as reading port? Or maybe threshold is wrong? How can i found the right value? Thank you
Here the MPLAB C18 Apis http://dl.dropbox.com/u/14663091/API%20microchip%20C18.pdf .
Regarding function myReadADC2(): you need to switch ANSEL and ANSELH configs as RC7/AN9 is configured in bit 1 of ANSELH. Also call me paranoid but for the line
iRet += (ADRESH << 8);
I always like to either save it a temporary variable first or cast explicitly the value ADRESH before shifting it up:
iRet += (((UINT) ADRESH) << 8);
That way I know for sure the bits won't get lost when shifting up which has bitten me before.
Regarding function myReadADC():
OpenADC() only takes two parameters. I presume that bitfield in the third parameter field is for the analog enable (ADRESH/ADRES). I'm assuming that's handled by SetChanADC() but you may have to set ADRESH/ADRES manually. It may help to set a breakpoint in the debugger and stop after configuration is complete to make sure your registers are set appropriatley.