Sending a text string out a serial port in C without using printf - printf

I'm writing 8-bit PIC microcontroller C code in Microchip's MPLAB X tool using their XC8 compiler for a demo / evaluation board for a sensor IC. The PIC I'm using has multiple serial ports, so I'm using UART2 to output only sensor data. I wanted to be able to format the output data for hex or decimal so I have stdio redirected to this port. The Rx side of UART2 is unused.
UART1 is used for command and control. This port needs to be able to output ASCII messages such as "Enter the register address:". The sensor is highly configurable, so my code initializes it with default values, but the user can halt sampling, read and write registers and re-start sampling.
Since I can't use printf when writing to UART1, I had to find another way to do this. Here's an example of what I'm doing. The file is large so I'm just cutting and pasting the relevent stuff here:
char msgString[64];
switch(command)
{
case (READCMD): // Read command
{
if (SERIAL_STATE_IDLE == serialPortState)
{
strcpy (msgString, "Input Read Address Hex Value 00-18");
Display_PrintMessage();
Display_PrintChar(prompt);
serialPortState = SERIAL_STATE_READ;
}
...
void Display_PrintMessage(void)
{
msgLength=strlen(msgString);
for (i=0; i<msgLength; ++i)
{
UART1_Write(msgString[i]);
}
Display_PrintChar(newLine);
}
void Display_PrintChar(char c)
{
UART1_Write(c);
}
I've verified that the port is working by writing some characters out to it early in main and they do appear on my terminal. I think the problem is with how I'm using the strcpy function. In the MPLAB debugger it appears that msgString is still all null characters after the strcpy executes. The strlen function may see msgString as zero length, hence, nothing prints out.
I would love to hear what you think may be wrong with my code, or, please suggest another way of accomplishing what I need.
Thanks in advance for any and all responses, they are much appreciated.
CobraRGuy

Related

(STM32) Erasing flash and writing to flash gives HAL_FLASH_ERROR_PGP error (using HAL)

Trying to write to flash to store some configuration. I am using an STM32F446ze where I want to use the last 16kb sector as storage.
I specified VOLTAGE_RANGE_3 when I erased my sector. VOLTAGE_RANGE_3 is mapped to:
#define FLASH_VOLTAGE_RANGE_3 0x00000002U /*!< Device operating range: 2.7V to 3.6V */
I am getting an error when writing to flash when I use FLASH_TYPEPROGRAM_WORD. The error is HAL_FLASH_ERROR_PGP. Reading the reference manual I read that this has to do with using wrong parallelism/voltage levels.
From the reference manual I can read
Furthermore, in the reference manual I can read:
Programming errors
It is not allowed to program data to the Flash
memory that would cross the 128-bit row boundary. In such a case, the
write operation is not performed and a program alignment error flag
(PGAERR) is set in the FLASH_SR register. The write access type (byte,
half-word, word or double word) must correspond to the type of
parallelism chosen (x8, x16, x32 or x64). If not, the write operation
is not performed and a program parallelism error flag (PGPERR) is set
in the FLASH_SR register
So I thought:
I erased the sector in voltage range 3
That gives me 2.7 to 3.6v specification
That gives me x32 parallelism size
I should be able to write WORDs to flash.
But, this line give me an error (after unlocking the flash)
uint32_t sizeOfStorageType = ....; // Some uint I want to write to flash as test
HAL_StatusTypeDef flashStatus = HAL_FLASH_Program(TYPEPROGRAM_WORD, address++, (uint64_t) sizeOfStorageType);
auto err= HAL_FLASH_GetError(); // err == 4 == HAL_FLASH_ERROR_PGP: FLASH Programming Parallelism error flag
while (flashStatus != HAL_OK)
{
}
But when I start to write bytes instead, it goes fine.
uint8_t *arr = (uint8_t*) &sizeOfStorageType;
HAL_StatusTypeDef flashStatus;
for (uint8_t i=0; i<4; i++)
{
flashStatus = HAL_FLASH_Program(TYPEPROGRAM_BYTE, address++, (uint64_t) *(arr+i));
while (flashStatus != HAL_OK)
{
}
}
My questions:
Am I understanding it correctly that after erasing a sector, I can only write one TYPEPROGRAM? Thus, after erasing I can only write bytes, OR, half-words, OR, words, OR double words?
What am I missing / doing wrong in above context. Why can I only write bytes, while I erased with VOLTAGE_RANGE_3?
This looks like an data alignment error, but not the one related with 128-bit flash memory rows which is mentioned in the reference manual. That one is probably related with double word writes only, and is irrelevant in your case.
If you want to program 4 bytes at a time, your address needs to be word aligned, meaning that it needs to be divisible by 4. Also, address is not a uint32_t* (pointer), it's a raw uint32_t so address++ increments it by 1, not 4. As far as I know, Cortex M4 core converts unaligned accesses on the bus into multiple smaller size aligned accesses automatically, but this violates the flash parallelism rule.
BTW, it's perfectly valid to perform a mixture of byte, half-word and word writes as long as they are properly aligned. Also, unlike the flash hardware of F0, F1 and F3 series, you can try to overwrite a previously written location without causing an error. 0->1 bit changes are just ignored.

I want to parse GNSS data from NMEA sentence using STM32F0 and Truestudio

I am facing difficulty while parsing GPS data from NMEA sentence. I am using Quectel L89 GNSS module (baud rate: 115200) and Truestudio IDE. I had used CubeMX for making skeletal structure of program.
I had tried using both HAL_UART_RxCpltCallback with HAL_UART_Receive_IT and HAL_UART_Receive_IT independently. I am able to read data sometimes and sometimes not. The thing is that raw data starts from $GPRMC AND ends with $PSTMCPU while reading data from ST sometimes rec_buffer starts from $GPRMC(ok condition) and sometimes from $GNVTG(wrong condition).
How will I ensure that rec_buffer always starts from $GPRMC
I am calling this in while(1):
HAL_UART_Receive_IT(&huart2, rec_buff, 2400); // from GPS UART
I also tried this:
void HAL_UART_RxCpltCallback( UART_HandleTypeDef * huart )
{
if (huart->Instance == USART2) //GPS UART
{
HAL_UART_Transmit(&huart1, &rec_buff[0], 1, 10);
HAL_UART_Receive_IT(&huart2, rrc_buff, 2400);
}
}
L89 RAW DATA:
$GPRMC,094640.000,A,2838.86700,N,07711.56483,E,0.4,99.3,050119,,,A*5E
$GPGGA,094640.000,2838.86700,N,07711.56483,E,1,08,1.0,226.91,M,-35.9,M,,*42
$GNGNS,094640.000,2838.86700,N,07711.56483,E,ANNNNN,08,1.0,0226.9,-35.9,,*78
$GPVTG,99.3,T,,M,0.4,N,0.7,K,A*3D
$GPGST,094640.000,46.0,26.0,17.7,0.2,25.7,18.2,19.6*68
$GPGBS,094640.000,25.7,18.2,19.6,,,,*4B
$GNGSA,A,3,23,09,03,16,26,22,27,07,,,,,1.9,1.0,1.6*28
$GNGSA,A,3,,,,,,,,,,,,,1.9,1.0,1.6*22
$GPGSV,3,1,11,16,70,076,27,03,58,227,30,23,52,331,36,26,44,046,18*78
$GPGSV,3,2,11,22,44,198,21,09,24,316,34,27,19,137,19,07,16,266,27*70
$GPGSV,3,3,11,31,14,058,,14,10,112,,08,09,164,20,,,,*45
$PSTMPRES,14.7,-4.3,3.4,4.7,8.1,-34.9,-10.0,14.5,-8.6,,,,,,,,,,,,,,,,*05
$PSTMVRES,0.3,-0.0,0.0,-0.0,0.3,0.0,-0.5,-0.5,0.1,,,,,,,,,,,,,,,,*0B
$PSTMTG,2034,553618.0000,8,128199118,10,-46760.0000,002a,1025,0,0,10,2034,553618.0000,10,2034,553618.0000,6*5A
$PSTMTS,1,23,87178102.875,-44634.02,01,36,107871,1,9650745.91,13059080.41,21268551.81,-1601.01,2104.54,-615.14,-60384.66,6.69,0,0.00,0.00,0,0,0,0*01
$PSTMTS,1,09,88801609.812,-43889.80,01,34,102805,1,16028885.38,3193585.53,20890300.53,-1492.86,2210.83,804.75,142511.90,11.66,0,0.00,0.00,0,0,0,0*07
$PSTMTS,1,03,86575601.125,-48258.84,01,30,78613,1,13348561.56,22507400.16,4720188.47,-3.99,659.00,-3098.78,52480.77,6.38,0,0.00,0.00,0,0,0,0*2D
$PSTMTS,1,16,85874269.688,-47283.16,01,27,75521,1,-1886095.16,22466622.38,13519219.09,-747.65,-1602.10,2568.93,-3251.94,5.72,0,0.00,0.00,0,0,0,0*2A
$PSTMTS,1,26,3381954.688,-49146.08,00,18,53036,1,-8037289.25,15873771.62,19660297.47,-1174.67,-2217.50,1326.00,26300.98,7.53,0,0.00,0.00,0,0,0,0*3C
$PSTMTS,1,22,3609068.188,-49215.92,00,21,27316,1,10235851.56,24348322.31,-2780494.62,-516.89,-162.93,-3068.35,-184537.24,7.77,0,0.00,0.00,0,0,0,0*08
$PSTMTS,1,27,5418358.750,-44043.68,00,19,77495,1,-10438473.00,23018353.00,-7765070.44,-768.74,651.32,3023.75,-13120.58,14.66,0,0.00,0.00,0,0,0,0*23
$PSTMTS,1,07,90050798.438,-44830.90,01,27,76584,1,25565687.72,6701073.97,4735275.25,-590.77,171.17,3061.98,17221.07,16.12,0,0.00,0.00,0,0,0,0*04
$PSTMTS,1,08,454836.875,-43869.36,00,20,3383,0,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0,0.00,0.00,0,0,0,0*2B
$PSTMNOTCHSTATUS,3786017,0,2449,0,2,5420680,0,3824,0,2*57
$PSTMADCDATA,943,903,,,,,,*48
$PSTMANTENNASTATUS,0*4D
$PSTMSBAS,0,0,,,,*19
$PSTMCPU,48.64,-1,49*41
Any help/ suggestion will be appreciated.
Thanks clifford for your suggestions. I need to parse data from RMC, VTG, GGA AND GSA. I am unable to change the baud rate and also being unable to stop proprietary sentences.
There is nothing "wrong" about the output from your GNSS; it is normal for such a device to output a number of standard and proprietary sentences. It is your responsibility to either configure the module to output only the sentences you need or parse the sentences and discard those you don't want.
Send the following command to the GNSS to disable all configurable sentences but the RMC sentence:
$PSMTSETPAR,1201,0X00000040*75
You need to either send this configuration every time on start-up, or save the configuration in the non-volatile memory by:
$PSMTSAVEPAR*58
It is possible that the module will output other sentences that are not configurable (I am not familiare with the specific module); you may still need to perform parsing and filtering on the host. That is as simple as parsing the talker ID and format specifier and discarding sentences you are not interested in. There are nay number of ways to do that; your question shows no such attempt.
Your code is too "low-level" as it is - the IEC61162-1 (NMEA 0183) sentence format is line oriented, but HAL_UART_Receive_IT() will just grab a chunk of data without any knowledge of the protocol and entirely asynchronous to the sentence structure - you could as easily start and end mid-sentence. Moreover HAL_UART_RxCpltCallback() occurs in the interrupt context - that is not the place to be receiving large chunks of data and parsing it. You first need some higher-level serial I/O code that will buffer the incoming data into a queue and support line-oriented input.
You then need to process each line - discarding those that are of no interest. In that sense it is too broad a question and not specifically related to GNSS data parsing - that is not really your problem here, it is as I say lower-level than than - you need ot build a suitable serial I/O infrastructure.
Another issue is that of you wait for 2400 characters to be received, you will get the data in chunks, and only the most recently received will be valid. If real-time reception of current position is required; you cannot do it that way.
I am no ST HAL expert and would not choose to use it; it is poorly documented. What it seems you should do in HAL_UART_RxCpltCallback is place the received data into a FIFO buffer that will be processed by some other thread (i.e. not in the interrupt context); because of variable length of NMEA sentences, you will need to do that on a single character basis. A perhaps simpler method is to poll the HALL receive buffer with a zero timeout and accumulate characters until a line is available. You can then inspect that line to see if it is an RMC sentence and then process that. For example, the following allows non-blocking processing of serial input. If no other functions need be performed, the timeout can be extended to wait indefinitely for each character:
// Strictly NMEA0183 requires only 81 byte sentence buffer, but
// L89 appears to output non-compliant proprietary sentences.
static char line_buffer[1024];
int line_buffer_index = 0;
for (;;)
{
HAL_StatusTypeDef rx_status = HAL_OK ;
while( rx_status == HAL_OK )
{
// Receive a character if available (zero timeout)
char ch = 0;
HAL_StatusTypeDef rx_status = HAL_UART_Receive( &huart2, ch, 1, 0 ) ;
// If character received and not the LF following the previous CR
if( rx_status == HAL_OK && ch != `\n ` )
{
// If the end of the sentence...
if( ch == `\r ` )
{
// terminate the line
line_buffer[line_buffer_index] = `\0` ;
// Start a new line
line_buffer_index = 0 ;
// Check if line is an RMC from a GNSS
if( line_buffer_index > 6 &&
memcmp( line_buffer, "$G", 2) == 0 &&
memcmp( &line_buffer[3], "RMC", 3) == 0 )
{
// Process RMC
processRMC( line_buffer ) ;
}
}
else
{
// add character to line buffer
line_buffer[line_buffer_index] = ch ;
line_buffer_index++ ;
if( line_buffer_index > sizeof(line_buffer) - 1 )
{
// Line too long, discard all
line_buffer_index = 0 ;
}
}
}
}
// do other work here if necessary
}

How to write ISR macro for 2 pins on the same port in PIC32MZ2048ECH144 using Microchip Harmony Configurator(MHC)?

I am using PIC32MZ2048ECH144. I have two switches connected to RH8(pin Number 81) and RH9(pin Number 82). I do not see any option in MHC to set interrupt at pin level, therefore I get the ISR generated for the port-H. I need the ISRs for each pin to be invoked separately.
Hence, in "system_init.c", in "SYS_Initialize" function I added the following lines,
PLIB_PORTS_PinChangeNoticePerPortEnable(PORTS_ID_0, PORT_CHANNEL_H, PORTS_BIT_POS_8);
PLIB_PORTS_PinChangeNoticePerPortEnable(PORTS_ID_0, PORT_CHANNEL_H, PORTS_BIT_POS_9);
The ISR generated by MHC in "system_interrupt.c":
void __ISR(_CHANGE_NOTICE_H_VECTOR, ipl3AUTO) _IntHandlerChangeNotification_PortH1(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_CHANGE_NOTICE_H);
APP_SwitchChangeNoticed();
}
I replaced the above ISR macro with the below lines:
void __ISR(_ADC1_DATA22_VECTOR, ipl3AUTO) _IntHandlerChangeNotification_PortH1(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0, INT_SOURCE_CHANGE_NOTICE);
APP_SwitchChangeNoticed();
}
void __ISR(_ADC1_DATA23_VECTOR, ipl3AUTO) _IntHandlerChangeNotification_PortH(void)
{
PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_CHANGE_NOTICE_H);
test1();
}
This did not work out. I referred the link http://microchip.wikidot.com/faq:78. I feel I am wrong in choosing the vector numbers for the ISR macros from "/pic32mx/include/proc/p32mz2048ech144.h". (I used _ADC1_DATA22_VECTOR and _ADC1_DATA23_VECTOR thinking that the values against them 81 and 82 are pin numbers, which again did not work.) Any help or hints on how to set pin level interrupts(2 pins on the same port) would be really great! Kindly apologize for any mistakes in my post.
Thanks in advance.
The short answer is that what you are asking for cant be achieved directly with two separate ISRs. There is only one change notification ISR vector available for the whole H port. You would normally achieve what you are looking for with an added software check to determine which of your two pins is in a different state. Another method is to simple move your signal to another port (if your board is not finalized).
The name you give the function has no bearing on what the ISR will react to. The real magic comes in the __ISR macro arguments.
For example:
void __ISR(_CHANGE_NOTICE_H_VECTOR, ipl3AUTO) _IntHandler1234()
Notice the _CHANGE_NOTICE_H_VECTOR; it signifies that this interrupt service routine will be called when the change notification interrupt happens on port H.

Objective-C - Arduino communication stops

I am trying to control some stepper motors using an app I am writing in Objective-C and my Arduino board. I use popen to send a byte (the letter a) to my Arduino and count the steps taken (Xc):
-(IBAction)PlusX:(id)sender{
popen("echo a > /dev/tty.usbmodem621", "r");
Xc = Xc +1;
_lXc.stringValue = #(Xc);
[NSThread sleepForTimeInterval:0.16f];
}
My Arduino reads this in the void loop and makes a step.
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
Serial.println(incomingByte);
if(incomingByte == 'a'){
MotorX->step(1, FORWARD, SINGLE);
}
}
This al works pretty much as expected. Except after byte/step/action 144 the Objective-C app keeps counting the steps correctly, however they don't appear in Arduino's serial monitor and the motor stops making steps. Is there anybody who knows why this keeps happening?
Thanks
you are opening many file (in linux everything is a file) but never closing it. Then you are creating "garbage" open file, because you can't use that file anymore, but it is there. And any OS give you a limit of maximum file open (file descriptor limit); in your case about 144.
when you "kill" your application, that descriptor get released, so you can use them again.
Also, because you don't check the descriptor is valid, you didn't debugged that after the 144 open it was giving error.
Solution (that does NOT check for error) is
pclose(popen("echo a > /dev/tty.usbmodem621", 'r'));

Formatting characters on serial output!

I'm interfacing an SD card to ATmega128 using EFSL.
Card gets initialised, but I get an error saying "Unknown error 0xff(see sandisk docs p5-13)"... on serial port output.
That's not all, I hv used 'sprintf' to form strings which I display on serial port. The puzzling thing is, I'm also getting the actual formatting characters like "%02d" on the output ! I think my application itself is crashing. I can give my source code, but it's too big. If anyone wants to look at it , i'll copy paste it here.
I've also checked the sandisk docs p5-13 but I can't figure it out...
I'm gonna need help on this one guys...
Please let me know if anyone knows anything about this
Thanxs...
I happen to have written some code for an ATmega8 and written my own printf function that outputs to the serial port here.
the SerWrite function is your function that transfers a 0-terminated unsigned char array to the serial port.
#define SIZE_STATUS 80
void ser_printf(const char *fmt, ...) {
va_list args;
uchar STATUS[SIZE_STATUS];
va_start(args, fmt);
uchar s = (uchar) vsnprintf(STATUS, SIZE_STATUS, fmt, args);
va_end(args);
SerWrite(STATUS, s);
}
I don't know how, but problem was solved when I used a different card. I tried two 1/2 GB cards with FAT32 formatting; both didn't work, But the card that worked was 16 MB canon card with FAT12 formatting.I'm trying to figure this out. I'll post the answer if I find it,
Thanks anyway guys...