커맨드 조건
[0x02] [CMD] [DATANUM] [DATA] [CHECKSUM] [0x03]
1. CMD
- ‘LED’
- ‘FND’
2. DATANUM : 2바이트
[DATA] 길이
3. DATA : 가변
- ‘LED’ : DATANUM = 2
[LEDNUM][STATE]
예) ‘01’ - 1번 LED ON
‘10’ - 2번 LED OFF
- ‘FND’ : DATANUM = 4
[DISPCOUNT]
예) ‘1234’
‘4567’
4.CHECKSUM : 1바이트
- CHECKSUM = CMD + DATANUM + DATA
5. Example
- [0x02] [‘LED’] [‘02’] [‘01’] [CHECKSUM] [0x03]
- [0x02] [‘FND’] [‘04’] [‘1234’] [CHECKSUM] [0x03]
핵심코드
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3){
if(cmd_flag==0 && rx==start_signal){
memset(buf,0,sizeof(buf));
cmd_flag = 1;
}
if(cmd_flag){
if(bufindex<buflen){
buf[bufindex++] = rx;
char *p;
if(buf[9]==end_signal || buf[11]==end_signal){
if((p = strstr((char*)buf,"LED")) != NULL){
uint8_t lednum = *(p+5)-'0';
uint8_t ledstate = *(p+6)-'0';
if(chksum(p, 7)) {
led_on(lednum, ledstate);
}
}else if((p = strstr((char*)buf,"FND")) != NULL){
if(chksum(p, 9)){
for(int i=0; i<4; i++){
fndnum[i] = *(p+i+5)-'0';
}
}
}
cmd_flag = 0;
bufindex = 0;
}
}else bufindex=0;
}
HAL_UART_Receive_IT(&huart3, &rx, 1);
}
}
전역변수인 cmd_flag 는 0으로 선언 동시 초기화되고 cmd_flag 가 0인 상태에서 커맨드의 시작을 알리는 0x02가 들어오면 버퍼를 memset하고 cmd_flag 를 1로 만든다.
cmd_flag 가 1일 때는 bufindex 가 buflen 보다 적은동안 1씩 증가하며 버퍼에 계속 쌓는다. 그리고 이 부분은 나중에 교수님 코드를 보고 내가 실수했구나 깨달은건데 end_signal을 체크하는 if 구문이 버퍼에 쌓는 부분 아래에 있으면 안되고 아래 처럼 구성되어야 될 것 같다.
if(bufindex<buflen && rx != end_signal)
buf[bufindex++] = rx;
else {
// 커맨드 처리하는 부분
}
그리고 end_signal이 오는 부분도 지금은 커맨드 길이가 고정이라 [9] 또는 [11] 인덱스만 비교했는데 나중에는 가변길이에 따라 다른위치에 오는 end_signal을 확인할 수 있도록 해야겠다. 이를 위해 아스키코드를 decimal로 바꾸는 함수를 하나 만들어 놓고 재사용해야겠다고 느꼈다.
그리고 led에 대한 커맨드가 들어왔을때 led on off 하는 부분을 함수화 했는데 사실상 GPIOx나 PIN 넘버나 전부 HEX 코드로 되어있기 때문에 배열로 만들어 놓고 인덱스를 통해 사용하는게 훨씬 효율적이라는 생각이 들었다.
마지막으로 커맨드를 실행하고 나면 cmd_flag 와 bufindex 를 다시 0으로 만든다.
uint8_t chksum(const char *p, uint8_t len){
uint8_t chksum=0;
for(int i=0; i<len ; i++){
chksum += *(p+i);
}
if((chksum & 0xff)==*(p+len)) return 1;
else return 0;
}
그리고 chksum이라는 함수는 포인터와 길이를 넘겨주면 해당 length 전까지 chksum 변수에 아스키코드값을 계속 누적합하고 그게 체크섬 비트와 일치하면 return 1, 일치하지 않으면 return 0 하게끔 했다. 이 함수의 리턴값이 1인 경우만 커맨드에 따른 제어를 시행한다.
커맨드를 보내는데는 Serialportmon이라는 프로그램을 사용하였다.
https://blog.daum.net/pg365/276
아래와 같이 단축키 지정해놓고 보낼 문자열을 ASCII 코드로도 HEX 코드로도 볼 수 있다. 체크섬 비트부분은 직접 계산기로 계산해서 적어줬다.
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "rtc.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define start_signal 0x02
#define end_signal 0x03
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint8_t rx;
uint8_t bufindex=0;
uint8_t buf[15];
uint8_t buflen=15;
uint8_t fndnum[4]={'x','x','x','x'};
uint8_t fndpos=0;
uint8_t com[4]={0,};
uint8_t cmd_flag=0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void DisplayFND(uint8_t fnd, uint8_t location);
uint8_t Decto7Seg(uint8_t num);
void counter0to9999(uint8_t num[4], uint32_t tick);
void count_time(uint8_t num[4], uint32_t tick);
void led_on(uint8_t lednum, uint8_t ledstate);
uint8_t chksum(const char *p, uint8_t len);
char* chkcommand(const uint8_t *buf);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------1*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_RTC_Init();
MX_USART3_UART_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, &rx, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
DisplayFND(Decto7Seg(fndnum[0]),1);
DisplayFND(Decto7Seg(fndnum[1]),2);
DisplayFND(Decto7Seg(fndnum[2]),3);
DisplayFND(Decto7Seg(fndnum[3]),4);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
void DisplayFND(uint8_t fnd, uint8_t location){
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_14, 0); // latch clock pin off
uint16_t data = 0;
data = fnd << 8;
data |= 1 << (location-1);
// for example, to display 1 to first FND
// MSB 1111 1001 dddd 0001 LSB (d means don't care)
for(int i=0 ; i<16; i++){
if(data >> (15-i) & 1){ // MSB first in
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, 1); // Data line
}else {
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, 0); // Data line
}
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_13, 1); // shift clock pin on
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_13, 0); // shift clock pin off
}
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_14, 1); // latch clock pin on
}
uint8_t Decto7Seg(uint8_t num){
// for anode type
switch(num){
case 0:
return 0xc0;
case 1:
return 0xf9;
case 2:
return 0xa4;
case 3:
return 0xb0;
case 4:
return 0x99;
case 5:
return 0x92;
case 6:
return 0x82;
case 7:
return 0xd8;
case 8:
return 0x80;
case 9:
return 0x90;
case 'a':
return 0x00;
default:
return 0xff;
}
}
void counter0to9999(uint8_t num[4], uint32_t tick){
num[0] = (tick%10000)/1000;
num[1] = (tick%1000)/100;
num[2] = (tick%100)/10;
num[3] = tick%10;
}
void count_time(uint8_t num[4], uint32_t tick){
num[3] = (tick%60)%10;
num[2] = (tick%60)/10;
num[1] = ((tick%3600)/60)%10;
num[0] = ((tick%3600)/60)/10;
}
void led_on(uint8_t lednum, uint8_t ledstate){
switch(lednum){
case 0:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, !ledstate);
break;
case 1:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, !ledstate);
break;
case 2:
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, !ledstate);
break;
case 3:
HAL_GPIO_WritePin(GPIOD, GPIO_PIN_14, !ledstate);
break;
default :
break;
}
}
uint8_t chksum(const char *p, uint8_t len){
uint8_t chksum=0;
for(int i=0; i<len ; i++){
chksum += *(p+i);
}
if((chksum & 0xff)==*(p+len)) return 1;
else return 0;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART3){
if(cmd_flag==0 && rx==start_signal){
memset(buf,0,sizeof(buf));
cmd_flag = 1;
}
if(cmd_flag){
if(bufindex<buflen){
buf[bufindex++] = rx;
char *p;
if(buf[9]==end_signal || buf[11]==end_signal){
if((p = strstr((char*)buf,"LED")) != NULL){
uint8_t lednum = *(p+5)-'0';
uint8_t ledstate = *(p+6)-'0';
if(chksum(p, 7)) {
led_on(lednum, ledstate);
}
}else if((p = strstr((char*)buf,"FND")) != NULL){
if(chksum(p, 9)){
for(int i=0; i<4; i++){
fndnum[i] = *(p+i+5)-'0';
}
}
}
cmd_flag = 0;
bufindex = 0;
}
}else bufindex=0;
}
HAL_UART_Receive_IT(&huart3, &rx, 1);
}
}
/* USER CODE END 4 */
'임베디드 개발 > STM32 (ARM Cortex-M)' 카테고리의 다른 글
STM32 ] 블루투스 모듈 MLT-BT05 사용하기 / 메시지 주고받기 , AT 커맨드 (0) | 2022.06.19 |
---|---|
STM32 ] RTC 와 LCD 모듈을 사용한 알람시계 구현 (2) - 더블클릭 기능 추가 (0) | 2022.06.19 |
STM32 ] Timer 인터럽트를 사용하여 ADC 값 받기 + 그래프 보면서 디버깅하는 팁 (0) | 2022.06.17 |
STM32 ] RTC 와 LCD 모듈을 사용한 알람시계 구현 (1) (8) | 2022.06.11 |
STM32 ] RTC , GetTime / GetDate 함수로 시간 값 확인 시 주의사항 (1) | 2022.06.11 |